Unstable 0.17.1.0
This commit is contained in:
@@ -112,395 +112,395 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
public void ClientWriteInput(IWriteMessage msg)
|
||||
{
|
||||
if (extraData != null)
|
||||
msg.Write((byte)ClientNetObject.CHARACTER_INPUT);
|
||||
|
||||
if (memInput.Count > 60)
|
||||
{
|
||||
switch ((NetEntityEvent.Type)extraData[0])
|
||||
memInput.RemoveRange(60, memInput.Count - 60);
|
||||
}
|
||||
|
||||
msg.Write(LastNetworkUpdateID);
|
||||
byte inputCount = Math.Min((byte)memInput.Count, (byte)60);
|
||||
msg.Write(inputCount);
|
||||
for (int i = 0; i < inputCount; i++)
|
||||
{
|
||||
msg.WriteRangedInteger((int)memInput[i].states, 0, (int)InputNetFlags.MaxVal);
|
||||
msg.Write(memInput[i].intAim);
|
||||
if (memInput[i].states.HasFlag(InputNetFlags.Select) ||
|
||||
memInput[i].states.HasFlag(InputNetFlags.Deselect) ||
|
||||
memInput[i].states.HasFlag(InputNetFlags.Use) ||
|
||||
memInput[i].states.HasFlag(InputNetFlags.Health) ||
|
||||
memInput[i].states.HasFlag(InputNetFlags.Grab))
|
||||
{
|
||||
case NetEntityEvent.Type.InventoryState:
|
||||
msg.WriteRangedInteger(0, 0, 4);
|
||||
Inventory.ClientWrite(msg, extraData);
|
||||
break;
|
||||
case NetEntityEvent.Type.Treatment:
|
||||
msg.WriteRangedInteger(1, 0, 4);
|
||||
msg.Write(AnimController.Anim == AnimController.Animation.CPR);
|
||||
break;
|
||||
case NetEntityEvent.Type.Status:
|
||||
msg.WriteRangedInteger(2, 0, 4);
|
||||
break;
|
||||
case NetEntityEvent.Type.UpdateTalents:
|
||||
msg.WriteRangedInteger(3, 0, 4);
|
||||
msg.Write((ushort)characterTalents.Count);
|
||||
foreach (var unlockedTalent in characterTalents)
|
||||
{
|
||||
msg.Write(unlockedTalent.Prefab.UintIdentifier);
|
||||
}
|
||||
break;
|
||||
msg.Write(memInput[i].interact);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
if (!(extraData is IEventData eventData)) { throw new Exception($"Malformed character event: expected {nameof(Character)}.{nameof(IEventData)}"); }
|
||||
|
||||
msg.WriteRangedInteger((int)eventData.EventType, (int)EventType.MinValue, (int)EventType.MaxValue);
|
||||
switch (eventData)
|
||||
{
|
||||
case InventoryStateEventData inventoryStateEventData:
|
||||
Inventory.ClientEventWrite(msg, inventoryStateEventData);
|
||||
break;
|
||||
case TreatmentEventData _:
|
||||
msg.Write(AnimController.Anim == AnimController.Animation.CPR);
|
||||
break;
|
||||
case StatusEventData _:
|
||||
//do nothing
|
||||
break;
|
||||
case UpdateTalentsEventData _:
|
||||
msg.Write((ushort)characterTalents.Count);
|
||||
foreach (var unlockedTalent in characterTalents)
|
||||
{
|
||||
msg.Write(unlockedTalent.Prefab.UintIdentifier);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Exception($"Malformed character event: did not expect {eventData.GetType().Name}");
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientReadPosition(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
bool facingRight = AnimController.Dir > 0.0f;
|
||||
|
||||
lastRecvPositionUpdateTime = (float)Lidgren.Network.NetTime.Now;
|
||||
|
||||
AnimController.Frozen = false;
|
||||
Enabled = true;
|
||||
|
||||
UInt16 networkUpdateID = 0;
|
||||
if (msg.ReadBoolean())
|
||||
{
|
||||
networkUpdateID = msg.ReadUInt16();
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.Write((byte)ClientNetObject.CHARACTER_INPUT);
|
||||
bool aimInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Aim].Held = aimInput;
|
||||
keys[(int)InputType.Aim].SetState(false, aimInput);
|
||||
|
||||
if (memInput.Count > 60)
|
||||
bool shootInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Shoot].Held = shootInput;
|
||||
keys[(int)InputType.Shoot].SetState(false, shootInput);
|
||||
|
||||
bool useInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Use].Held = useInput;
|
||||
keys[(int)InputType.Use].SetState(false, useInput);
|
||||
|
||||
if (AnimController is HumanoidAnimController)
|
||||
{
|
||||
memInput.RemoveRange(60, memInput.Count - 60);
|
||||
bool crouching = msg.ReadBoolean();
|
||||
keys[(int)InputType.Crouch].Held = crouching;
|
||||
keys[(int)InputType.Crouch].SetState(false, crouching);
|
||||
}
|
||||
|
||||
msg.Write(LastNetworkUpdateID);
|
||||
byte inputCount = Math.Min((byte)memInput.Count, (byte)60);
|
||||
msg.Write(inputCount);
|
||||
for (int i = 0; i < inputCount; i++)
|
||||
{
|
||||
msg.WriteRangedInteger((int)memInput[i].states, 0, (int)InputNetFlags.MaxVal);
|
||||
msg.Write(memInput[i].intAim);
|
||||
if (memInput[i].states.HasFlag(InputNetFlags.Select) ||
|
||||
memInput[i].states.HasFlag(InputNetFlags.Deselect) ||
|
||||
memInput[i].states.HasFlag(InputNetFlags.Use) ||
|
||||
memInput[i].states.HasFlag(InputNetFlags.Health) ||
|
||||
memInput[i].states.HasFlag(InputNetFlags.Grab))
|
||||
{
|
||||
msg.Write(memInput[i].interact);
|
||||
}
|
||||
}
|
||||
bool attackInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Attack].Held = attackInput;
|
||||
keys[(int)InputType.Attack].SetState(false, attackInput);
|
||||
|
||||
double aimAngle = msg.ReadUInt16() / 65535.0 * 2.0 * Math.PI;
|
||||
cursorPosition = AimRefPosition + new Vector2((float)Math.Cos(aimAngle), (float)Math.Sin(aimAngle)) * 500.0f;
|
||||
TransformCursorPos();
|
||||
|
||||
bool ragdollInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Ragdoll].Held = ragdollInput;
|
||||
keys[(int)InputType.Ragdoll].SetState(false, ragdollInput);
|
||||
|
||||
facingRight = msg.ReadBoolean();
|
||||
}
|
||||
msg.WritePadBits();
|
||||
}
|
||||
|
||||
public virtual void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
{
|
||||
switch (type)
|
||||
bool entitySelected = msg.ReadBoolean();
|
||||
Character selectedCharacter = null;
|
||||
Item selectedItem = null;
|
||||
|
||||
AnimController.Animation animation = AnimController.Animation.None;
|
||||
if (entitySelected)
|
||||
{
|
||||
case ServerNetObject.ENTITY_POSITION:
|
||||
bool facingRight = AnimController.Dir > 0.0f;
|
||||
|
||||
lastRecvPositionUpdateTime = (float)Lidgren.Network.NetTime.Now;
|
||||
|
||||
AnimController.Frozen = false;
|
||||
Enabled = true;
|
||||
|
||||
UInt16 networkUpdateID = 0;
|
||||
if (msg.ReadBoolean())
|
||||
ushort characterID = msg.ReadUInt16();
|
||||
ushort itemID = msg.ReadUInt16();
|
||||
selectedCharacter = FindEntityByID(characterID) as Character;
|
||||
selectedItem = FindEntityByID(itemID) as Item;
|
||||
if (characterID != NullEntityID)
|
||||
{
|
||||
bool doingCpr = msg.ReadBoolean();
|
||||
if (doingCpr && SelectedCharacter != null)
|
||||
{
|
||||
networkUpdateID = msg.ReadUInt16();
|
||||
animation = AnimController.Animation.CPR;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool aimInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Aim].Held = aimInput;
|
||||
keys[(int)InputType.Aim].SetState(false, aimInput);
|
||||
|
||||
bool shootInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Shoot].Held = shootInput;
|
||||
keys[(int)InputType.Shoot].SetState(false, shootInput);
|
||||
|
||||
bool useInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Use].Held = useInput;
|
||||
keys[(int)InputType.Use].SetState(false, useInput);
|
||||
|
||||
if (AnimController is HumanoidAnimController)
|
||||
{
|
||||
bool crouching = msg.ReadBoolean();
|
||||
keys[(int)InputType.Crouch].Held = crouching;
|
||||
keys[(int)InputType.Crouch].SetState(false, crouching);
|
||||
}
|
||||
|
||||
bool attackInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Attack].Held = attackInput;
|
||||
keys[(int)InputType.Attack].SetState(false, attackInput);
|
||||
|
||||
double aimAngle = msg.ReadUInt16() / 65535.0 * 2.0 * Math.PI;
|
||||
cursorPosition = AimRefPosition + new Vector2((float)Math.Cos(aimAngle), (float)Math.Sin(aimAngle)) * 500.0f;
|
||||
TransformCursorPos();
|
||||
|
||||
bool ragdollInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Ragdoll].Held = ragdollInput;
|
||||
keys[(int)InputType.Ragdoll].SetState(false, ragdollInput);
|
||||
|
||||
facingRight = msg.ReadBoolean();
|
||||
}
|
||||
|
||||
bool entitySelected = msg.ReadBoolean();
|
||||
Character selectedCharacter = null;
|
||||
Item selectedItem = null;
|
||||
|
||||
AnimController.Animation animation = AnimController.Animation.None;
|
||||
if (entitySelected)
|
||||
{
|
||||
ushort characterID = msg.ReadUInt16();
|
||||
ushort itemID = msg.ReadUInt16();
|
||||
selectedCharacter = FindEntityByID(characterID) as Character;
|
||||
selectedItem = FindEntityByID(itemID) as Item;
|
||||
if (characterID != NullEntityID)
|
||||
{
|
||||
bool doingCpr = msg.ReadBoolean();
|
||||
if (doingCpr && SelectedCharacter != null)
|
||||
{
|
||||
animation = AnimController.Animation.CPR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 pos = new Vector2(
|
||||
msg.ReadSingle(),
|
||||
msg.ReadSingle());
|
||||
float MaxVel = NetConfig.MaxPhysicsBodyVelocity;
|
||||
Vector2 linearVelocity = new Vector2(
|
||||
msg.ReadRangedSingle(-MaxVel, MaxVel, 12),
|
||||
msg.ReadRangedSingle(-MaxVel, MaxVel, 12));
|
||||
linearVelocity = NetConfig.Quantize(linearVelocity, -MaxVel, MaxVel, 12);
|
||||
|
||||
bool fixedRotation = msg.ReadBoolean();
|
||||
float? rotation = null;
|
||||
float? angularVelocity = null;
|
||||
if (!fixedRotation)
|
||||
{
|
||||
rotation = msg.ReadSingle();
|
||||
float MaxAngularVel = NetConfig.MaxPhysicsBodyAngularVelocity;
|
||||
angularVelocity = msg.ReadRangedSingle(-MaxAngularVel, MaxAngularVel, 8);
|
||||
angularVelocity = NetConfig.Quantize(angularVelocity.Value, -MaxAngularVel, MaxAngularVel, 8);
|
||||
}
|
||||
|
||||
bool readStatus = msg.ReadBoolean();
|
||||
if (readStatus)
|
||||
{
|
||||
ReadStatus(msg);
|
||||
AIController?.ClientRead(msg);
|
||||
}
|
||||
|
||||
msg.ReadPadBits();
|
||||
|
||||
int index = 0;
|
||||
if (GameMain.Client.Character == this && CanMove)
|
||||
{
|
||||
var posInfo = new CharacterStateInfo(
|
||||
pos, rotation,
|
||||
networkUpdateID,
|
||||
facingRight ? Direction.Right : Direction.Left,
|
||||
selectedCharacter, selectedItem, animation);
|
||||
|
||||
while (index < memState.Count && NetIdUtils.IdMoreRecent(posInfo.ID, memState[index].ID))
|
||||
index++;
|
||||
memState.Insert(index, posInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
var posInfo = new CharacterStateInfo(
|
||||
pos, rotation,
|
||||
linearVelocity, angularVelocity,
|
||||
sendingTime, facingRight ? Direction.Right : Direction.Left,
|
||||
selectedCharacter, selectedItem, animation);
|
||||
|
||||
while (index < memState.Count && posInfo.Timestamp > memState[index].Timestamp)
|
||||
index++;
|
||||
memState.Insert(index, posInfo);
|
||||
}
|
||||
|
||||
break;
|
||||
case ServerNetObject.ENTITY_EVENT:
|
||||
int eventType = msg.ReadRangedInteger(0, 13);
|
||||
switch (eventType)
|
||||
{
|
||||
case 0: //NetEntityEvent.Type.InventoryState
|
||||
if (Inventory == null)
|
||||
{
|
||||
string errorMsg = "Received an inventory update message for an entity with no inventory ([name], removed: " + Removed + ")";
|
||||
DebugConsole.ThrowError(errorMsg.Replace("[name]", Name));
|
||||
GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ClientRead:NoInventory" + ID, GameAnalyticsManager.ErrorSeverity.Error, errorMsg.Replace("[name]", SpeciesName.Value));
|
||||
|
||||
//read anyway to prevent messing up reading the rest of the message
|
||||
_ = msg.ReadUInt16();
|
||||
byte inventoryItemCount = msg.ReadByte();
|
||||
for (int i = 0; i < inventoryItemCount; i++)
|
||||
{
|
||||
msg.ReadUInt16();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Inventory.ClientRead(type, msg, sendingTime);
|
||||
}
|
||||
break;
|
||||
case 1: //NetEntityEvent.Type.Control
|
||||
bool myCharacter = msg.ReadBoolean();
|
||||
byte ownerID = msg.ReadByte();
|
||||
ResetNetState();
|
||||
if (myCharacter)
|
||||
{
|
||||
if (controlled != null)
|
||||
{
|
||||
LastNetworkUpdateID = controlled.LastNetworkUpdateID;
|
||||
}
|
||||
|
||||
if (!IsDead) { Controlled = this; }
|
||||
IsRemotePlayer = false;
|
||||
GameMain.Client.HasSpawned = true;
|
||||
GameMain.Client.Character = this;
|
||||
GameMain.LightManager.LosEnabled = true;
|
||||
GameMain.LightManager.LosAlpha = 1f;
|
||||
GameMain.Client.WaitForNextRoundRespawn = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (controlled == this)
|
||||
{
|
||||
Controlled = null;
|
||||
IsRemotePlayer = ownerID > 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2: //NetEntityEvent.Type.Status
|
||||
ReadStatus(msg);
|
||||
break;
|
||||
case 3: //NetEntityEvent.Type.UpdateSkills
|
||||
int skillCount = msg.ReadByte();
|
||||
for (int i = 0; i < skillCount; i++)
|
||||
{
|
||||
Identifier skillIdentifier = msg.ReadIdentifier();
|
||||
float skillLevel = msg.ReadSingle();
|
||||
info?.SetSkillLevel(skillIdentifier, skillLevel);
|
||||
}
|
||||
break;
|
||||
case 4: // NetEntityEvent.Type.SetAttackTarget
|
||||
case 5: //NetEntityEvent.Type.ExecuteAttack
|
||||
int attackLimbIndex = msg.ReadByte();
|
||||
UInt16 targetEntityID = msg.ReadUInt16();
|
||||
int targetLimbIndex = msg.ReadByte();
|
||||
Vector2 targetSimPos = new Vector2(msg.ReadSingle(), msg.ReadSingle());
|
||||
//255 = entity already removed, no need to do anything
|
||||
if (attackLimbIndex == 255 || Removed) { break; }
|
||||
if (attackLimbIndex >= AnimController.Limbs.Length)
|
||||
{
|
||||
DebugConsole.ThrowError($"Received invalid {(eventType == 4 ? "SetAttackTarget" : "ExecuteAttack")} message. Limb index out of bounds (character: {Name}, limb index: {attackLimbIndex}, limb count: {AnimController.Limbs.Length})");
|
||||
break;
|
||||
}
|
||||
Limb attackLimb = AnimController.Limbs[attackLimbIndex];
|
||||
Limb targetLimb = null;
|
||||
IDamageable targetEntity = FindEntityByID(targetEntityID) as IDamageable;
|
||||
if (targetEntity == null && eventType == 4)
|
||||
{
|
||||
DebugConsole.ThrowError($"Received invalid SetAttackTarget message. Target entity not found (ID {targetEntityID})");
|
||||
break;
|
||||
}
|
||||
if (targetEntity is Character targetCharacter)
|
||||
{
|
||||
if (targetLimbIndex >= targetCharacter.AnimController.Limbs.Length)
|
||||
{
|
||||
DebugConsole.ThrowError($"Received invalid {(eventType == 4 ? "SetAttackTarget" : "ExecuteAttack")} message. Target limb index out of bounds (target character: {targetCharacter.Name}, limb index: {targetLimbIndex}, limb count: {targetCharacter.AnimController.Limbs.Length})");
|
||||
break;
|
||||
}
|
||||
targetLimb = targetCharacter.AnimController.Limbs[targetLimbIndex];
|
||||
}
|
||||
if (attackLimb?.attack != null && Controlled != this)
|
||||
{
|
||||
if (eventType == 4)
|
||||
{
|
||||
SetAttackTarget(attackLimb, targetEntity, targetSimPos);
|
||||
PlaySound(CharacterSound.SoundType.Attack, maxInterval: 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
attackLimb.ExecuteAttack(targetEntity, targetLimb, out _);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 6: //NetEntityEvent.Type.AssignCampaignInteraction
|
||||
byte campaignInteractionType = msg.ReadByte();
|
||||
bool requireConsciousness = msg.ReadBoolean();
|
||||
(GameMain.GameSession?.GameMode as CampaignMode)?.AssignNPCMenuInteraction(this, (CampaignMode.InteractionType)campaignInteractionType);
|
||||
RequireConsciousnessForCustomInteract = requireConsciousness;
|
||||
break;
|
||||
case 7: //NetEntityEvent.Type.ObjectiveManagerState
|
||||
// 1 = order, 2 = objective
|
||||
int msgType = msg.ReadRangedInteger(0, 2);
|
||||
if (msgType == 0) { break; }
|
||||
bool validData = msg.ReadBoolean();
|
||||
if (!validData) { break; }
|
||||
if (msgType == 1)
|
||||
{
|
||||
UInt32 orderPrefabUintIdentifier = msg.ReadUInt32();
|
||||
var orderPrefab = OrderPrefab.Prefabs.Find(p => p.UintIdentifier == orderPrefabUintIdentifier);
|
||||
Identifier option = Identifier.Empty;
|
||||
if (orderPrefab.HasOptions)
|
||||
{
|
||||
int optionIndex = msg.ReadRangedInteger(-1, orderPrefab.AllOptions.Length);
|
||||
if (optionIndex > -1)
|
||||
{
|
||||
option = orderPrefab.AllOptions[optionIndex];
|
||||
}
|
||||
}
|
||||
GameMain.GameSession?.CrewManager?.SetOrderHighlight(this, orderPrefab.Identifier, option);
|
||||
}
|
||||
else if (msgType == 2)
|
||||
{
|
||||
Identifier identifier = msg.ReadIdentifier();
|
||||
Identifier option = msg.ReadIdentifier();
|
||||
ushort objectiveTargetEntityId = msg.ReadUInt16();
|
||||
var objectiveTargetEntity = FindEntityByID(objectiveTargetEntityId);
|
||||
GameMain.GameSession?.CrewManager?.CreateObjectiveIcon(this, identifier, option, objectiveTargetEntity);
|
||||
}
|
||||
break;
|
||||
case 8: //NetEntityEvent.Type.TeamChange
|
||||
byte newTeamId = msg.ReadByte();
|
||||
ChangeTeam((CharacterTeamType)newTeamId);
|
||||
break;
|
||||
case 9: //NetEntityEvent.Type.AddToCrew
|
||||
GameMain.GameSession.CrewManager.AddCharacter(this);
|
||||
CharacterTeamType teamID = (CharacterTeamType)msg.ReadByte();
|
||||
ushort itemCount = msg.ReadUInt16();
|
||||
for (int i = 0; i < itemCount; i++)
|
||||
{
|
||||
ushort itemID = msg.ReadUInt16();
|
||||
if (!(Entity.FindEntityByID(itemID) is Item item)) { continue; }
|
||||
item.AllowStealing = true;
|
||||
var wifiComponent = item.GetComponent<WifiComponent>();
|
||||
if (wifiComponent != null)
|
||||
{
|
||||
wifiComponent.TeamID = teamID;
|
||||
}
|
||||
var idCard = item.GetComponent<IdCard>();
|
||||
if (idCard != null)
|
||||
{
|
||||
idCard.TeamID = teamID;
|
||||
idCard.SubmarineSpecificID = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 10: //NetEntityEvent.Type.UpdateExperience
|
||||
int experienceAmount = msg.ReadInt32();
|
||||
info?.SetExperience(experienceAmount);
|
||||
break;
|
||||
case 11: //NetEntityEvent.Type.UpdateTalents:
|
||||
ushort talentCount = msg.ReadUInt16();
|
||||
for (int i = 0; i < talentCount; i++)
|
||||
{
|
||||
bool addedThisRound = msg.ReadBoolean();
|
||||
UInt32 talentIdentifier = msg.ReadUInt32();
|
||||
GiveTalent(talentIdentifier, addedThisRound);
|
||||
}
|
||||
break;
|
||||
case 12: //NetEntityEvent.Type.UpdateMoney:
|
||||
int moneyAmount = msg.ReadInt32();
|
||||
SetMoney(moneyAmount);
|
||||
break;
|
||||
case 13: //NetEntityEvent.Type.UpdatePermanentStats:
|
||||
byte savedStatValueCount = msg.ReadByte();
|
||||
StatTypes statType = (StatTypes)msg.ReadByte();
|
||||
info?.ClearSavedStatValues(statType);
|
||||
for (int i = 0; i < savedStatValueCount; i++)
|
||||
{
|
||||
string statIdentifier = msg.ReadString();
|
||||
float statValue = msg.ReadSingle();
|
||||
bool removeOnDeath = msg.ReadBoolean();
|
||||
info?.ChangeSavedStatValue(statType, statValue, statIdentifier, removeOnDeath, setValue: true);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
msg.ReadPadBits();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 pos = new Vector2(
|
||||
msg.ReadSingle(),
|
||||
msg.ReadSingle());
|
||||
float MaxVel = NetConfig.MaxPhysicsBodyVelocity;
|
||||
Vector2 linearVelocity = new Vector2(
|
||||
msg.ReadRangedSingle(-MaxVel, MaxVel, 12),
|
||||
msg.ReadRangedSingle(-MaxVel, MaxVel, 12));
|
||||
linearVelocity = NetConfig.Quantize(linearVelocity, -MaxVel, MaxVel, 12);
|
||||
|
||||
bool fixedRotation = msg.ReadBoolean();
|
||||
float? rotation = null;
|
||||
float? angularVelocity = null;
|
||||
if (!fixedRotation)
|
||||
{
|
||||
rotation = msg.ReadSingle();
|
||||
float MaxAngularVel = NetConfig.MaxPhysicsBodyAngularVelocity;
|
||||
angularVelocity = msg.ReadRangedSingle(-MaxAngularVel, MaxAngularVel, 8);
|
||||
angularVelocity = NetConfig.Quantize(angularVelocity.Value, -MaxAngularVel, MaxAngularVel, 8);
|
||||
}
|
||||
|
||||
bool readStatus = msg.ReadBoolean();
|
||||
if (readStatus)
|
||||
{
|
||||
ReadStatus(msg);
|
||||
AIController?.ClientRead(msg);
|
||||
}
|
||||
|
||||
msg.ReadPadBits();
|
||||
|
||||
int index = 0;
|
||||
if (GameMain.Client.Character == this && CanMove)
|
||||
{
|
||||
var posInfo = new CharacterStateInfo(
|
||||
pos, rotation,
|
||||
networkUpdateID,
|
||||
facingRight ? Direction.Right : Direction.Left,
|
||||
selectedCharacter, selectedItem, animation);
|
||||
|
||||
while (index < memState.Count && NetIdUtils.IdMoreRecent(posInfo.ID, memState[index].ID))
|
||||
index++;
|
||||
memState.Insert(index, posInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
var posInfo = new CharacterStateInfo(
|
||||
pos, rotation,
|
||||
linearVelocity, angularVelocity,
|
||||
sendingTime, facingRight ? Direction.Right : Direction.Left,
|
||||
selectedCharacter, selectedItem, animation);
|
||||
|
||||
while (index < memState.Count && posInfo.Timestamp > memState[index].Timestamp)
|
||||
index++;
|
||||
memState.Insert(index, posInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
EventType eventType = (EventType)msg.ReadRangedInteger((int)EventType.MinValue, (int)EventType.MaxValue);
|
||||
switch (eventType)
|
||||
{
|
||||
case EventType.InventoryState:
|
||||
if (Inventory == null)
|
||||
{
|
||||
string errorMsg = "Received an inventory update message for an entity with no inventory ([name], removed: " + Removed + ")";
|
||||
DebugConsole.ThrowError(errorMsg.Replace("[name]", Name));
|
||||
GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ClientRead:NoInventory" + ID, GameAnalyticsManager.ErrorSeverity.Error, errorMsg.Replace("[name]", SpeciesName.Value));
|
||||
|
||||
//read anyway to prevent messing up reading the rest of the message
|
||||
_ = msg.ReadUInt16();
|
||||
byte inventoryItemCount = msg.ReadByte();
|
||||
for (int i = 0; i < inventoryItemCount; i++)
|
||||
{
|
||||
msg.ReadUInt16();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Inventory.ClientEventRead(msg, sendingTime);
|
||||
}
|
||||
break;
|
||||
case EventType.Control:
|
||||
bool myCharacter = msg.ReadBoolean();
|
||||
byte ownerID = msg.ReadByte();
|
||||
ResetNetState();
|
||||
if (myCharacter)
|
||||
{
|
||||
if (controlled != null)
|
||||
{
|
||||
LastNetworkUpdateID = controlled.LastNetworkUpdateID;
|
||||
}
|
||||
|
||||
if (!IsDead) { Controlled = this; }
|
||||
IsRemotePlayer = false;
|
||||
GameMain.Client.HasSpawned = true;
|
||||
GameMain.Client.Character = this;
|
||||
GameMain.LightManager.LosEnabled = true;
|
||||
GameMain.LightManager.LosAlpha = 1f;
|
||||
GameMain.Client.WaitForNextRoundRespawn = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (controlled == this)
|
||||
{
|
||||
Controlled = null;
|
||||
IsRemotePlayer = ownerID > 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EventType.Status:
|
||||
ReadStatus(msg);
|
||||
break;
|
||||
case EventType.UpdateSkills:
|
||||
int skillCount = msg.ReadByte();
|
||||
for (int i = 0; i < skillCount; i++)
|
||||
{
|
||||
Identifier skillIdentifier = msg.ReadIdentifier();
|
||||
float skillLevel = msg.ReadSingle();
|
||||
info?.SetSkillLevel(skillIdentifier, skillLevel);
|
||||
}
|
||||
break;
|
||||
case EventType.SetAttackTarget:
|
||||
case EventType.ExecuteAttack:
|
||||
int attackLimbIndex = msg.ReadByte();
|
||||
UInt16 targetEntityID = msg.ReadUInt16();
|
||||
int targetLimbIndex = msg.ReadByte();
|
||||
float targetX = msg.ReadSingle();
|
||||
float targetY = msg.ReadSingle();
|
||||
Vector2 targetSimPos = new Vector2(targetX, targetY);
|
||||
//255 = entity already removed, no need to do anything
|
||||
if (attackLimbIndex == 255 || Removed) { break; }
|
||||
if (attackLimbIndex >= AnimController.Limbs.Length)
|
||||
{
|
||||
DebugConsole.ThrowError($"Received invalid {(eventType == EventType.SetAttackTarget ? "SetAttackTarget" : "ExecuteAttack")} message. Limb index out of bounds (character: {Name}, limb index: {attackLimbIndex}, limb count: {AnimController.Limbs.Length})");
|
||||
break;
|
||||
}
|
||||
Limb attackLimb = AnimController.Limbs[attackLimbIndex];
|
||||
Limb targetLimb = null;
|
||||
IDamageable targetEntity = FindEntityByID(targetEntityID) as IDamageable;
|
||||
if (targetEntity == null && eventType == EventType.SetAttackTarget)
|
||||
{
|
||||
DebugConsole.ThrowError($"Received invalid SetAttackTarget message. Target entity not found (ID {targetEntityID})");
|
||||
break;
|
||||
}
|
||||
if (targetEntity is Character targetCharacter)
|
||||
{
|
||||
if (targetLimbIndex >= targetCharacter.AnimController.Limbs.Length)
|
||||
{
|
||||
DebugConsole.ThrowError($"Received invalid {(eventType == EventType.SetAttackTarget ? "SetAttackTarget" : "ExecuteAttack")} message. Target limb index out of bounds (target character: {targetCharacter.Name}, limb index: {targetLimbIndex}, limb count: {targetCharacter.AnimController.Limbs.Length})");
|
||||
break;
|
||||
}
|
||||
targetLimb = targetCharacter.AnimController.Limbs[targetLimbIndex];
|
||||
}
|
||||
if (attackLimb?.attack != null && Controlled != this)
|
||||
{
|
||||
if (eventType == EventType.SetAttackTarget)
|
||||
{
|
||||
SetAttackTarget(attackLimb, targetEntity, targetSimPos);
|
||||
PlaySound(CharacterSound.SoundType.Attack, maxInterval: 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
attackLimb.ExecuteAttack(targetEntity, targetLimb, out _);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EventType.AssignCampaignInteraction:
|
||||
byte campaignInteractionType = msg.ReadByte();
|
||||
bool requireConsciousness = msg.ReadBoolean();
|
||||
(GameMain.GameSession?.GameMode as CampaignMode)?.AssignNPCMenuInteraction(this, (CampaignMode.InteractionType)campaignInteractionType);
|
||||
RequireConsciousnessForCustomInteract = requireConsciousness;
|
||||
break;
|
||||
case EventType.ObjectiveManagerState:
|
||||
// 1 = order, 2 = objective
|
||||
AIObjectiveManager.ObjectiveType msgType
|
||||
= (AIObjectiveManager.ObjectiveType)msg.ReadRangedInteger(
|
||||
(int)AIObjectiveManager.ObjectiveType.MinValue,
|
||||
(int)AIObjectiveManager.ObjectiveType.MaxValue);
|
||||
if (msgType == 0) { break; }
|
||||
bool validData = msg.ReadBoolean();
|
||||
if (!validData) { break; }
|
||||
if (msgType == AIObjectiveManager.ObjectiveType.Order)
|
||||
{
|
||||
UInt32 orderPrefabUintIdentifier = msg.ReadUInt32();
|
||||
var orderPrefab = OrderPrefab.Prefabs.Find(p => p.UintIdentifier == orderPrefabUintIdentifier);
|
||||
Identifier option = Identifier.Empty;
|
||||
if (orderPrefab.HasOptions)
|
||||
{
|
||||
int optionIndex = msg.ReadRangedInteger(-1, orderPrefab.AllOptions.Length);
|
||||
if (optionIndex > -1)
|
||||
{
|
||||
option = orderPrefab.AllOptions[optionIndex];
|
||||
}
|
||||
}
|
||||
GameMain.GameSession?.CrewManager?.SetOrderHighlight(this, orderPrefab.Identifier, option);
|
||||
}
|
||||
else if (msgType == AIObjectiveManager.ObjectiveType.Objective)
|
||||
{
|
||||
Identifier identifier = msg.ReadIdentifier();
|
||||
Identifier option = msg.ReadIdentifier();
|
||||
ushort objectiveTargetEntityId = msg.ReadUInt16();
|
||||
var objectiveTargetEntity = FindEntityByID(objectiveTargetEntityId);
|
||||
GameMain.GameSession?.CrewManager?.CreateObjectiveIcon(this, identifier, option, objectiveTargetEntity);
|
||||
}
|
||||
break;
|
||||
case EventType.TeamChange:
|
||||
byte newTeamId = msg.ReadByte();
|
||||
ChangeTeam((CharacterTeamType)newTeamId);
|
||||
break;
|
||||
case EventType.AddToCrew:
|
||||
GameMain.GameSession.CrewManager.AddCharacter(this);
|
||||
CharacterTeamType teamID = (CharacterTeamType)msg.ReadByte();
|
||||
ushort itemCount = msg.ReadUInt16();
|
||||
for (int i = 0; i < itemCount; i++)
|
||||
{
|
||||
ushort itemID = msg.ReadUInt16();
|
||||
if (!(Entity.FindEntityByID(itemID) is Item item)) { continue; }
|
||||
item.AllowStealing = true;
|
||||
var wifiComponent = item.GetComponent<WifiComponent>();
|
||||
if (wifiComponent != null)
|
||||
{
|
||||
wifiComponent.TeamID = teamID;
|
||||
}
|
||||
var idCard = item.GetComponent<IdCard>();
|
||||
if (idCard != null)
|
||||
{
|
||||
idCard.TeamID = teamID;
|
||||
idCard.SubmarineSpecificID = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EventType.UpdateExperience:
|
||||
int experienceAmount = msg.ReadInt32();
|
||||
info?.SetExperience(experienceAmount);
|
||||
break;
|
||||
case EventType.UpdateTalents:
|
||||
ushort talentCount = msg.ReadUInt16();
|
||||
for (int i = 0; i < talentCount; i++)
|
||||
{
|
||||
bool addedThisRound = msg.ReadBoolean();
|
||||
UInt32 talentIdentifier = msg.ReadUInt32();
|
||||
GiveTalent(talentIdentifier, addedThisRound);
|
||||
}
|
||||
break;
|
||||
case EventType.UpdateMoney:
|
||||
int moneyAmount = msg.ReadInt32();
|
||||
SetMoney(moneyAmount);
|
||||
break;
|
||||
case EventType.UpdatePermanentStats:
|
||||
byte savedStatValueCount = msg.ReadByte();
|
||||
StatTypes statType = (StatTypes)msg.ReadByte();
|
||||
info?.ClearSavedStatValues(statType);
|
||||
for (int i = 0; i < savedStatValueCount; i++)
|
||||
{
|
||||
string statIdentifier = msg.ReadString();
|
||||
float statValue = msg.ReadSingle();
|
||||
bool removeOnDeath = msg.ReadBoolean();
|
||||
info?.ChangeSavedStatValue(statType, statValue, statIdentifier, removeOnDeath, setValue: true);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
msg.ReadPadBits();
|
||||
}
|
||||
|
||||
public static Character ReadSpawnData(IReadMessage inc)
|
||||
@@ -542,6 +542,8 @@ namespace Barotrauma
|
||||
{
|
||||
bool hasOwner = inc.ReadBoolean();
|
||||
int ownerId = hasOwner ? inc.ReadByte() : -1;
|
||||
int balance = hasOwner ? inc.ReadInt32() : -1;
|
||||
int rewardDistribution = hasOwner ? inc.ReadRangedInteger(0, 100) : -1;
|
||||
byte teamID = inc.ReadByte();
|
||||
bool hasAi = inc.ReadBoolean();
|
||||
Identifier infoSpeciesName = inc.ReadIdentifier();
|
||||
@@ -558,6 +560,8 @@ namespace Barotrauma
|
||||
}
|
||||
character.TeamID = (CharacterTeamType)teamID;
|
||||
character.CampaignInteractionType = (CampaignMode.InteractionType)inc.ReadByte();
|
||||
character.Wallet.Balance = balance;
|
||||
character.Wallet.RewardDistribution = rewardDistribution;
|
||||
if (character.CampaignInteractionType != CampaignMode.InteractionType.None)
|
||||
{
|
||||
(GameMain.GameSession.GameMode as CampaignMode)?.AssignNPCMenuInteraction(character, character.CampaignInteractionType);
|
||||
@@ -597,7 +601,7 @@ namespace Barotrauma
|
||||
: Identifier.Empty)
|
||||
.WithManualPriority(orderPriority)
|
||||
.WithOrderGiver(orderGiver);
|
||||
character.SetOrder(order, speak: false, force: true);
|
||||
character.SetOrder(order, isNewOrder: true, speak: false, force: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user