Unstable 1.8.4.0

This commit is contained in:
Markus Isberg
2025-03-12 12:56:27 +00:00
parent a4c3e868e4
commit a4a3427e4e
627 changed files with 29860 additions and 10018 deletions
@@ -1,6 +1,7 @@
using Barotrauma.Networking;
using System.Linq;
using System.Xml.Linq;
using Barotrauma.Items.Components;
namespace Barotrauma
{
@@ -40,7 +41,7 @@ namespace Barotrauma
{
matchingData.ApplyPermadeath();
if (GameMain.Server is { ServerSettings.IronmanMode: true })
if (GameMain.Server?.ServerSettings is { IronmanModeActive: true })
{
mpCampaign.SaveSingleCharacter(matchingData);
}
@@ -85,5 +86,16 @@ namespace Barotrauma
{
GameServer.Log($"{GameServer.CharacterLogName(this)} has gained the talent '{talentPrefab.DisplayName}'", ServerLog.MessageType.Talent);
}
private void SyncInGameEditables(Item item)
{
foreach (ItemComponent itemComponent in item.Components)
{
foreach (var serializableProperty in SerializableProperty.GetProperties<InGameEditable>(itemComponent))
{
GameMain.Server.CreateEntityEvent(item, new Item.ChangePropertyEventData(serializableProperty, itemComponent));
}
}
}
}
}
@@ -21,16 +21,16 @@ namespace Barotrauma
CauseOfDeath = null;
}
partial void OnSkillChanged(Identifier skillIdentifier, float prevLevel, float newLevel)
partial void OnSkillChanged(Identifier skillIdentifier, float prevLevel, float newLevel, bool forceNotification)
{
if (Character == null || Character.Removed) { return; }
if (!prevSentSkill.ContainsKey(skillIdentifier))
{
prevSentSkill[skillIdentifier] = prevLevel;
}
if (Math.Abs(prevSentSkill[skillIdentifier] - newLevel) > 0.01f)
if (Math.Abs(prevSentSkill[skillIdentifier] - newLevel) > 0.1f || forceNotification)
{
GameMain.NetworkMember.CreateEntityEvent(Character, new Character.UpdateSkillsEventData());
GameMain.NetworkMember.CreateEntityEvent(Character, new Character.UpdateSkillsEventData(skillIdentifier, forceNotification));
prevSentSkill[skillIdentifier] = newLevel;
}
}
@@ -57,6 +57,8 @@ namespace Barotrauma
msg.WriteString(Name);
msg.WriteString(OriginalName);
msg.WriteBoolean(RenamingEnabled);
msg.WriteByte((byte)BotStatus);
msg.WriteInt32(Salary);
msg.WriteByte((byte)Head.Preset.TagSet.Count);
foreach (Identifier tag in Head.Preset.TagSet)
{
@@ -98,6 +100,8 @@ namespace Barotrauma
msg.WriteInt32(ExperiencePoints);
msg.WriteRangedInteger(AdditionalTalentPoints, 0, MaxAdditionalTalentPoints);
msg.WriteBoolean(PermanentlyDead);
msg.WriteInt32(TalentRefundPoints);
msg.WriteInt32(TalentResetCount);
}
}
}
@@ -85,7 +85,8 @@ namespace Barotrauma
partial void UpdateNetInput()
{
if (!(this is AICharacter) || IsRemotePlayer)
//non-ai character (a character that was previously controlled by a player) or a remote player (which can be an AI character controlled by a player)
if (this is not AICharacter || IsRemotePlayer)
{
if (!CanMove)
{
@@ -298,17 +299,12 @@ namespace Barotrauma
Kill(causeOfDeath.type, causeOfDeath.affliction);
}
break;
case EventType.ConfirmTalentRefund:
if (!CanManageTalents(c)) { return; }
Info?.RefundTalents();
break;
case EventType.UpdateTalents:
if (c.Character != this)
{
if (!IsBot || !c.HasPermission(ClientPermissions.ManageBotTalents))
{
#if DEBUG
DebugConsole.Log("Received a character update message from a client who's not controlling the character");
#endif
return;
}
}
if (!CanManageTalents(c)) { return; }
// get the full list of talents from the player, only give the ones
// that are not already given (or otherwise not viable)
@@ -332,6 +328,22 @@ namespace Barotrauma
}
break;
}
bool CanManageTalents(Client client)
{
if (client.Character != this)
{
if (client.TeamID != TeamID || !IsBot || !client.HasPermission(ClientPermissions.ManageBotTalents) || client.Spectating)
{
#if DEBUG
DebugConsole.Log("A client tried to manage talents of a character they don't control or have permission to manage");
#endif
return false;
}
}
return true;
}
}
public void ServerWritePosition(ReadWriteMessage tempBuffer, Client c)
@@ -377,8 +389,15 @@ namespace Barotrauma
tempBuffer.WriteBoolean(shoot);
tempBuffer.WriteBoolean(use);
tempBuffer.WriteBoolean(AnimController is HumanoidAnimController { Crouching: true });
if (AnimController is HumanoidAnimController humanAnim)
{
tempBuffer.WriteBoolean(humanAnim.Crouching);
}
else if (AnimController is FishAnimController fishAnim)
{
tempBuffer.WriteBoolean(fishAnim.Reverse);
}
tempBuffer.WriteBoolean(attack);
Vector2 relativeCursorPos = cursorPosition - AimRefPosition;
@@ -409,21 +428,28 @@ namespace Barotrauma
tempBuffer.WriteSingle(SimPosition.Y);
float MaxVel = NetConfig.MaxPhysicsBodyVelocity;
AnimController.Collider.LinearVelocity = new Vector2(
MathHelper.Clamp(AnimController.Collider.LinearVelocity.X, -MaxVel, MaxVel),
MathHelper.Clamp(AnimController.Collider.LinearVelocity.Y, -MaxVel, MaxVel));
NetConfig.Quantize(AnimController.Collider.LinearVelocity.X, -MaxVel, MaxVel, 12),
NetConfig.Quantize(AnimController.Collider.LinearVelocity.Y, -MaxVel, MaxVel, 12));
tempBuffer.WriteRangedSingle(AnimController.Collider.LinearVelocity.X, -MaxVel, MaxVel, 12);
tempBuffer.WriteRangedSingle(AnimController.Collider.LinearVelocity.Y, -MaxVel, MaxVel, 12);
bool fixedRotation = AnimController.Collider.FarseerBody.FixedRotation || !AnimController.Collider.PhysEnabled;
AnimController.TargetMovement = new Vector2(
NetConfig.Quantize(AnimController.TargetMovement.X, -Ragdoll.MAX_SPEED, Ragdoll.MAX_SPEED, 12),
NetConfig.Quantize(AnimController.TargetMovement.Y, -Ragdoll.MAX_SPEED, Ragdoll.MAX_SPEED, 12));
tempBuffer.WriteRangedSingle(AnimController.TargetMovement.X, -Ragdoll.MAX_SPEED, Ragdoll.MAX_SPEED, 12);
tempBuffer.WriteRangedSingle(AnimController.TargetMovement.Y, -Ragdoll.MAX_SPEED, Ragdoll.MAX_SPEED, 12);
bool fixedRotation = AnimController.Collider.FarseerBody.FixedRotation;
tempBuffer.WriteBoolean(fixedRotation);
if (!fixedRotation)
{
tempBuffer.WriteSingle(AnimController.Collider.Rotation);
float MaxAngularVel = NetConfig.MaxPhysicsBodyAngularVelocity;
AnimController.Collider.AngularVelocity = NetConfig.Quantize(AnimController.Collider.AngularVelocity, -MaxAngularVel, MaxAngularVel, 8);
tempBuffer.WriteRangedSingle(MathHelper.Clamp(AnimController.Collider.AngularVelocity, -MaxAngularVel, MaxAngularVel), -MaxAngularVel, MaxAngularVel, 8);
tempBuffer.WriteSingle(AnimController.Collider.AngularVelocity);
}
tempBuffer.WriteBoolean(AnimController.IgnorePlatforms);
bool writeStatus = healthUpdateTimer <= 0.0f;
tempBuffer.WriteBoolean(writeStatus);
if (writeStatus)
@@ -463,21 +489,19 @@ namespace Barotrauma
break;
case CharacterStatusEventData statusEventData:
WriteStatus(msg, statusEventData.ForceAfflictionData);
msg.WriteBoolean(GodMode);
break;
case UpdateSkillsEventData _:
if (Info?.Job == null)
case UpdateSkillsEventData updateSkillsData:
if (Info?.Job is { } job)
{
msg.WriteByte((byte)0);
msg.WriteIdentifier(updateSkillsData.SkillIdentifier);
msg.WriteBoolean(updateSkillsData.ForceNotification);
//don't use Character.GetSkillLevel here, because it applies all the temporary boosts from items and afflictions on the skill level
msg.WriteSingle(job.GetSkillLevel(updateSkillsData.SkillIdentifier));
}
else
{
var skills = Info.Job.GetSkills();
msg.WriteByte((byte)skills.Count());
foreach (Skill skill in skills)
{
msg.WriteIdentifier(skill.Identifier);
msg.WriteSingle(skill.Level);
}
msg.WriteIdentifier(Identifier.Empty);
}
break;
case IAttackEventData attackEventData:
@@ -501,7 +525,14 @@ namespace Barotrauma
}
break;
case AssignCampaignInteractionEventData _:
msg.WriteByte((byte)CampaignInteractionType);
bool canClientInteract = true;
if (CampaignInteractionType == CampaignMode.InteractionType.Talk &&
ActiveConversation != null)
{
canClientInteract = ActiveConversation.CanClientStartConversation(c);
}
msg.WriteByte((byte)(canClientInteract ? CampaignInteractionType : CampaignMode.InteractionType.None));
msg.WriteBoolean(RequireConsciousnessForCustomInteract);
break;
case ObjectiveManagerStateEventData objectiveManagerStateEventData:
@@ -555,6 +586,7 @@ namespace Barotrauma
break;
case UpdateExperienceEventData _:
msg.WriteInt32(Info.ExperiencePoints);
msg.WriteInt32(info.AdditionalTalentPoints);
break;
case UpdateTalentsEventData _:
msg.WriteUInt16((ushort)characterTalents.Count);
@@ -565,9 +597,16 @@ namespace Barotrauma
}
break;
case UpdateMoneyEventData _:
msg.WriteInt32(GameMain.GameSession.Campaign.GetWallet(c).Balance);
msg.WriteInt32(Wallet?.Balance ?? 0);
break;
case UpdateRefundPointsEventData when Info is { } i:
msg.WriteInt32(i.TalentRefundPoints);
break;
case ConfirmRefundEventData:
// No data
break;
case UpdatePermanentStatsEventData updatePermanentStatsEventData:
StatTypes statType = updatePermanentStatsEventData.StatType;
if (Info == null)
{
@@ -644,6 +683,7 @@ namespace Barotrauma
{
CharacterHealth.ServerWrite(msg);
}
if (AnimController?.LimbJoints == null)
{
//0 limbs severed
@@ -699,7 +739,7 @@ namespace Barotrauma
return;
}
Client ownerClient = GameMain.Server.ConnectedClients.Find(c => c.Character == this);
Client ownerClient = GameMain.Server.ConnectedClients.Find(c => c.Character == this && (!c.SpectateOnly || !GameMain.Server.ServerSettings.AllowSpectating));
if (ownerClient != null)
{
msg.WriteBoolean(true);
@@ -0,0 +1,70 @@
using Barotrauma.Networking;
using System.Collections.Generic;
using System.Linq;
namespace Barotrauma
{
partial class Limb : ISerializableEntity, ISpatialEntity
{
/// <summary>
/// An invisible "ghost body" used for doing lag compensation server side by allowing clients' shots to hit bodies at the positions where
/// they "used to be" back when the client fired a weapon.
/// </summary>
public PhysicsBody LagCompensatedBody { get; private set; }
/// <summary>
/// A queue of past positions of the limb.
/// </summary>
public Queue<PosInfo> MemState { get; } = new Queue<PosInfo>();
partial void InitProjSpecific(ContentXElement element)
{
LagCompensatedBody = new PhysicsBody(Params, findNewContacts: false)
{
BodyType = FarseerPhysics.BodyType.Static,
CollisionCategories = Physics.CollisionLagCompensationBody,
CollidesWith = Physics.CollisionNone,
UserData = this
};
}
partial void UpdateProjSpecific(float deltaTime)
{
if (GameMain.Server == null) { return; }
MemState.Enqueue(new PosInfo(body.SimPosition, body.Rotation, body.LinearVelocity, body.AngularVelocity, (float)Timing.TotalTime));
//clear old states
while (
MemState.Any() &&
MemState.Peek().Timestamp < Timing.TotalTime - GameMain.Server.ServerSettings.MaxLagCompensationSeconds)
{
MemState.Dequeue();
}
}
public static void SetLagCompensatedBodyPositions(Client client)
{
if (GameMain.Server == null) { return; }
//convert from milliseconds to seconds, assume latency is symmetrical (time from client to server is half of the roundtrip time / ping)
float latency = client.Ping / 1000.0f / 2;
float time = (float)Timing.TotalTime - MathUtils.Min(latency, GameMain.Server.ServerSettings.MaxLagCompensationSeconds);
foreach (var character in Character.CharacterList)
{
foreach (var limb in character.AnimController.Limbs)
{
if (limb.body.Enabled == false || limb.IgnoreCollisions) { continue; }
var matchingState = limb.MemState.FirstOrDefault(l => l.Timestamp <= time);
if (matchingState == null) { continue; }
limb.LagCompensatedBody.SetTransformIgnoreContacts(matchingState.Position, matchingState.Rotation ?? 0.0f);
}
}
}
partial void RemoveProjSpecific()
{
LagCompensatedBody.Remove();
}
}
}