public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) { bool isDocked = msg.ReadBoolean(); for (int i = 0; i < 2; i++) { if (hulls[i] == null) { continue; } item.linkedTo.Remove(hulls[i]); hulls[i].Remove(); hulls[i] = null; } if (gap != null) { item.linkedTo.Remove(gap); gap.Remove(); gap = null; } if (isDocked) { ushort dockingTargetID = msg.ReadUInt16(); bool isLocked = msg.ReadBoolean(); Entity targetEntity = Entity.FindEntityByID(dockingTargetID); if (targetEntity == null || !(targetEntity is Item)) { DebugConsole.ThrowError("Invalid docking port network event (can't dock to " + (targetEntity?.ToString() ?? "null") + ")"); return; } DockingTarget = (targetEntity as Item).GetComponent <DockingPort>(); if (DockingTarget == null) { DebugConsole.ThrowError("Invalid docking port network event (" + targetEntity + " doesn't have a docking port component)"); return; } Dock(DockingTarget); if (joint == null) { string errorMsg = "Error while reading a docking port network event (Dock method did not create a joint between the ports)." + " Submarine: " + (item.Submarine?.Info.Name ?? "null") + ", target submarine: " + (DockingTarget.item.Submarine?.Info.Name ?? "null"); if (item.Submarine?.ConnectedDockingPorts.ContainsKey(DockingTarget.item.Submarine) ?? false) { errorMsg += "\nAlready docked."; } if (item.Submarine == DockingTarget.item.Submarine) { errorMsg += "\nTrying to dock the submarine to itself."; } GameAnalyticsManager.AddErrorEventOnce("DockingPort.ClientRead:JointNotCreated", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); } if (isLocked) { if (DockingTarget.joint != null) { DockingTarget.Lock(isNetworkMessage: true); } else { Lock(isNetworkMessage: true); } } } else { Undock(); } }
public void ServerRead(ClientNetObject type, IReadMessage msg, Client c) { List <Item> prevItems = new List <Item>(Items); byte itemCount = msg.ReadByte(); ushort[] newItemIDs = new ushort[itemCount]; for (int i = 0; i < itemCount; i++) { newItemIDs[i] = msg.ReadUInt16(); } if (c == null || c.Character == null) { return; } bool accessible = c.Character.CanAccessInventory(this); if (this is CharacterInventory && accessible) { if (Owner == null || !(Owner is Character)) { accessible = false; } else if (!((CharacterInventory)this).AccessibleWhenAlive && !((Character)Owner).IsDead) { accessible = false; } } if (!accessible) { //create a network event to correct the client's inventory state //otherwise they may have an item in their inventory they shouldn't have been able to pick up, //and receiving an event for that inventory later will cause the item to be dropped CreateNetworkEvent(); for (int i = 0; i < capacity; i++) { if (!(Entity.FindEntityByID(newItemIDs[i]) is Item item)) { continue; } item.PositionUpdateInterval = 0.0f; if (item.ParentInventory != null && item.ParentInventory != this) { item.ParentInventory.CreateNetworkEvent(); } } return; } List <Inventory> prevItemInventories = new List <Inventory>(Items.Select(i => i?.ParentInventory)); for (int i = 0; i < capacity; i++) { Item newItem = newItemIDs[i] == 0 ? null : Entity.FindEntityByID(newItemIDs[i]) as Item; prevItemInventories.Add(newItem?.ParentInventory); if (newItemIDs[i] == 0 || (newItem != Items[i])) { if (Items[i] != null) { Item droppedItem = Items[i]; Entity prevOwner = Owner; droppedItem.Drop(null); var previousInventory = prevOwner switch { Item itemInventory => (itemInventory.FindParentInventory(inventory => inventory is CharacterInventory) as CharacterInventory), Character character => character.Inventory, _ => null }; if (previousInventory != null && previousInventory != c.Character?.Inventory) { GameMain.Server?.KarmaManager.OnItemTakenFromPlayer(previousInventory, c, droppedItem); } if (droppedItem.body != null && prevOwner != null) { droppedItem.body.SetTransform(prevOwner.SimPosition, 0.0f); } } System.Diagnostics.Debug.Assert(Items[i] == null); } } for (int i = 0; i < capacity; i++) { if (newItemIDs[i] > 0) { if (!(Entity.FindEntityByID(newItemIDs[i]) is Item item) || item == Items[i]) { continue; } if (GameMain.Server != null) { var holdable = item.GetComponent <Holdable>(); if (holdable != null && !holdable.CanBeDeattached()) { continue; } if (!prevItems.Contains(item) && !item.CanClientAccess(c)) { if (item.body != null && !c.PendingPositionUpdates.Contains(item)) { c.PendingPositionUpdates.Enqueue(item); } item.PositionUpdateInterval = 0.0f; continue; } } TryPutItem(item, i, true, true, c.Character, false); for (int j = 0; j < capacity; j++) { if (Items[j] == item && newItemIDs[j] != item.ID) { Items[j] = null; } } } } CreateNetworkEvent(); foreach (Inventory prevInventory in prevItemInventories.Distinct()) { if (prevInventory != this) { prevInventory?.CreateNetworkEvent(); } } foreach (Item item in Items.Distinct()) { if (item == null) { continue; } if (!prevItems.Contains(item)) { if (Owner == c.Character) { GameServer.Log(GameServer.CharacterLogName(c.Character) + " picked up " + item.Name, ServerLog.MessageType.Inventory); } else { GameServer.Log(GameServer.CharacterLogName(c.Character) + " placed " + item.Name + " in " + Owner, ServerLog.MessageType.Inventory); } } } foreach (Item item in prevItems.Distinct()) { if (item == null) { continue; } if (!Items.Contains(item)) { if (Owner == c.Character) { GameServer.Log(GameServer.CharacterLogName(c.Character) + " dropped " + item.Name, ServerLog.MessageType.Inventory); } else { GameServer.Log(GameServer.CharacterLogName(c.Character) + " removed " + item.Name + " from " + Owner, ServerLog.MessageType.Inventory); } } } }
public virtual void ServerRead(ClientNetObject type, IReadMessage msg, Client c) { if (GameMain.Server == null) { return; } switch (type) { case ClientNetObject.CHARACTER_INPUT: if (c.Character != this) { #if DEBUG DebugConsole.Log("Received a character update message from a client who's not controlling the character"); #endif return; } UInt16 networkUpdateID = msg.ReadUInt16(); byte inputCount = msg.ReadByte(); if (AllowInput) { Enabled = true; } for (int i = 0; i < inputCount; i++) { InputNetFlags newInput = (InputNetFlags)msg.ReadRangedInteger(0, (int)InputNetFlags.MaxVal); UInt16 newAim = 0; UInt16 newInteract = 0; if (newInput != InputNetFlags.None && newInput != InputNetFlags.FacingLeft) { c.KickAFKTimer = 0.0f; } else if (AnimController.Dir < 0.0f != newInput.HasFlag(InputNetFlags.FacingLeft)) { //character changed the direction they're facing c.KickAFKTimer = 0.0f; } newAim = msg.ReadUInt16(); if (newInput.HasFlag(InputNetFlags.Select) || newInput.HasFlag(InputNetFlags.Deselect) || newInput.HasFlag(InputNetFlags.Use) || newInput.HasFlag(InputNetFlags.Health) || newInput.HasFlag(InputNetFlags.Grab)) { newInteract = msg.ReadUInt16(); } if (NetIdUtils.IdMoreRecent((ushort)(networkUpdateID - i), LastNetworkUpdateID) && (i < 60)) { if ((i > 0 && memInput[i - 1].intAim != newAim)) { c.KickAFKTimer = 0.0f; } NetInputMem newMem = new NetInputMem { states = newInput, intAim = newAim, interact = newInteract, networkUpdateID = (ushort)(networkUpdateID - i) }; memInput.Insert(i, newMem); LastInputTime = Timing.TotalTime; } } if (NetIdUtils.IdMoreRecent(networkUpdateID, LastNetworkUpdateID)) { LastNetworkUpdateID = networkUpdateID; } if (memInput.Count > 60) { //deleting inputs from the queue here means the server is way behind and data needs to be dropped //we'll make the server drop down to 30 inputs for good measure memInput.RemoveRange(30, memInput.Count - 30); } break; case ClientNetObject.ENTITY_STATE: int eventType = msg.ReadRangedInteger(0, 3); switch (eventType) { case 0: Inventory.ServerRead(type, msg, c); break; case 1: bool doingCPR = msg.ReadBoolean(); if (c.Character != this) { #if DEBUG DebugConsole.Log("Received a character update message from a client who's not controlling the character"); #endif return; } AnimController.Anim = doingCPR ? AnimController.Animation.CPR : AnimController.Animation.None; break; case 2: if (c.Character != this) { #if DEBUG DebugConsole.Log("Received a character update message from a client who's not controlling the character"); #endif return; } if (IsUnconscious) { var causeOfDeath = CharacterHealth.GetCauseOfDeath(); Kill(causeOfDeath.First, causeOfDeath.Second); } break; } break; } msg.ReadPadBits(); }
public static void ClientRead(IReadMessage msg) { UInt16 ID = msg.ReadUInt16(); ChatMessageType type = (ChatMessageType)msg.ReadByte(); PlayerConnectionChangeType changeType = PlayerConnectionChangeType.None; string txt = ""; string styleSetting = string.Empty; if (type != ChatMessageType.Order) { changeType = (PlayerConnectionChangeType)msg.ReadByte(); txt = msg.ReadString(); } string senderName = msg.ReadString(); Character senderCharacter = null; bool hasSenderCharacter = msg.ReadBoolean(); if (hasSenderCharacter) { senderCharacter = Entity.FindEntityByID(msg.ReadUInt16()) as Character; if (senderCharacter != null) { senderName = senderCharacter.Name; } } switch (type) { case ChatMessageType.Default: break; case ChatMessageType.Order: int orderIndex = msg.ReadByte(); UInt16 targetCharacterID = msg.ReadUInt16(); Character targetCharacter = Entity.FindEntityByID(targetCharacterID) as Character; Entity targetEntity = Entity.FindEntityByID(msg.ReadUInt16()); int optionIndex = msg.ReadByte(); Order order = null; if (orderIndex < 0 || orderIndex >= Order.PrefabList.Count) { DebugConsole.ThrowError("Invalid order message - order index out of bounds."); if (NetIdUtils.IdMoreRecent(ID, LastID)) { LastID = ID; } return; } else { order = Order.PrefabList[orderIndex]; } string orderOption = ""; if (optionIndex >= 0 && optionIndex < order.Options.Length) { orderOption = order.Options[optionIndex]; } txt = order.GetChatMessage(targetCharacter?.Name, senderCharacter?.CurrentHull?.DisplayName, givingOrderToSelf: targetCharacter == senderCharacter, orderOption: orderOption); if (GameMain.Client.GameStarted && Screen.Selected == GameMain.GameScreen) { if (order.TargetAllCharacters) { GameMain.GameSession?.CrewManager?.AddOrder( new Order(order.Prefab, targetEntity, (targetEntity as Item)?.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType), orderGiver: senderCharacter), order.Prefab.FadeOutTime); } else if (targetCharacter != null) { targetCharacter.SetOrder( new Order(order.Prefab, targetEntity, (targetEntity as Item)?.Components.FirstOrDefault(ic => ic.GetType() == order.ItemComponentType), orderGiver: senderCharacter), orderOption, senderCharacter); } } if (NetIdUtils.IdMoreRecent(ID, LastID)) { GameMain.Client.AddChatMessage( new OrderChatMessage(order, orderOption, txt, targetEntity, targetCharacter, senderCharacter)); LastID = ID; } return; case ChatMessageType.ServerMessageBox: txt = TextManager.GetServerMessage(txt); break; case ChatMessageType.ServerMessageBoxInGame: styleSetting = msg.ReadString(); txt = TextManager.GetServerMessage(txt); break; } if (NetIdUtils.IdMoreRecent(ID, LastID)) { switch (type) { case ChatMessageType.MessageBox: case ChatMessageType.ServerMessageBox: //only show the message box if the text differs from the text in the currently visible box if ((GUIMessageBox.VisibleBox as GUIMessageBox)?.Text?.Text != txt) { new GUIMessageBox("", txt); } break; case ChatMessageType.ServerMessageBoxInGame: new GUIMessageBox("", txt, new string[0], type: GUIMessageBox.Type.InGame, iconStyle: styleSetting); break; case ChatMessageType.Console: DebugConsole.NewMessage(txt, MessageColor[(int)ChatMessageType.Console]); break; case ChatMessageType.ServerLog: if (!Enum.TryParse(senderName, out ServerLog.MessageType messageType)) { return; } GameMain.Client.ServerSettings.ServerLog?.WriteLine(txt, messageType); break; default: GameMain.Client.AddChatMessage(txt, type, senderName, senderCharacter, changeType); break; } LastID = ID; } }
public void ReadMessage(IReadMessage inc) { System.Diagnostics.Debug.Assert(!activeTransfers.Any(t => t.Status == FileTransferStatus.Error || t.Status == FileTransferStatus.Canceled || t.Status == FileTransferStatus.Finished), "List of active file transfers contains entires that should have been removed"); byte transferMessageType = inc.ReadByte(); switch (transferMessageType) { case (byte)FileTransferMessageType.Initiate: { byte transferId = inc.ReadByte(); var existingTransfer = activeTransfers.Find(t => t.ID == transferId); finishedTransfers.RemoveAll(t => t.First == transferId); byte fileType = inc.ReadByte(); //ushort chunkLen = inc.ReadUInt16(); int fileSize = inc.ReadInt32(); string fileName = inc.ReadString(); if (existingTransfer != null) { if (fileType != (byte)existingTransfer.FileType || fileSize != existingTransfer.FileSize || fileName != existingTransfer.FileName) { GameMain.Client.CancelFileTransfer(transferId); DebugConsole.ThrowError("File transfer error: file transfer initiated with an ID that's already in use"); } else //resend acknowledgement packet { GameMain.Client.UpdateFileTransfer(transferId, 0); } return; } if (!ValidateInitialData(fileType, fileName, fileSize, out string errorMsg)) { GameMain.Client.CancelFileTransfer(transferId); DebugConsole.ThrowError("File transfer failed (" + errorMsg + ")"); return; } if (GameSettings.VerboseLogging) { DebugConsole.Log("Received file transfer initiation message: "); DebugConsole.Log(" File: " + fileName); DebugConsole.Log(" Size: " + fileSize); DebugConsole.Log(" ID: " + transferId); } string downloadFolder = downloadFolders[(FileTransferType)fileType]; if (!Directory.Exists(downloadFolder)) { try { Directory.CreateDirectory(downloadFolder); } catch (Exception e) { DebugConsole.ThrowError("Could not start a file transfer: failed to create the folder \"" + downloadFolder + "\".", e); return; } } FileTransferIn newTransfer = new FileTransferIn(inc.Sender, Path.Combine(downloadFolder, fileName), (FileTransferType)fileType) { ID = transferId, Status = FileTransferStatus.Receiving, FileSize = fileSize }; int maxRetries = 4; for (int i = 0; i <= maxRetries; i++) { try { newTransfer.OpenStream(); } catch (IOException e) { if (i < maxRetries) { DebugConsole.NewMessage("Failed to initiate a file transfer {" + e.Message + "}, retrying in 250 ms...", Color.Red); Thread.Sleep(250); } else { DebugConsole.NewMessage("Failed to initiate a file transfer {" + e.Message + "}", Color.Red); GameMain.Client.CancelFileTransfer(transferId); newTransfer.Status = FileTransferStatus.Error; OnTransferFailed(newTransfer); return; } } } activeTransfers.Add(newTransfer); GameMain.Client.UpdateFileTransfer(transferId, 0); //send acknowledgement packet } break; case (byte)FileTransferMessageType.TransferOnSameMachine: { byte transferId = inc.ReadByte(); byte fileType = inc.ReadByte(); string filePath = inc.ReadString(); if (GameSettings.VerboseLogging) { DebugConsole.Log("Received file transfer message on the same machine: "); DebugConsole.Log(" File: " + filePath); DebugConsole.Log(" ID: " + transferId); } if (!File.Exists(filePath)) { DebugConsole.ThrowError("File transfer on the same machine failed, file \"" + filePath + "\" not found."); GameMain.Client.CancelFileTransfer(transferId); return; } FileTransferIn directTransfer = new FileTransferIn(inc.Sender, filePath, (FileTransferType)fileType) { ID = transferId, Status = FileTransferStatus.Finished, FileSize = 0 }; Md5Hash.RemoveFromCache(directTransfer.FilePath); OnFinished(directTransfer); } break; case (byte)FileTransferMessageType.Data: { byte transferId = inc.ReadByte(); var activeTransfer = activeTransfers.Find(t => t.Connection == inc.Sender && t.ID == transferId); if (activeTransfer == null) { //it's possible for the server to send some extra data //before it acknowledges that the download is finished, //so let's suppress the error message in that case finishedTransfers.RemoveAll(t => t.Second + 5.0 < Timing.TotalTime); if (!finishedTransfers.Any(t => t.First == transferId)) { GameMain.Client.CancelFileTransfer(transferId); DebugConsole.ThrowError("File transfer error: received data without a transfer initiation message"); } return; } int offset = inc.ReadInt32(); if (offset != activeTransfer.Received) { if (offset < activeTransfer.Received) { GameMain.Client.UpdateFileTransfer(activeTransfer.ID, activeTransfer.Received); } return; } int bytesToRead = inc.ReadUInt16(); if (activeTransfer.Received + bytesToRead > activeTransfer.FileSize) { GameMain.Client.CancelFileTransfer(transferId); DebugConsole.ThrowError("File transfer error: Received more data than expected (total received: " + activeTransfer.Received + ", msg received: " + (inc.LengthBytes - inc.BytePosition) + ", msg length: " + inc.LengthBytes + ", msg read: " + inc.BytePosition + ", filesize: " + activeTransfer.FileSize); activeTransfer.Status = FileTransferStatus.Error; StopTransfer(activeTransfer); return; } try { activeTransfer.ReadBytes(inc, bytesToRead); } catch (Exception e) { GameMain.Client.CancelFileTransfer(transferId); DebugConsole.ThrowError("File transfer error: " + e.Message); activeTransfer.Status = FileTransferStatus.Error; StopTransfer(activeTransfer, true); return; } if (activeTransfer.Status == FileTransferStatus.Finished) { GameMain.Client.UpdateFileTransfer(activeTransfer.ID, activeTransfer.Received, true); activeTransfer.Dispose(); if (ValidateReceivedData(activeTransfer, out string errorMessage)) { finishedTransfers.Add(new Pair <int, double>(transferId, Timing.TotalTime)); StopTransfer(activeTransfer); Md5Hash.RemoveFromCache(activeTransfer.FilePath); OnFinished(activeTransfer); } else { new GUIMessageBox("File transfer aborted", errorMessage); activeTransfer.Status = FileTransferStatus.Error; StopTransfer(activeTransfer, true); } } } break; case (byte)FileTransferMessageType.Cancel: { byte transferId = inc.ReadByte(); var matchingTransfer = activeTransfers.Find(t => t.Connection == inc.Sender && t.ID == transferId); if (matchingTransfer != null) { new GUIMessageBox("File transfer cancelled", "The server has cancelled the transfer of the file \"" + matchingTransfer.FileName + "\"."); StopTransfer(matchingTransfer); } break; } } }
public static Character ReadSpawnData(IReadMessage inc, bool spawn = true) { DebugConsole.Log("Reading character spawn data"); if (GameMain.Client == null) { return(null); } bool noInfo = inc.ReadBoolean(); ushort id = inc.ReadUInt16(); string speciesName = inc.ReadString(); string seed = inc.ReadString(); Vector2 position = new Vector2(inc.ReadSingle(), inc.ReadSingle()); bool enabled = inc.ReadBoolean(); DebugConsole.Log("Received spawn data for " + speciesName); Character character = null; if (noInfo) { if (!spawn) { return(null); } character = Create(speciesName, position, seed, null, true); character.ID = id; } else { bool hasOwner = inc.ReadBoolean(); int ownerId = hasOwner ? inc.ReadByte() : -1; byte teamID = inc.ReadByte(); bool hasAi = inc.ReadBoolean(); string infoSpeciesName = inc.ReadString(); if (!spawn) { return(null); } CharacterInfo info = CharacterInfo.ClientRead(infoSpeciesName, inc); character = Create(infoSpeciesName, position, seed, info, GameMain.Client.ID != ownerId, hasAi); character.ID = id; character.TeamID = (TeamType)teamID; if (character.IsHuman && character.TeamID != TeamType.FriendlyNPC) { CharacterInfo duplicateCharacterInfo = GameMain.GameSession.CrewManager.GetCharacterInfos().FirstOrDefault(c => c.ID == info.ID); GameMain.GameSession.CrewManager.RemoveCharacterInfo(duplicateCharacterInfo); GameMain.GameSession.CrewManager.AddCharacter(character); } if (GameMain.Client.ID == ownerId) { GameMain.Client.HasSpawned = true; GameMain.Client.Character = character; Controlled = character; GameMain.LightManager.LosEnabled = true; character.memInput.Clear(); character.memState.Clear(); character.memLocalState.Clear(); } } character.Enabled = Controlled == character || enabled; return(character); }
public virtual void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) { switch (type) { 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()) { networkUpdateID = msg.ReadUInt16(); } 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); } 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, 4); switch (eventType) { case 0: if (Inventory == null) { string errorMsg = "Received an inventory update message for an entity with no inventory (" + Name + ", removed: " + Removed + ")"; DebugConsole.ThrowError(errorMsg); GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ClientRead:NoInventory" + ID, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); //read anyway to prevent messing up reading the rest of the message UInt16 lastEventID = msg.ReadUInt16(); byte itemCount = msg.ReadByte(); for (int i = 0; i < itemCount; i++) { msg.ReadUInt16(); } } else { Inventory.ClientRead(type, msg, sendingTime); } break; case 1: byte ownerID = msg.ReadByte(); ResetNetState(); if (ownerID == GameMain.Client.ID) { if (controlled != null) { LastNetworkUpdateID = controlled.LastNetworkUpdateID; } if (!IsDead) { Controlled = this; } IsRemotePlayer = false; GameMain.Client.HasSpawned = true; GameMain.Client.Character = this; GameMain.LightManager.LosEnabled = true; } else { if (controlled == this) { Controlled = null; IsRemotePlayer = ownerID > 0; } } break; case 2: ReadStatus(msg); break; case 3: int skillCount = msg.ReadByte(); for (int i = 0; i < skillCount; i++) { string skillIdentifier = msg.ReadString(); float skillLevel = msg.ReadSingle(); info?.SetSkillLevel(skillIdentifier, skillLevel, WorldPosition + Vector2.UnitY * 150.0f); } break; case 4: int attackLimbIndex = msg.ReadByte(); UInt16 targetEntityID = msg.ReadUInt16(); int targetLimbIndex = msg.ReadByte(); //255 = entity already removed, no need to do anything if (attackLimbIndex == 255) { break; } if (attackLimbIndex >= AnimController.Limbs.Length) { DebugConsole.ThrowError($"Received invalid ExecuteAttack message. Limb index out of bounds ({attackLimbIndex})"); break; } Limb attackLimb = AnimController.Limbs[attackLimbIndex]; Limb targetLimb = null; if (!(FindEntityByID(targetEntityID) is IDamageable targetEntity)) { DebugConsole.ThrowError($"Received invalid ExecuteAttack message. Target entity not found (ID {targetEntityID})"); break; } if (targetEntity is Character targetCharacter) { if (targetLimbIndex >= targetCharacter.AnimController.Limbs.Length) { DebugConsole.ThrowError($"Received invalid ExecuteAttack message. Target limb index out of bounds ({targetLimbIndex})"); break; } targetLimb = targetCharacter.AnimController.Limbs[targetLimbIndex]; } if (attackLimb?.attack != null) { attackLimb.ExecuteAttack(targetEntity, targetLimb, out _); } break; } msg.ReadPadBits(); break; } }
/// <summary> /// Read the events from the message, ignoring ones we've already received. Returns false if reading the events fails. /// </summary> public bool Read(ServerNetObject type, IReadMessage msg, float sendingTime, List <IServerSerializable> entities) { UInt16 unreceivedEntityEventCount = 0; if (type == ServerNetObject.ENTITY_EVENT_INITIAL) { unreceivedEntityEventCount = msg.ReadUInt16(); firstNewID = msg.ReadUInt16(); if (GameSettings.VerboseLogging) { DebugConsole.NewMessage( "received midround syncing msg, unreceived: " + unreceivedEntityEventCount + ", first new ID: " + firstNewID, Microsoft.Xna.Framework.Color.Yellow); } } else if (firstNewID != null) { if (GameSettings.VerboseLogging) { DebugConsole.NewMessage("midround syncing complete, switching to ID " + (UInt16)(firstNewID - 1), Microsoft.Xna.Framework.Color.Yellow); } lastReceivedID = (UInt16)(firstNewID - 1); firstNewID = null; } entities.Clear(); UInt16 firstEventID = msg.ReadUInt16(); int eventCount = msg.ReadByte(); for (int i = 0; i < eventCount; i++) { UInt16 thisEventID = (UInt16)(firstEventID + (UInt16)i); UInt16 entityID = msg.ReadUInt16(); if (entityID == Entity.NullEntityID) { if (GameSettings.VerboseLogging) { DebugConsole.NewMessage("received msg " + thisEventID + " (null entity)", Microsoft.Xna.Framework.Color.Orange); } msg.ReadPadBits(); entities.Add(null); if (thisEventID == (UInt16)(lastReceivedID + 1)) { lastReceivedID++; } continue; } byte msgLength = msg.ReadByte(); IServerSerializable entity = Entity.FindEntityByID(entityID) as IServerSerializable; entities.Add(entity); //skip the event if we've already received it or if the entity isn't found if (thisEventID != (UInt16)(lastReceivedID + 1) || entity == null) { if (thisEventID != (UInt16)(lastReceivedID + 1)) { if (GameSettings.VerboseLogging) { DebugConsole.NewMessage( "Received msg " + thisEventID + " (waiting for " + (lastReceivedID + 1) + ")", NetIdUtils.IdMoreRecent(thisEventID, (UInt16)(lastReceivedID + 1)) ? Microsoft.Xna.Framework.Color.Red : Microsoft.Xna.Framework.Color.Yellow); } } else if (entity == null) { DebugConsole.NewMessage( "Received msg " + thisEventID + ", entity " + entityID + " not found", Microsoft.Xna.Framework.Color.Red); GameMain.Client.ReportError(ClientNetError.MISSING_ENTITY, eventID: thisEventID, entityID: entityID); return(false); } msg.BitPosition += msgLength * 8; msg.ReadPadBits(); } else { long msgPosition = msg.BitPosition; if (GameSettings.VerboseLogging) { DebugConsole.NewMessage("received msg " + thisEventID + " (" + entity.ToString() + ")", Microsoft.Xna.Framework.Color.Green); } lastReceivedID++; try { ReadEvent(msg, entity, sendingTime); msg.ReadPadBits(); if (msg.BitPosition != msgPosition + msgLength * 8) { string errorMsg = "Message byte position incorrect after reading an event for the entity \"" + entity.ToString() + "\". Read " + (msg.BitPosition - msgPosition) + " bits, expected message length was " + (msgLength * 8) + " bits."; #if DEBUG DebugConsole.ThrowError(errorMsg); #endif GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:BitPosMismatch", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); //TODO: force the BitPosition to correct place? Having some entity in a potentially incorrect state is not as bad as a desync kick //msg.BitPosition = (int)(msgPosition + msgLength * 8); } } catch (Exception e) { string errorMsg = "Failed to read event for entity \"" + entity.ToString() + "\" (" + e.Message + ")! (MidRoundSyncing: " + thisClient.MidRoundSyncing + ")\n" + e.StackTrace; errorMsg += "\nPrevious entities:"; for (int j = entities.Count - 2; j >= 0; j--) { errorMsg += "\n" + (entities[j] == null ? "NULL" : entities[j].ToString()); } if (GameSettings.VerboseLogging) { DebugConsole.ThrowError("Failed to read event for entity \"" + entity.ToString() + "\"!", e); } GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:ReadFailed" + entity.ToString(), GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); msg.BitPosition = (int)(msgPosition + msgLength * 8); msg.ReadPadBits(); } } } return(true); }
//static because we may need to instantiate the campaign if it hasn't been done yet public static void ClientRead(IReadMessage msg) { byte campaignID = msg.ReadByte(); UInt16 updateID = msg.ReadUInt16(); UInt16 saveID = msg.ReadUInt16(); string mapSeed = msg.ReadString(); UInt16 currentLocIndex = msg.ReadUInt16(); UInt16 selectedLocIndex = msg.ReadUInt16(); byte selectedMissionIndex = msg.ReadByte(); UInt16 startWatchmanID = msg.ReadUInt16(); UInt16 endWatchmanID = msg.ReadUInt16(); int money = msg.ReadInt32(); bool purchasedHullRepairs = msg.ReadBoolean(); bool purchasedItemRepairs = msg.ReadBoolean(); bool purchasedLostShuttles = msg.ReadBoolean(); UInt16 purchasedItemCount = msg.ReadUInt16(); List <PurchasedItem> purchasedItems = new List <PurchasedItem>(); for (int i = 0; i < purchasedItemCount; i++) { string itemPrefabIdentifier = msg.ReadString(); int itemQuantity = msg.ReadRangedInteger(0, CargoManager.MaxQuantity); purchasedItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity)); } bool hasCharacterData = msg.ReadBoolean(); CharacterInfo myCharacterInfo = null; if (hasCharacterData) { myCharacterInfo = CharacterInfo.ClientRead(CharacterPrefab.HumanSpeciesName, msg); } MultiPlayerCampaign campaign = GameMain.GameSession?.GameMode as MultiPlayerCampaign; if (campaign == null || campaignID != campaign.CampaignID) { string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer); GameMain.GameSession = new GameSession(null, savePath, GameModePreset.List.Find(g => g.Identifier == "multiplayercampaign")); campaign = ((MultiPlayerCampaign)GameMain.GameSession.GameMode); campaign.CampaignID = campaignID; campaign.GenerateMap(mapSeed); GameMain.NetLobbyScreen.ToggleCampaignMode(true); } //server has a newer save file if (NetIdUtils.IdMoreRecent(saveID, campaign.PendingSaveID)) { /*//stop any active campaign save transfers, they're outdated now * List<FileReceiver.FileTransferIn> saveTransfers = * GameMain.Client.FileReceiver.ActiveTransfers.FindAll(t => t.FileType == FileTransferType.CampaignSave); * * foreach (var transfer in saveTransfers) * { * GameMain.Client.FileReceiver.StopTransfer(transfer); * } * * GameMain.Client.RequestFile(FileTransferType.CampaignSave, null, null);*/ campaign.PendingSaveID = saveID; } if (NetIdUtils.IdMoreRecent(updateID, campaign.lastUpdateID)) { campaign.SuppressStateSending = true; //we need to have the latest save file to display location/mission/store if (campaign.LastSaveID == saveID) { campaign.Map.SetLocation(currentLocIndex == UInt16.MaxValue ? -1 : currentLocIndex); campaign.Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex); campaign.Map.SelectMission(selectedMissionIndex); campaign.CargoManager.SetPurchasedItems(purchasedItems); } campaign.startWatchmanID = startWatchmanID; campaign.endWatchmanID = endWatchmanID; campaign.Money = money; campaign.PurchasedHullRepairs = purchasedHullRepairs; campaign.PurchasedItemRepairs = purchasedItemRepairs; campaign.PurchasedLostShuttles = purchasedLostShuttles; if (myCharacterInfo != null) { GameMain.Client.CharacterInfo = myCharacterInfo; GameMain.NetLobbyScreen.SetCampaignCharacterInfo(myCharacterInfo); } else { GameMain.NetLobbyScreen.SetCampaignCharacterInfo(null); } campaign.lastUpdateID = updateID; campaign.SuppressStateSending = false; } }
//static because we may need to instantiate the campaign if it hasn't been done yet public static void ClientRead(IReadMessage msg) { bool isFirstRound = msg.ReadBoolean(); byte campaignID = msg.ReadByte(); UInt16 updateID = msg.ReadUInt16(); UInt16 saveID = msg.ReadUInt16(); string mapSeed = msg.ReadString(); UInt16 currentLocIndex = msg.ReadUInt16(); UInt16 selectedLocIndex = msg.ReadUInt16(); byte selectedMissionIndex = msg.ReadByte(); bool allowDebugTeleport = msg.ReadBoolean(); float? reputation = null; if (msg.ReadBoolean()) { reputation = msg.ReadSingle(); } Dictionary <string, float> factionReps = new Dictionary <string, float>(); byte factionsCount = msg.ReadByte(); for (int i = 0; i < factionsCount; i++) { factionReps.Add(msg.ReadString(), msg.ReadSingle()); } bool forceMapUI = msg.ReadBoolean(); int money = msg.ReadInt32(); bool purchasedHullRepairs = msg.ReadBoolean(); bool purchasedItemRepairs = msg.ReadBoolean(); bool purchasedLostShuttles = msg.ReadBoolean(); byte missionCount = msg.ReadByte(); List <Pair <string, byte> > availableMissions = new List <Pair <string, byte> >(); for (int i = 0; i < missionCount; i++) { string missionIdentifier = msg.ReadString(); byte connectionIndex = msg.ReadByte(); availableMissions.Add(new Pair <string, byte>(missionIdentifier, connectionIndex)); } UInt16?storeBalance = null; if (msg.ReadBoolean()) { storeBalance = msg.ReadUInt16(); } UInt16 buyCrateItemCount = msg.ReadUInt16(); List <PurchasedItem> buyCrateItems = new List <PurchasedItem>(); for (int i = 0; i < buyCrateItemCount; i++) { string itemPrefabIdentifier = msg.ReadString(); int itemQuantity = msg.ReadRangedInteger(0, CargoManager.MaxQuantity); buyCrateItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity)); } UInt16 purchasedItemCount = msg.ReadUInt16(); List <PurchasedItem> purchasedItems = new List <PurchasedItem>(); for (int i = 0; i < purchasedItemCount; i++) { string itemPrefabIdentifier = msg.ReadString(); int itemQuantity = msg.ReadRangedInteger(0, CargoManager.MaxQuantity); purchasedItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity)); } UInt16 soldItemCount = msg.ReadUInt16(); List <SoldItem> soldItems = new List <SoldItem>(); for (int i = 0; i < soldItemCount; i++) { string itemPrefabIdentifier = msg.ReadString(); UInt16 id = msg.ReadUInt16(); bool removed = msg.ReadBoolean(); byte sellerId = msg.ReadByte(); soldItems.Add(new SoldItem(ItemPrefab.Prefabs[itemPrefabIdentifier], id, removed, sellerId)); } ushort pendingUpgradeCount = msg.ReadUInt16(); List <PurchasedUpgrade> pendingUpgrades = new List <PurchasedUpgrade>(); for (int i = 0; i < pendingUpgradeCount; i++) { string upgradeIdentifier = msg.ReadString(); UpgradePrefab prefab = UpgradePrefab.Find(upgradeIdentifier); string categoryIdentifier = msg.ReadString(); UpgradeCategory category = UpgradeCategory.Find(categoryIdentifier); int upgradeLevel = msg.ReadByte(); if (prefab == null || category == null) { continue; } pendingUpgrades.Add(new PurchasedUpgrade(prefab, category, upgradeLevel)); } bool hasCharacterData = msg.ReadBoolean(); CharacterInfo myCharacterInfo = null; if (hasCharacterData) { myCharacterInfo = CharacterInfo.ClientRead(CharacterPrefab.HumanSpeciesName, msg); } if (!(GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign) || campaignID != campaign.CampaignID) { string savePath = SaveUtil.CreateSavePath(SaveUtil.SaveType.Multiplayer); GameMain.GameSession = new GameSession(null, savePath, GameModePreset.MultiPlayerCampaign, mapSeed); campaign = (MultiPlayerCampaign)GameMain.GameSession.GameMode; campaign.CampaignID = campaignID; GameMain.NetLobbyScreen.ToggleCampaignMode(true); } //server has a newer save file if (NetIdUtils.IdMoreRecent(saveID, campaign.PendingSaveID)) { campaign.PendingSaveID = saveID; } if (NetIdUtils.IdMoreRecent(updateID, campaign.lastUpdateID)) { campaign.SuppressStateSending = true; campaign.IsFirstRound = isFirstRound; //we need to have the latest save file to display location/mission/store if (campaign.LastSaveID == saveID) { campaign.ForceMapUI = forceMapUI; UpgradeStore.WaitForServerUpdate = false; campaign.Map.SetLocation(currentLocIndex == UInt16.MaxValue ? -1 : currentLocIndex); campaign.Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex); campaign.Map.SelectMission(selectedMissionIndex); campaign.Map.AllowDebugTeleport = allowDebugTeleport; campaign.CargoManager.SetItemsInBuyCrate(buyCrateItems); campaign.CargoManager.SetPurchasedItems(purchasedItems); campaign.CargoManager.SetSoldItems(soldItems); if (storeBalance.HasValue) { campaign.Map.CurrentLocation.StoreCurrentBalance = storeBalance.Value; } campaign.UpgradeManager.SetPendingUpgrades(pendingUpgrades); campaign.UpgradeManager.PurchasedUpgrades.Clear(); foreach (var(identifier, rep) in factionReps) { Faction faction = campaign.Factions.FirstOrDefault(f => f.Prefab.Identifier.Equals(identifier, StringComparison.OrdinalIgnoreCase)); if (faction?.Reputation != null) { faction.Reputation.Value = rep; } else { DebugConsole.ThrowError($"Received an update for a faction that doesn't exist \"{identifier}\"."); } } if (reputation.HasValue) { campaign.Map.CurrentLocation.Reputation.Value = reputation.Value; campaign?.CampaignUI?.UpgradeStore?.RefreshAll(); } foreach (var availableMission in availableMissions) { MissionPrefab missionPrefab = MissionPrefab.List.Find(mp => mp.Identifier == availableMission.First); if (missionPrefab == null) { DebugConsole.ThrowError($"Error when receiving campaign data from the server: mission prefab \"{availableMission.First}\" not found."); continue; } if (availableMission.Second < 0 || availableMission.Second >= campaign.Map.CurrentLocation.Connections.Count) { DebugConsole.ThrowError($"Error when receiving campaign data from the server: connection index for mission \"{availableMission.First}\" out of range (index: {availableMission.Second}, current location: {campaign.Map.CurrentLocation.Name}, connections: {campaign.Map.CurrentLocation.Connections.Count})."); continue; } LocationConnection connection = campaign.Map.CurrentLocation.Connections[availableMission.Second]; campaign.Map.CurrentLocation.UnlockMission(missionPrefab, connection); } GameMain.NetLobbyScreen.ToggleCampaignMode(true); } bool shouldRefresh = campaign.Money != money || campaign.PurchasedHullRepairs != purchasedHullRepairs || campaign.PurchasedItemRepairs != purchasedItemRepairs || campaign.PurchasedLostShuttles != purchasedLostShuttles; campaign.Money = money; campaign.PurchasedHullRepairs = purchasedHullRepairs; campaign.PurchasedItemRepairs = purchasedItemRepairs; campaign.PurchasedLostShuttles = purchasedLostShuttles; if (shouldRefresh) { campaign?.CampaignUI?.UpgradeStore?.RefreshAll(); } if (myCharacterInfo != null) { GameMain.Client.CharacterInfo = myCharacterInfo; GameMain.NetLobbyScreen.SetCampaignCharacterInfo(myCharacterInfo); } else { GameMain.NetLobbyScreen.SetCampaignCharacterInfo(null); } campaign.lastUpdateID = updateID; campaign.SuppressStateSending = false; } }
public virtual void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) { switch (type) { 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()) { networkUpdateID = msg.ReadUInt16(); } 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 as EnemyAIController)?.PetBehavior?.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, 9); 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); GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ClientRead:NoInventory" + ID, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); //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 byte ownerID = msg.ReadByte(); ResetNetState(); if (ownerID == GameMain.Client.ID) { 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++) { string skillIdentifier = msg.ReadString(); float skillLevel = msg.ReadSingle(); info?.SetSkillLevel(skillIdentifier, skillLevel, Position + Vector2.UnitY * 150.0f); } 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 SetAttack/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; if (!(FindEntityByID(targetEntityID) is IDamageable targetEntity)) { DebugConsole.ThrowError($"Received invalid SetAttack/ExecuteAttack message. Target entity not found (ID {targetEntityID})"); break; } if (targetEntity is Character targetCharacter) { if (targetLimbIndex >= targetCharacter.AnimController.Limbs.Length) { DebugConsole.ThrowError($"Received invalid SetAttack/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) { if (eventType == 4) { SetAttackTarget(attackLimb, targetEntity, targetSimPos); } 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) { int orderIndex = msg.ReadRangedInteger(0, Order.PrefabList.Count); var orderPrefab = Order.PrefabList[orderIndex]; string option = null; 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) { string identifier = msg.ReadString(); string option = msg.ReadString(); 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 <Items.Components.WifiComponent>(); if (wifiComponent != null) { wifiComponent.TeamID = teamID; } } break; } msg.ReadPadBits(); break; } }
public static void ClientRead(IReadMessage inc) { ReadyCheckState state = (ReadyCheckState)inc.ReadByte(); CrewManager? crewManager = GameMain.GameSession?.CrewManager; List <Client> otherClients = GameMain.Client.ConnectedClients; if (crewManager == null || otherClients == null) { if (state == ReadyCheckState.Start) { SendState(ReadyStatus.No); } return; } switch (state) { case ReadyCheckState.Start: bool isOwn = false; byte authorId = 0; float duration = inc.ReadSingle(); string author = inc.ReadString(); bool hasAuthor = inc.ReadBoolean(); if (hasAuthor) { authorId = inc.ReadByte(); isOwn = authorId == GameMain.Client.ID; } ushort clientCount = inc.ReadUInt16(); List <byte> clients = new List <byte>(); for (int i = 0; i < clientCount; i++) { clients.Add(inc.ReadByte()); } ReadyCheck rCheck = new ReadyCheck(clients, duration); crewManager.ActiveReadyCheck = rCheck; if (isOwn) { SendState(ReadyStatus.Yes); rCheck.CreateResultsMessage(); } else { rCheck.CreateMessageBox(author); } if (hasAuthor && rCheck.Clients.ContainsKey(authorId)) { rCheck.Clients[authorId] = ReadyStatus.Yes; } break; case ReadyCheckState.Update: float time = inc.ReadSingle(); ReadyStatus newState = (ReadyStatus)inc.ReadByte(); byte targetId = inc.ReadByte(); if (crewManager.ActiveReadyCheck != null) { crewManager.ActiveReadyCheck.time = time; crewManager.ActiveReadyCheck?.UpdateState(targetId, newState); } break; case ReadyCheckState.End: ushort count = inc.ReadUInt16(); for (int i = 0; i < count; i++) { byte id = inc.ReadByte(); ReadyStatus status = (ReadyStatus)inc.ReadByte(); crewManager.ActiveReadyCheck?.UpdateState(id, status); } crewManager.ActiveReadyCheck?.EndReadyCheck(); crewManager.ActiveReadyCheck?.msgBox?.Close(); crewManager.ActiveReadyCheck = null; break; } }
/// <summary> /// Read the events from the message, ignoring ones we've already received /// </summary> public void Read(IReadMessage msg, Client sender = null) { UInt16 firstEventID = msg.ReadUInt16(); int eventCount = msg.ReadByte(); for (int i = 0; i < eventCount; i++) { UInt16 thisEventID = (UInt16)(firstEventID + (UInt16)i); UInt16 entityID = msg.ReadUInt16(); if (entityID == Entity.NullEntityID) { msg.ReadPadBits(); if (thisEventID == (UInt16)(sender.LastSentEntityEventID + 1)) { sender.LastSentEntityEventID++; } continue; } byte msgLength = msg.ReadByte(); IClientSerializable entity = Entity.FindEntityByID(entityID) as IClientSerializable; //skip the event if we've already received it if (thisEventID != (UInt16)(sender.LastSentEntityEventID + 1)) { if (GameSettings.VerboseLogging) { DebugConsole.NewMessage("Received msg " + thisEventID, Color.Red); } msg.BitPosition += msgLength * 8; } else if (entity == null) { //entity not found -> consider the even read and skip over it //(can happen, for example, when a client uses a medical item repeatedly //and creates an event for it before receiving the event about it being removed) if (GameSettings.VerboseLogging) { DebugConsole.NewMessage( "Received msg " + thisEventID + ", entity " + entityID + " not found", Microsoft.Xna.Framework.Color.Orange); } sender.LastSentEntityEventID++; msg.BitPosition += msgLength * 8; } else { if (GameSettings.VerboseLogging) { DebugConsole.NewMessage("Received msg " + thisEventID, Microsoft.Xna.Framework.Color.Green); } UInt16 characterStateID = msg.ReadUInt16(); ReadWriteMessage buffer = new ReadWriteMessage(); byte[] temp = msg.ReadBytes(msgLength - 2); buffer.Write(temp, 0, msgLength - 2); buffer.BitPosition = 0; BufferEvent(new BufferedEvent(sender, sender.Character, characterStateID, entity, buffer)); sender.LastSentEntityEventID++; } msg.ReadPadBits(); } }
private void ReadConnectionInitializationStep(PendingClient pendingClient, IReadMessage inc) { if (!started) { return; } pendingClient.TimeOut = NetworkConnection.TimeoutThreshold; ConnectionInitialization initializationStep = (ConnectionInitialization)inc.ReadByte(); //DebugConsole.NewMessage(initializationStep+" "+pendingClient.InitializationStep); if (pendingClient.InitializationStep != initializationStep) { return; } pendingClient.UpdateTime = Timing.TotalTime + Timing.Step; switch (initializationStep) { case ConnectionInitialization.SteamTicketAndVersion: string name = Client.SanitizeName(inc.ReadString()); UInt64 steamId = inc.ReadUInt64(); UInt16 ticketLength = inc.ReadUInt16(); inc.BitPosition += ticketLength * 8; //skip ticket, owner handles steam authentication if (!Client.IsValidName(name, serverSettings)) { RemovePendingClient(pendingClient, DisconnectReason.InvalidName, "The name \"" + name + "\" is invalid"); return; } string version = inc.ReadString(); bool isCompatibleVersion = NetworkMember.IsCompatible(version, GameMain.Version.ToString()) ?? false; if (!isCompatibleVersion) { RemovePendingClient(pendingClient, DisconnectReason.InvalidVersion, $"DisconnectMessage.InvalidVersion~[version]={GameMain.Version.ToString()}~[clientversion]={version}"); GameServer.Log(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (incompatible game version)", ServerLog.MessageType.Error); DebugConsole.NewMessage(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (incompatible game version)", Microsoft.Xna.Framework.Color.Red); return; } int contentPackageCount = (int)inc.ReadVariableUInt32(); List <ClientContentPackage> clientContentPackages = new List <ClientContentPackage>(); for (int i = 0; i < contentPackageCount; i++) { string packageName = inc.ReadString(); string packageHash = inc.ReadString(); clientContentPackages.Add(new ClientContentPackage(packageName, packageHash)); } //check if the client is missing any of our packages List <ContentPackage> missingPackages = new List <ContentPackage>(); foreach (ContentPackage serverContentPackage in GameMain.SelectedPackages) { if (!serverContentPackage.HasMultiplayerIncompatibleContent) { continue; } bool packageFound = clientContentPackages.Any(cp => cp.Name == serverContentPackage.Name && cp.Hash == serverContentPackage.MD5hash.Hash); if (!packageFound) { missingPackages.Add(serverContentPackage); } } //check if the client is using packages we don't have List <ClientContentPackage> redundantPackages = new List <ClientContentPackage>(); foreach (ClientContentPackage clientContentPackage in clientContentPackages) { bool packageFound = GameMain.SelectedPackages.Any(cp => cp.Name == clientContentPackage.Name && cp.MD5hash.Hash == clientContentPackage.Hash); if (!packageFound) { redundantPackages.Add(clientContentPackage); } } if (missingPackages.Count == 1) { RemovePendingClient(pendingClient, DisconnectReason.MissingContentPackage, $"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(missingPackages[0])}"); GameServer.Log(name + " (" + pendingClient.SteamID + ") couldn't join the server (missing content package " + GetPackageStr(missingPackages[0]) + ")", ServerLog.MessageType.Error); return; } else if (missingPackages.Count > 1) { List <string> packageStrs = new List <string>(); missingPackages.ForEach(cp => packageStrs.Add(GetPackageStr(cp))); RemovePendingClient(pendingClient, DisconnectReason.MissingContentPackage, $"DisconnectMessage.MissingContentPackages~[missingcontentpackages]={string.Join(", ", packageStrs)}"); GameServer.Log(name + " (" + pendingClient.SteamID + ") couldn't join the server (missing content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error); return; } if (redundantPackages.Count == 1) { RemovePendingClient(pendingClient, DisconnectReason.IncompatibleContentPackage, $"DisconnectMessage.IncompatibleContentPackage~[incompatiblecontentpackage]={GetPackageStr(redundantPackages[0])}"); GameServer.Log(name + " (" + pendingClient.SteamID + ") couldn't join the server (using an incompatible content package " + GetPackageStr(redundantPackages[0]) + ")", ServerLog.MessageType.Error); return; } if (redundantPackages.Count > 1) { List <string> packageStrs = new List <string>(); redundantPackages.ForEach(cp => packageStrs.Add(GetPackageStr(cp))); RemovePendingClient(pendingClient, DisconnectReason.IncompatibleContentPackage, $"DisconnectMessage.IncompatibleContentPackages~[incompatiblecontentpackages]={string.Join(", ", packageStrs)}"); GameServer.Log(name + " (" + pendingClient.SteamID + ") couldn't join the server (using incompatible content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error); return; } if (!pendingClient.AuthSessionStarted) { pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password : ConnectionInitialization.ContentPackageOrder; pendingClient.Name = name; pendingClient.AuthSessionStarted = true; } break; case ConnectionInitialization.Password: int pwLength = inc.ReadByte(); byte[] incPassword = inc.ReadBytes(pwLength); if (pendingClient.PasswordSalt == null) { DebugConsole.ThrowError("Received password message from client without salt"); return; } if (serverSettings.IsPasswordCorrect(incPassword, pendingClient.PasswordSalt.Value)) { pendingClient.InitializationStep = ConnectionInitialization.ContentPackageOrder; } else { pendingClient.Retries++; if (serverSettings.BanAfterWrongPassword && pendingClient.Retries > serverSettings.MaxPasswordRetriesBeforeBan) { string banMsg = "Failed to enter correct password too many times"; serverSettings.BanList.BanPlayer(pendingClient.Name, pendingClient.SteamID, banMsg, null); RemovePendingClient(pendingClient, DisconnectReason.Banned, banMsg); return; } } pendingClient.UpdateTime = Timing.TotalTime; break; case ConnectionInitialization.ContentPackageOrder: pendingClient.InitializationStep = ConnectionInitialization.Success; pendingClient.UpdateTime = Timing.TotalTime; break; } }
public static void ServerRead(IReadMessage msg, Client c) { c.KickAFKTimer = 0.0f; UInt16 ID = msg.ReadUInt16(); ChatMessageType type = (ChatMessageType)msg.ReadByte(); string txt = ""; int orderIndex = -1; Character orderTargetCharacter = null; Entity orderTargetEntity = null; int orderOptionIndex = -1; OrderChatMessage orderMsg = null; if (type == ChatMessageType.Order) { orderIndex = msg.ReadByte(); orderTargetCharacter = Entity.FindEntityByID(msg.ReadUInt16()) as Character; orderTargetEntity = Entity.FindEntityByID(msg.ReadUInt16()) as Entity; orderOptionIndex = msg.ReadByte(); if (orderIndex < 0 || orderIndex >= Order.PrefabList.Count) { DebugConsole.ThrowError($"Invalid order message from client \"{c.Name}\" - order index out of bounds ({orderIndex}, {orderOptionIndex})."); if (NetIdUtils.IdMoreRecent(ID, c.LastSentChatMsgID)) { c.LastSentChatMsgID = ID; } return; } Order order = Order.PrefabList[orderIndex]; string orderOption = orderOptionIndex < 0 || orderOptionIndex >= order.Options.Length ? "" : order.Options[orderOptionIndex]; orderMsg = new OrderChatMessage(order, orderOption, orderTargetEntity, orderTargetCharacter, c.Character); txt = orderMsg.Text; } else { txt = msg.ReadString() ?? ""; } if (!NetIdUtils.IdMoreRecent(ID, c.LastSentChatMsgID)) { return; } c.LastSentChatMsgID = ID; if (txt.Length > MaxLength) { txt = txt.Substring(0, MaxLength); } c.LastSentChatMessages.Add(txt); if (c.LastSentChatMessages.Count > 10) { c.LastSentChatMessages.RemoveRange(0, c.LastSentChatMessages.Count - 10); } float similarity = 0.0f; for (int i = 0; i < c.LastSentChatMessages.Count; i++) { float closeFactor = 1.0f / (c.LastSentChatMessages.Count - i); if (string.IsNullOrEmpty(txt)) { similarity += closeFactor; } else { int levenshteinDist = ToolBox.LevenshteinDistance(txt, c.LastSentChatMessages[i]); similarity += Math.Max((txt.Length - levenshteinDist) / (float)txt.Length * closeFactor, 0.0f); } } //order/report messages can be sent a little faster than normal messages without triggering the spam filter if (orderMsg != null) { similarity *= 0.25f; } bool isOwner = GameMain.Server.OwnerConnection != null && c.Connection == GameMain.Server.OwnerConnection; if (similarity + c.ChatSpamSpeed > 5.0f && !isOwner) { GameMain.Server.KarmaManager.OnSpamFilterTriggered(c); c.ChatSpamCount++; if (c.ChatSpamCount > 3) { //kick for spamming too much GameMain.Server.KickClient(c, TextManager.Get("SpamFilterKicked")); } else { ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked"), ChatMessageType.Server, null); c.ChatSpamTimer = 10.0f; GameMain.Server.SendDirectChatMessage(denyMsg, c); } return; } c.ChatSpamSpeed += similarity + 0.5f; if (c.ChatSpamTimer > 0.0f && !isOwner) { ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked"), ChatMessageType.Server, null); c.ChatSpamTimer = 10.0f; GameMain.Server.SendDirectChatMessage(denyMsg, c); return; } if (type == ChatMessageType.Order) { if (c.Character == null || c.Character.SpeechImpediment >= 100.0f || c.Character.IsDead) { return; } ChatMessageType messageType = CanUseRadio(orderMsg.Sender) ? ChatMessageType.Radio : ChatMessageType.Default; if (orderMsg.Order.TargetAllCharacters) { //do nothing } else if (orderTargetCharacter != null) { orderTargetCharacter.SetOrder( new Order(orderMsg.Order.Prefab, orderTargetEntity, (orderTargetEntity as Item)?.Components.FirstOrDefault(ic => ic.GetType() == orderMsg.Order.ItemComponentType)), orderMsg.OrderOption, orderMsg.Sender); } GameMain.Server.SendOrderChatMessage(orderMsg); } else { GameMain.Server.SendChatMessage(txt, null, c); } }
public void ServerRead(ClientNetObject type, IReadMessage msg, Client c) { List <Item> prevItems = new List <Item>(AllItems.Distinct()); byte slotCount = msg.ReadByte(); List <ushort>[] newItemIDs = new List <ushort> [slotCount]; for (int i = 0; i < slotCount; i++) { newItemIDs[i] = new List <ushort>(); int itemCount = msg.ReadRangedInteger(0, MaxStackSize); for (int j = 0; j < itemCount; j++) { newItemIDs[i].Add(msg.ReadUInt16()); } } if (c == null || c.Character == null) { return; } bool accessible = c.Character.CanAccessInventory(this); if (this is CharacterInventory characterInventory && accessible) { if (Owner == null || !(Owner is Character ownerCharacter)) { accessible = false; } else if (!characterInventory.AccessibleWhenAlive && !ownerCharacter.IsDead) { accessible = false; } } if (!accessible) { //create a network event to correct the client's inventory state //otherwise they may have an item in their inventory they shouldn't have been able to pick up, //and receiving an event for that inventory later will cause the item to be dropped CreateNetworkEvent(); for (int i = 0; i < capacity; i++) { foreach (ushort id in newItemIDs[i]) { if (!(Entity.FindEntityByID(id) is Item item)) { continue; } item.PositionUpdateInterval = 0.0f; if (item.ParentInventory != null && item.ParentInventory != this) { item.ParentInventory.CreateNetworkEvent(); } } } return; } List <Inventory> prevItemInventories = new List <Inventory>() { this }; for (int i = 0; i < capacity; i++) { foreach (Item item in slots[i].Items.ToList()) { if (!newItemIDs[i].Contains(item.ID)) { Item droppedItem = item; Entity prevOwner = Owner; Inventory previousInventory = droppedItem.ParentInventory; droppedItem.Drop(null); droppedItem.PreviousParentInventory = previousInventory; var previousCharacterInventory = prevOwner switch { Item itemInventory => itemInventory.FindParentInventory(inventory => inventory is CharacterInventory) as CharacterInventory, Character character => character.Inventory, _ => null }; if (previousCharacterInventory != null && previousCharacterInventory != c.Character?.Inventory) { GameMain.Server?.KarmaManager.OnItemTakenFromPlayer(previousCharacterInventory, c, droppedItem); } if (droppedItem.body != null && prevOwner != null) { droppedItem.body.SetTransform(prevOwner.SimPosition, 0.0f); } } } foreach (ushort id in newItemIDs[i]) { Item newItem = id == 0 ? null : Entity.FindEntityByID(id) as Item; prevItemInventories.Add(newItem?.ParentInventory); } } for (int i = 0; i < capacity; i++) { foreach (ushort id in newItemIDs[i]) { if (!(Entity.FindEntityByID(id) is Item item) || slots[i].Contains(item)) { continue; } if (GameMain.Server != null) { var holdable = item.GetComponent <Holdable>(); if (holdable != null && !holdable.CanBeDeattached()) { continue; } if (!prevItems.Contains(item) && !item.CanClientAccess(c) && (c.Character == null || item.PreviousParentInventory == null || !c.Character.CanAccessInventory(item.PreviousParentInventory))) { #if DEBUG || UNSTABLE DebugConsole.NewMessage($"Client {c.Name} failed to pick up item \"{item}\" (parent inventory: {(item.ParentInventory?.Owner.ToString() ?? "null")}). No access.", Color.Yellow); #endif if (item.body != null && !c.PendingPositionUpdates.Contains(item)) { c.PendingPositionUpdates.Enqueue(item); } item.PositionUpdateInterval = 0.0f; continue; } } TryPutItem(item, i, true, true, c.Character, false); for (int j = 0; j < capacity; j++) { if (slots[j].Contains(item) && !newItemIDs[j].Contains(item.ID)) { slots[j].RemoveItem(item); } } } } CreateNetworkEvent(); foreach (Inventory prevInventory in prevItemInventories.Distinct()) { if (prevInventory != this) { prevInventory?.CreateNetworkEvent(); } } foreach (Item item in AllItems.Distinct()) { if (item == null) { continue; } if (!prevItems.Contains(item)) { if (Owner == c.Character) { HumanAIController.ItemTaken(item, c.Character); GameServer.Log(GameServer.CharacterLogName(c.Character) + " picked up " + item.Name, ServerLog.MessageType.Inventory); } else { GameServer.Log(GameServer.CharacterLogName(c.Character) + " placed " + item.Name + " in " + Owner, ServerLog.MessageType.Inventory); } } } foreach (Item item in prevItems.Distinct()) { if (item == null) { continue; } if (!AllItems.Contains(item)) { if (Owner == c.Character) { GameServer.Log(GameServer.CharacterLogName(c.Character) + " dropped " + item.Name, ServerLog.MessageType.Inventory); } else { GameServer.Log(GameServer.CharacterLogName(c.Character) + " removed " + item.Name + " from " + Owner, ServerLog.MessageType.Inventory); } } } }
private void HandleDataMessage(IReadMessage inc) { if (!isActive) { return; } UInt64 recipientSteamId = inc.ReadUInt64(); DeliveryMethod deliveryMethod = (DeliveryMethod)inc.ReadByte(); int p2pDataStart = inc.BytePosition; byte incByte = inc.ReadByte(); bool isCompressed = (incByte & (byte)PacketHeader.IsCompressed) != 0; bool isConnectionInitializationStep = (incByte & (byte)PacketHeader.IsConnectionInitializationStep) != 0; bool isDisconnectMessage = (incByte & (byte)PacketHeader.IsDisconnectMessage) != 0; bool isServerMessage = (incByte & (byte)PacketHeader.IsServerMessage) != 0; bool isHeartbeatMessage = (incByte & (byte)PacketHeader.IsHeartbeatMessage) != 0; if (recipientSteamId != selfSteamID) { if (!isServerMessage) { DebugConsole.ThrowError("Received non-server message meant for remote peer"); return; } RemotePeer peer = remotePeers.Find(p => p.SteamID == recipientSteamId); if (peer == null) { return; } if (isDisconnectMessage) { DisconnectPeer(peer, inc.ReadString()); return; } Steamworks.P2PSend sendType; switch (deliveryMethod) { case DeliveryMethod.Reliable: case DeliveryMethod.ReliableOrdered: //the documentation seems to suggest that the Reliable send type //enforces packet order (TODO: verify) sendType = Steamworks.P2PSend.Reliable; break; default: sendType = Steamworks.P2PSend.Unreliable; break; } byte[] p2pData; if (isConnectionInitializationStep) { p2pData = new byte[inc.LengthBytes - p2pDataStart + 8]; p2pData[0] = inc.Buffer[p2pDataStart]; Lidgren.Network.NetBitWriter.WriteUInt64(SteamManager.CurrentLobbyID, 64, p2pData, 8); Array.Copy(inc.Buffer, p2pDataStart + 1, p2pData, 9, inc.LengthBytes - p2pDataStart - 1); } else { p2pData = new byte[inc.LengthBytes - p2pDataStart]; Array.Copy(inc.Buffer, p2pDataStart, p2pData, 0, p2pData.Length); } if (p2pData.Length + 4 >= MsgConstants.MTU) { DebugConsole.Log("WARNING: message length comes close to exceeding MTU, forcing reliable send (" + p2pData.Length.ToString() + " bytes)"); sendType = Steamworks.P2PSend.Reliable; } bool successSend = Steamworks.SteamNetworking.SendP2PPacket(recipientSteamId, p2pData, p2pData.Length, 0, sendType); sentBytes += p2pData.Length; if (!successSend) { if (sendType != Steamworks.P2PSend.Reliable) { DebugConsole.Log("WARNING: message couldn't be sent unreliably, forcing reliable send (" + p2pData.Length.ToString() + " bytes)"); sendType = Steamworks.P2PSend.Reliable; successSend = Steamworks.SteamNetworking.SendP2PPacket(recipientSteamId, p2pData, p2pData.Length, 0, sendType); sentBytes += p2pData.Length; } if (!successSend) { DebugConsole.ThrowError("Failed to send message to remote peer! (" + p2pData.Length.ToString() + " bytes)"); } } } else { if (isDisconnectMessage) { DebugConsole.ThrowError("Received disconnect message from owned server"); return; } if (!isServerMessage) { DebugConsole.ThrowError("Received non-server message from owned server"); return; } if (isHeartbeatMessage) { return; //timeout is handled by Lidgren, ignore this message } if (isConnectionInitializationStep) { IWriteMessage outMsg = new WriteOnlyMessage(); outMsg.Write(selfSteamID); outMsg.Write((byte)(PacketHeader.IsConnectionInitializationStep)); outMsg.Write(Name); byte[] msgToSend = (byte[])outMsg.Buffer.Clone(); Array.Resize(ref msgToSend, outMsg.LengthBytes); ChildServerRelay.Write(msgToSend); return; } else { if (initializationStep != ConnectionInitialization.Success) { OnInitializationComplete?.Invoke(); initializationStep = ConnectionInitialization.Success; } UInt16 length = inc.ReadUInt16(); IReadMessage msg = new ReadOnlyMessage(inc.Buffer, isCompressed, inc.BytePosition, length, ServerConnection); OnMessageReceived?.Invoke(msg); return; } } }
private void HandleDataMessage(IReadMessage inc) { if (!started) { return; } UInt64 senderSteamId = inc.ReadUInt64(); byte incByte = inc.ReadByte(); bool isCompressed = (incByte & (byte)PacketHeader.IsCompressed) != 0; bool isConnectionInitializationStep = (incByte & (byte)PacketHeader.IsConnectionInitializationStep) != 0; bool isDisconnectMessage = (incByte & (byte)PacketHeader.IsDisconnectMessage) != 0; bool isServerMessage = (incByte & (byte)PacketHeader.IsServerMessage) != 0; bool isHeartbeatMessage = (incByte & (byte)PacketHeader.IsHeartbeatMessage) != 0; if (isServerMessage) { DebugConsole.ThrowError("Got server message from" + senderSteamId.ToString()); return; } if (senderSteamId != OwnerSteamID) //sender is remote, handle disconnects and heartbeats { PendingClient pendingClient = pendingClients.Find(c => c.SteamID == senderSteamId); SteamP2PConnection connectedClient = connectedClients.Find(c => c.SteamID == senderSteamId) as SteamP2PConnection; pendingClient?.Heartbeat(); connectedClient?.Heartbeat(); if (serverSettings.BanList.IsBanned(senderSteamId, out string banReason)) { if (pendingClient != null) { RemovePendingClient(pendingClient, DisconnectReason.Banned, banReason); } else if (connectedClient != null) { Disconnect(connectedClient, DisconnectReason.Banned.ToString() + "/ " + banReason); } return; } else if (isDisconnectMessage) { if (pendingClient != null) { string disconnectMsg = $"ServerMessage.HasDisconnected~[client]={pendingClient.Name}"; RemovePendingClient(pendingClient, DisconnectReason.Unknown, disconnectMsg); } else if (connectedClient != null) { string disconnectMsg = $"ServerMessage.HasDisconnected~[client]={connectedClient.Name}"; Disconnect(connectedClient, disconnectMsg, false); } return; } else if (isHeartbeatMessage) { //message exists solely as a heartbeat, ignore its contents return; } else if (isConnectionInitializationStep) { if (pendingClient != null) { ReadConnectionInitializationStep(pendingClient, new ReadOnlyMessage(inc.Buffer, false, inc.BytePosition, inc.LengthBytes - inc.BytePosition, null)); } else { ConnectionInitialization initializationStep = (ConnectionInitialization)inc.ReadByte(); if (initializationStep == ConnectionInitialization.ConnectionStarted) { pendingClients.Add(new PendingClient(new SteamP2PConnection("PENDING", senderSteamId)) { SteamID = senderSteamId }); } } } else if (connectedClient != null) { UInt16 length = inc.ReadUInt16(); IReadMessage msg = new ReadOnlyMessage(inc.Buffer, isCompressed, inc.BytePosition, length, connectedClient); OnMessageReceived?.Invoke(connectedClient, msg); } } else //sender is owner { if (OwnerConnection != null) { (OwnerConnection as SteamP2PConnection).Heartbeat(); } if (isDisconnectMessage) { DebugConsole.ThrowError("Received disconnect message from owner"); return; } if (isServerMessage) { DebugConsole.ThrowError("Received server message from owner"); return; } if (isConnectionInitializationStep) { if (OwnerConnection == null) { string ownerName = inc.ReadString(); OwnerConnection = new SteamP2PConnection(ownerName, OwnerSteamID) { Language = GameMain.Config.Language }; OnInitializationComplete?.Invoke(OwnerConnection); } return; } if (isHeartbeatMessage) { return; } else { UInt16 length = inc.ReadUInt16(); IReadMessage msg = new ReadOnlyMessage(inc.Buffer, isCompressed, inc.BytePosition, length, OwnerConnection); OnMessageReceived?.Invoke(OwnerConnection, msg); } } }
public void ServerRead(ClientNetObject type, IReadMessage msg, Client c) { NetEntityEvent.Type eventType = (NetEntityEvent.Type)msg.ReadRangedInteger(0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1); c.KickAFKTimer = 0.0f; switch (eventType) { case NetEntityEvent.Type.ComponentState: int componentIndex = msg.ReadRangedInteger(0, components.Count - 1); (components[componentIndex] as IClientSerializable).ServerRead(type, msg, c); break; case NetEntityEvent.Type.InventoryState: int containerIndex = msg.ReadRangedInteger(0, components.Count - 1); (components[containerIndex] as ItemContainer).Inventory.ServerRead(type, msg, c); break; case NetEntityEvent.Type.Treatment: if (c.Character == null || !c.Character.CanInteractWith(this)) { return; } UInt16 characterID = msg.ReadUInt16(); byte limbIndex = msg.ReadByte(); Character targetCharacter = FindEntityByID(characterID) as Character; if (targetCharacter == null) { break; } if (targetCharacter != c.Character && c.Character.SelectedCharacter != targetCharacter) { break; } Limb targetLimb = limbIndex < targetCharacter.AnimController.Limbs.Length ? targetCharacter.AnimController.Limbs[limbIndex] : null; if (ContainedItems == null || ContainedItems.All(i => i == null)) { GameServer.Log(GameServer.CharacterLogName(c.Character) + " used item " + Name, ServerLog.MessageType.ItemInteraction); } else { GameServer.Log( GameServer.CharacterLogName(c.Character) + " used item " + Name + " (contained items: " + string.Join(", ", ContainedItems.Select(i => i.Name)) + ")", ServerLog.MessageType.ItemInteraction); } ApplyTreatment(c.Character, targetCharacter, targetLimb); break; case NetEntityEvent.Type.ChangeProperty: ReadPropertyChange(msg, inGameEditableOnly: GameMain.NetworkMember.IsServer, sender: c); break; case NetEntityEvent.Type.Combine: UInt16 combineTargetID = msg.ReadUInt16(); Item combineTarget = FindEntityByID(combineTargetID) as Item; if (combineTarget == null || !c.Character.CanInteractWith(this) || !c.Character.CanInteractWith(combineTarget)) { return; } Combine(combineTarget, c.Character); break; } }
public void ServerRead(ClientNetObject type, IReadMessage msg, Client c) { List <Wire>[] wires = new List <Wire> [Connections.Count]; //read wire IDs for each connection for (int i = 0; i < Connections.Count; i++) { wires[i] = new List <Wire>(); for (int j = 0; j < Connection.MaxLinked; j++) { ushort wireId = msg.ReadUInt16(); if (!(Entity.FindEntityByID(wireId) is Item wireItem)) { continue; } Wire wireComponent = wireItem.GetComponent <Wire>(); if (wireComponent != null) { wires[i].Add(wireComponent); } } } List <Wire> clientSideDisconnectedWires = new List <Wire>(); ushort disconnectedWireCount = msg.ReadUInt16(); for (int i = 0; i < disconnectedWireCount; i++) { ushort wireId = msg.ReadUInt16(); if (!(Entity.FindEntityByID(wireId) is Item wireItem)) { continue; } Wire wireComponent = wireItem.GetComponent <Wire>(); if (wireComponent == null) { continue; } clientSideDisconnectedWires.Add(wireComponent); } //don't allow rewiring locked panels if (Locked || !GameMain.NetworkMember.ServerSettings.AllowRewiring) { return; } item.CreateServerEvent(this); //check if the character can access this connectionpanel //and all the wires they're trying to connect if (!item.CanClientAccess(c)) { return; } for (int i = 0; i < Connections.Count; i++) { foreach (Wire wire in wires[i]) { //wire not found in any of the connections yet (client is trying to connect a new wire) // -> we need to check if the client has access to it if (!Connections.Any(connection => connection.Wires.Contains(wire)) && !DisconnectedWires.Contains(wire)) { if (!wire.Item.CanClientAccess(c)) { return; } } } } if (!CheckCharacterSuccess(c.Character)) { item.CreateServerEvent(this); c.Character.Inventory?.CreateNetworkEvent(); for (int i = 0; i < 2; i++) { var selectedWire = c.Character.SelectedItems[i]?.GetComponent <Wire>(); if (selectedWire == null) { continue; } selectedWire.CreateNetworkEvent(); var panel1 = selectedWire.Connections[0]?.ConnectionPanel; if (panel1 != null && panel1 != this) { panel1.item.CreateServerEvent(panel1); } var panel2 = selectedWire.Connections[1]?.ConnectionPanel; if (panel2 != null && panel2 != this) { panel2.item.CreateServerEvent(panel2); } CoroutineManager.InvokeAfter(() => { item.CreateServerEvent(this); if (panel1 != null && panel1 != this) { panel1.item.CreateServerEvent(panel1); } if (panel2 != null && panel2 != this) { panel2.item.CreateServerEvent(panel2); } if (!selectedWire.Item.Removed) { selectedWire.CreateNetworkEvent(); } }, 1.0f); } GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnFailure, this, c.Character.ID }); return; } //go through existing wire links for (int i = 0; i < Connections.Count; i++) { int j = -1; foreach (Wire existingWire in Connections[i].Wires) { j++; if (existingWire == null) { continue; } //existing wire not in the list of new wires -> disconnect it if (!wires[i].Contains(existingWire)) { if (existingWire.Locked) { //this should not be possible unless the client is running a modified version of the game GameServer.Log(GameServer.CharacterLogName(c.Character) + " attempted to disconnect a locked wire from " + Connections[i].Item.Name + " (" + Connections[i].Name + ")", ServerLog.MessageType.Error); continue; } existingWire.RemoveConnection(item); if (existingWire.Item.ParentInventory == null) { item.GetComponent <ConnectionPanel>()?.DisconnectedWires.Add(existingWire); } if (!wires.Any(w => w.Contains(existingWire))) { GameMain.Server.KarmaManager.OnWireDisconnected(c.Character, existingWire); } if (existingWire.Connections[0] == null && existingWire.Connections[1] == null) { GameServer.Log(GameServer.CharacterLogName(c.Character) + " disconnected a wire from " + Connections[i].Item.Name + " (" + Connections[i].Name + ")", ServerLog.MessageType.Wiring); if (existingWire.Item.ParentInventory != null) { //in an inventory and not connected to anything -> the wire cannot have any nodes existingWire.ClearConnections(); } else if (!clientSideDisconnectedWires.Contains(existingWire)) { //not in an inventory, not connected to anything, not hanging loose from any panel -> must be dropped existingWire.Item.Drop(c.Character); } } else if (existingWire.Connections[0] != null) { GameServer.Log(GameServer.CharacterLogName(c.Character) + " disconnected a wire from " + Connections[i].Item.Name + " (" + Connections[i].Name + ") to " + existingWire.Connections[0].Item.Name + " (" + existingWire.Connections[0].Name + ")", ServerLog.MessageType.Wiring); //wires that are not in anyone's inventory (i.e. not currently being rewired) //can never be connected to only one connection // -> the client must have dropped the wire from the connection panel /*if (existingWire.Item.ParentInventory == null && !wires.Any(w => w.Contains(existingWire))) * { * //let other clients know the item was also disconnected from the other connection * existingWire.Connections[0].Item.CreateServerEvent(existingWire.Connections[0].Item.GetComponent<ConnectionPanel>()); * existingWire.Item.Drop(c.Character); * }*/ } else if (existingWire.Connections[1] != null) { GameServer.Log(GameServer.CharacterLogName(c.Character) + " disconnected a wire from " + Connections[i].Item.Name + " (" + Connections[i].Name + ") to " + existingWire.Connections[1].Item.Name + " (" + existingWire.Connections[1].Name + ")", ServerLog.MessageType.Wiring); /*if (existingWire.Item.ParentInventory == null && !wires.Any(w => w.Contains(existingWire))) * { * //let other clients know the item was also disconnected from the other connection * existingWire.Connections[1].Item.CreateServerEvent(existingWire.Connections[1].Item.GetComponent<ConnectionPanel>()); * existingWire.Item.Drop(c.Character); * }*/ } Connections[i].SetWire(j, null); } } } foreach (Wire disconnectedWire in DisconnectedWires.ToList()) { if (disconnectedWire.Connections[0] == null && disconnectedWire.Connections[1] == null && !clientSideDisconnectedWires.Contains(disconnectedWire) && disconnectedWire.Item.ParentInventory == null) { disconnectedWire.Item.Drop(c.Character); GameServer.Log(GameServer.CharacterLogName(c.Character) + " dropped " + disconnectedWire.Name, ServerLog.MessageType.Inventory); } } //go through new wires for (int i = 0; i < Connections.Count; i++) { foreach (Wire newWire in wires[i]) { //already connected, no need to do anything if (Connections[i].Wires.Contains(newWire)) { continue; } Connections[i].TryAddLink(newWire); newWire.Connect(Connections[i], true, true); var otherConnection = newWire.OtherConnection(Connections[i]); if (otherConnection == null) { GameServer.Log(GameServer.CharacterLogName(c.Character) + " connected a wire to " + Connections[i].Item.Name + " (" + Connections[i].Name + ")", ServerLog.MessageType.Wiring); } else { GameServer.Log(GameServer.CharacterLogName(c.Character) + " connected a wire from " + Connections[i].Item.Name + " (" + Connections[i].Name + ") to " + (otherConnection == null ? "none" : otherConnection.Item.Name + " (" + (otherConnection.Name) + ")"), ServerLog.MessageType.Wiring); } } } }
public static Character ReadSpawnData(IReadMessage inc) { DebugConsole.Log("Reading character spawn data"); if (GameMain.Client == null) { return(null); } bool noInfo = inc.ReadBoolean(); ushort id = inc.ReadUInt16(); string speciesName = inc.ReadString(); string seed = inc.ReadString(); Vector2 position = new Vector2(inc.ReadSingle(), inc.ReadSingle()); bool enabled = inc.ReadBoolean(); DebugConsole.Log("Received spawn data for " + speciesName); Character character = null; if (noInfo) { character = Create(speciesName, position, seed, null, true); character.ID = id; bool containsStatusData = inc.ReadBoolean(); if (containsStatusData) { character.ReadStatus(inc); } } else { bool hasOwner = inc.ReadBoolean(); int ownerId = hasOwner ? inc.ReadByte() : -1; byte teamID = inc.ReadByte(); bool hasAi = inc.ReadBoolean(); string infoSpeciesName = inc.ReadString(); CharacterInfo info = CharacterInfo.ClientRead(infoSpeciesName, inc); character = Create(speciesName, position, seed, info, GameMain.Client.ID != ownerId, hasAi); character.ID = id; character.TeamID = (TeamType)teamID; // Check if the character has a current order if (inc.ReadBoolean()) { int orderPrefabIndex = inc.ReadByte(); Entity targetEntity = FindEntityByID(inc.ReadUInt16()); Character orderGiver = inc.ReadBoolean() ? FindEntityByID(inc.ReadUInt16()) as Character : null; int orderOptionIndex = inc.ReadByte(); if (orderPrefabIndex >= 0 && orderPrefabIndex < Order.PrefabList.Count) { var orderPrefab = Order.PrefabList[orderPrefabIndex]; if (!orderPrefab.MustSetTarget || (targetEntity != null && (targetEntity as Item).Components.Any(c => c?.GetType() == orderPrefab.ItemComponentType))) { character.SetOrder( new Order(orderPrefab, targetEntity, (targetEntity as Item)?.Components.FirstOrDefault(c => c?.GetType() == orderPrefab.ItemComponentType), orderGiver: orderGiver), orderOptionIndex >= 0 && orderOptionIndex < orderPrefab.Options.Length ? orderPrefab.Options[orderOptionIndex] : null, orderGiver, speak: false); } else { DebugConsole.ThrowError("Could not set order \"" + orderPrefab.Identifier + "\" for character \"" + character.Name + "\" because required target entity was not found."); } } else { DebugConsole.ThrowError("Invalid order prefab index - index (" + orderPrefabIndex + ") out of bounds."); } } bool containsStatusData = inc.ReadBoolean(); if (containsStatusData) { character.ReadStatus(inc); } if (character.IsHuman && character.TeamID != TeamType.FriendlyNPC && !character.IsDead) { CharacterInfo duplicateCharacterInfo = GameMain.GameSession.CrewManager.GetCharacterInfos().FirstOrDefault(c => c.ID == info.ID); GameMain.GameSession.CrewManager.RemoveCharacterInfo(duplicateCharacterInfo); GameMain.GameSession.CrewManager.AddCharacter(character); } if (GameMain.Client.ID == ownerId) { GameMain.Client.HasSpawned = true; GameMain.Client.Character = character; if (!character.IsDead) { Controlled = character; } GameMain.LightManager.LosEnabled = true; character.memInput.Clear(); character.memState.Clear(); character.memLocalState.Clear(); } } character.Enabled = Controlled == character || enabled; return(character); }
public static Character ReadSpawnData(IReadMessage inc) { DebugConsole.Log("Reading character spawn data"); if (GameMain.Client == null) { return(null); } bool noInfo = inc.ReadBoolean(); ushort id = inc.ReadUInt16(); string speciesName = inc.ReadString(); string seed = inc.ReadString(); Vector2 position = new Vector2(inc.ReadSingle(), inc.ReadSingle()); bool enabled = inc.ReadBoolean(); DebugConsole.Log("Received spawn data for " + speciesName); Character character = null; if (noInfo) { character = Create(speciesName, position, seed, characterInfo: null, id: id, isRemotePlayer: false); bool containsStatusData = inc.ReadBoolean(); if (containsStatusData) { character.ReadStatus(inc); } } else { bool hasOwner = inc.ReadBoolean(); int ownerId = hasOwner ? inc.ReadByte() : -1; byte teamID = inc.ReadByte(); bool hasAi = inc.ReadBoolean(); string infoSpeciesName = inc.ReadString(); CharacterInfo info = CharacterInfo.ClientRead(infoSpeciesName, inc); character = Create(speciesName, position, seed, characterInfo: info, id: id, isRemotePlayer: ownerId > 0 && GameMain.Client.ID != ownerId, hasAi: hasAi); character.TeamID = (CharacterTeamType)teamID; character.CampaignInteractionType = (CampaignMode.InteractionType)inc.ReadByte(); if (character.CampaignInteractionType != CampaignMode.InteractionType.None) { (GameMain.GameSession.GameMode as CampaignMode)?.AssignNPCMenuInteraction(character, character.CampaignInteractionType); } // Check if the character has current orders int orderCount = inc.ReadByte(); for (int i = 0; i < orderCount; i++) { int orderPrefabIndex = inc.ReadByte(); Entity targetEntity = FindEntityByID(inc.ReadUInt16()); Character orderGiver = inc.ReadBoolean() ? FindEntityByID(inc.ReadUInt16()) as Character : null; int orderOptionIndex = inc.ReadByte(); int orderPriority = inc.ReadByte(); OrderTarget targetPosition = null; if (inc.ReadBoolean()) { var x = inc.ReadSingle(); var y = inc.ReadSingle(); var hull = FindEntityByID(inc.ReadUInt16()) as Hull; targetPosition = new OrderTarget(new Vector2(x, y), hull, true); } if (orderPrefabIndex >= 0 && orderPrefabIndex < Order.PrefabList.Count) { var orderPrefab = Order.PrefabList[orderPrefabIndex]; var component = orderPrefab.GetTargetItemComponent(targetEntity as Item); if (!orderPrefab.MustSetTarget || (targetEntity != null && component != null) || targetPosition != null) { var order = targetPosition == null ? new Order(orderPrefab, targetEntity, component, orderGiver: orderGiver) : new Order(orderPrefab, targetPosition, orderGiver: orderGiver); character.SetOrder(order, orderOptionIndex >= 0 && orderOptionIndex < orderPrefab.Options.Length ? orderPrefab.Options[orderOptionIndex] : null, orderPriority, orderGiver, speak: false); } else { DebugConsole.ThrowError("Could not set order \"" + orderPrefab.Identifier + "\" for character \"" + character.Name + "\" because required target entity was not found."); } } else { DebugConsole.ThrowError("Invalid order prefab index - index (" + orderPrefabIndex + ") out of bounds."); } } bool containsStatusData = inc.ReadBoolean(); if (containsStatusData) { character.ReadStatus(inc); } if (character.IsHuman && character.TeamID != CharacterTeamType.FriendlyNPC && character.TeamID != CharacterTeamType.None && !character.IsDead) { CharacterInfo duplicateCharacterInfo = GameMain.GameSession.CrewManager.GetCharacterInfos().FirstOrDefault(c => c.ID == info.ID); GameMain.GameSession.CrewManager.RemoveCharacterInfo(duplicateCharacterInfo); GameMain.GameSession.CrewManager.AddCharacter(character); } if (GameMain.Client.ID == ownerId) { GameMain.Client.HasSpawned = true; GameMain.Client.Character = character; if (!character.IsDead) { Controlled = character; } GameMain.LightManager.LosEnabled = true; GameMain.LightManager.LosAlpha = 1f; character.memInput.Clear(); character.memState.Clear(); character.memLocalState.Clear(); } } character.Enabled = Controlled == character || enabled; return(character); }
public static void ServerRead(IReadMessage msg, Client c) { c.KickAFKTimer = 0.0f; UInt16 ID = msg.ReadUInt16(); ChatMessageType type = (ChatMessageType)msg.ReadByte(); string txt; Character orderTargetCharacter = null; Entity orderTargetEntity = null; OrderChatMessage orderMsg = null; OrderTarget orderTargetPosition = null; Order.OrderTargetType orderTargetType = Order.OrderTargetType.Entity; int?wallSectionIndex = null; if (type == ChatMessageType.Order) { int orderIndex = msg.ReadByte(); orderTargetCharacter = Entity.FindEntityByID(msg.ReadUInt16()) as Character; orderTargetEntity = Entity.FindEntityByID(msg.ReadUInt16()) as Entity; Order orderPrefab = null; int? orderOptionIndex = null; string orderOption = null; // The option of a Dismiss order is written differently so we know what order we target // now that the game supports multiple current orders simultaneously if (orderIndex >= 0 && orderIndex < Order.PrefabList.Count) { orderPrefab = Order.PrefabList[orderIndex]; if (orderPrefab.Identifier != "dismissed") { orderOptionIndex = msg.ReadByte(); } // Does the dismiss order have a specified target? else if (msg.ReadBoolean()) { int identifierCount = msg.ReadByte(); if (identifierCount > 0) { int dismissedOrderIndex = msg.ReadByte(); Order dismissedOrderPrefab = null; if (dismissedOrderIndex >= 0 && dismissedOrderIndex < Order.PrefabList.Count) { dismissedOrderPrefab = Order.PrefabList[dismissedOrderIndex]; orderOption = dismissedOrderPrefab.Identifier; } if (identifierCount > 1) { int dismissedOrderOptionIndex = msg.ReadByte(); if (dismissedOrderPrefab != null) { var options = dismissedOrderPrefab.Options; if (options != null && dismissedOrderOptionIndex >= 0 && dismissedOrderOptionIndex < options.Length) { orderOption += $".{options[dismissedOrderOptionIndex]}"; } } } } } } else { orderOptionIndex = msg.ReadByte(); } int orderPriority = msg.ReadByte(); orderTargetType = (Order.OrderTargetType)msg.ReadByte(); if (msg.ReadBoolean()) { var x = msg.ReadSingle(); var y = msg.ReadSingle(); var hull = Entity.FindEntityByID(msg.ReadUInt16()) as Hull; orderTargetPosition = new OrderTarget(new Vector2(x, y), hull, true); } else if (orderTargetType == Order.OrderTargetType.WallSection) { wallSectionIndex = msg.ReadByte(); } if (orderIndex < 0 || orderIndex >= Order.PrefabList.Count) { DebugConsole.ThrowError($"Invalid order message from client \"{c.Name}\" - order index out of bounds ({orderIndex})."); if (NetIdUtils.IdMoreRecent(ID, c.LastSentChatMsgID)) { c.LastSentChatMsgID = ID; } return; } orderPrefab ??= Order.PrefabList[orderIndex]; orderOption ??= orderOptionIndex == null || orderOptionIndex < 0 || orderOptionIndex >= orderPrefab.Options.Length ? "" : orderPrefab.Options[orderOptionIndex.Value]; orderMsg = new OrderChatMessage(orderPrefab, orderOption, orderPriority, orderTargetPosition ?? orderTargetEntity as ISpatialEntity, orderTargetCharacter, c.Character) { WallSectionIndex = wallSectionIndex }; txt = orderMsg.Text; } else { txt = msg.ReadString() ?? ""; } if (!NetIdUtils.IdMoreRecent(ID, c.LastSentChatMsgID)) { return; } c.LastSentChatMsgID = ID; if (txt.Length > MaxLength) { txt = txt.Substring(0, MaxLength); } c.LastSentChatMessages.Add(txt); if (c.LastSentChatMessages.Count > 10) { c.LastSentChatMessages.RemoveRange(0, c.LastSentChatMessages.Count - 10); } float similarity = 0.0f; for (int i = 0; i < c.LastSentChatMessages.Count; i++) { float closeFactor = 1.0f / (c.LastSentChatMessages.Count - i); if (string.IsNullOrEmpty(txt)) { similarity += closeFactor; } else { int levenshteinDist = ToolBox.LevenshteinDistance(txt, c.LastSentChatMessages[i]); similarity += Math.Max((txt.Length - levenshteinDist) / (float)txt.Length * closeFactor, 0.0f); } } //order/report messages can be sent a little faster than normal messages without triggering the spam filter if (orderMsg != null) { similarity *= 0.25f; } bool isOwner = GameMain.Server.OwnerConnection != null && c.Connection == GameMain.Server.OwnerConnection; if (similarity + c.ChatSpamSpeed > 5.0f && !isOwner) { GameMain.Server.KarmaManager.OnSpamFilterTriggered(c); c.ChatSpamCount++; if (c.ChatSpamCount > 3) { //kick for spamming too much GameMain.Server.KickClient(c, TextManager.Get("SpamFilterKicked")); } else { ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked"), ChatMessageType.Server, null); c.ChatSpamTimer = 10.0f; GameMain.Server.SendDirectChatMessage(denyMsg, c); } return; } c.ChatSpamSpeed += similarity + 0.5f; if (c.ChatSpamTimer > 0.0f && !isOwner) { ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked"), ChatMessageType.Server, null); c.ChatSpamTimer = 10.0f; GameMain.Server.SendDirectChatMessage(denyMsg, c); return; } if (type == ChatMessageType.Order) { if (c.Character == null || c.Character.SpeechImpediment >= 100.0f || c.Character.IsDead) { return; } Order order = null; if (orderMsg.Order.IsReport) { HumanAIController.ReportProblem(orderMsg.Sender, orderMsg.Order); } else if (orderTargetCharacter != null && !orderMsg.Order.TargetAllCharacters) { switch (orderTargetType) { case Order.OrderTargetType.Entity: order = new Order(orderMsg.Order.Prefab, orderTargetEntity, orderMsg.Order.Prefab?.GetTargetItemComponent(orderTargetEntity as Item), orderGiver: orderMsg.Sender); break; case Order.OrderTargetType.Position: order = new Order(orderMsg.Order.Prefab, orderTargetPosition, orderGiver: orderMsg.Sender); break; } if (order != null) { orderTargetCharacter.SetOrder(order, orderMsg.OrderOption, orderMsg.OrderPriority, orderMsg.Sender); } } else if (orderMsg.Order.IsIgnoreOrder) { switch (orderTargetType) { case Order.OrderTargetType.Entity: if (orderTargetEntity is IIgnorable ignorableEntity) { ignorableEntity.OrderedToBeIgnored = orderMsg.Order.Identifier == "ignorethis"; } break; case Order.OrderTargetType.WallSection: if (!wallSectionIndex.HasValue) { break; } if (orderTargetEntity is Structure s && s.GetSection(wallSectionIndex.Value) is IIgnorable ignorableWall) { ignorableWall.OrderedToBeIgnored = orderMsg.Order.Identifier == "ignorethis"; } break; } } GameMain.Server.SendOrderChatMessage(orderMsg); } else { GameMain.Server.SendChatMessage(txt, null, c); } }
public void ServerReadCrew(IReadMessage msg, Client sender) { int[] pendingHires = null; bool updatePending = msg.ReadBoolean(); if (updatePending) { ushort pendingHireLength = msg.ReadUInt16(); pendingHires = new int[pendingHireLength]; for (int i = 0; i < pendingHireLength; i++) { pendingHires[i] = msg.ReadInt32(); } } bool validateHires = msg.ReadBoolean(); bool fireCharacter = msg.ReadBoolean(); int firedIdentifier = -1; if (fireCharacter) { firedIdentifier = msg.ReadInt32(); } Location location = map?.CurrentLocation; CharacterInfo firedCharacter = null; if (location != null && AllowedToManageCampaign(sender)) { if (fireCharacter) { firedCharacter = CrewManager.CharacterInfos.FirstOrDefault(info => info.GetIdentifier() == firedIdentifier); if (firedCharacter != null && (firedCharacter.Character?.IsBot ?? true)) { CrewManager.FireCharacter(firedCharacter); } else { DebugConsole.ThrowError($"Tried to fire an invalid character ({firedIdentifier})"); } } if (location.HireManager != null) { if (validateHires) { foreach (CharacterInfo hireInfo in location.HireManager.PendingHires) { TryHireCharacter(location, hireInfo); } } if (updatePending) { List <CharacterInfo> pendingHireInfos = new List <CharacterInfo>(); foreach (int identifier in pendingHires) { CharacterInfo match = location.GetHireableCharacters().FirstOrDefault(info => info.GetIdentifier() == identifier); if (match == null) { DebugConsole.ThrowError($"Tried to hire a character that doesn't exist ({identifier})"); continue; } pendingHireInfos.Add(match); } location.HireManager.PendingHires = pendingHireInfos; } } } // bounce back SendCrewState(validateHires, firedCharacter); }
private void ApplyRemoteState(IReadMessage msg) { List <Wire> prevWires = Connections.SelectMany(c => c.Wires.Where(w => w != null)).ToList(); List <Wire> newWires = new List <Wire>(); ushort userID = msg.ReadUInt16(); if (userID == 0) { user = null; } else { user = Entity.FindEntityByID(userID) as Character; base.IsActive = true; } foreach (Connection connection in Connections) { connection.ClearConnections(); } foreach (Connection connection in Connections) { for (int i = 0; i < connection.MaxWires; i++) { ushort wireId = msg.ReadUInt16(); if (!(Entity.FindEntityByID(wireId) is Item wireItem)) { continue; } Wire wireComponent = wireItem.GetComponent <Wire>(); if (wireComponent == null) { continue; } newWires.Add(wireComponent); connection.SetWire(i, wireComponent); wireComponent.Connect(connection, false); } } List <Wire> previousDisconnectedWires = new List <Wire>(DisconnectedWires); DisconnectedWires.Clear(); ushort disconnectedWireCount = msg.ReadUInt16(); for (int i = 0; i < disconnectedWireCount; i++) { ushort wireId = msg.ReadUInt16(); if (!(Entity.FindEntityByID(wireId) is Item wireItem)) { continue; } Wire wireComponent = wireItem.GetComponent <Wire>(); if (wireComponent == null) { continue; } DisconnectedWires.Add(wireComponent); base.IsActive = true; } foreach (Wire wire in prevWires) { bool connected = wire.Connections[0] != null || wire.Connections[1] != null; if (!connected) { foreach (Item item in Item.ItemList) { var connectionPanel = item.GetComponent <ConnectionPanel>(); if (connectionPanel != null && connectionPanel.DisconnectedWires.Contains(wire)) { connected = true; break; } } } if (wire.Item.ParentInventory == null && !connected) { wire.Item.Drop(null); } } foreach (Wire disconnectedWire in previousDisconnectedWires) { if (disconnectedWire.Connections[0] == null && disconnectedWire.Connections[1] == null && !DisconnectedWires.Contains(disconnectedWire)) { disconnectedWire.Item.Drop(dropper: null); } } }
public void ServerRead(IReadMessage msg, Client sender) { UInt16 currentLocIndex = msg.ReadUInt16(); UInt16 selectedLocIndex = msg.ReadUInt16(); byte selectedMissionCount = msg.ReadByte(); List <int> selectedMissionIndices = new List <int>(); for (int i = 0; i < selectedMissionCount; i++) { selectedMissionIndices.Add(msg.ReadByte()); } bool purchasedHullRepairs = msg.ReadBoolean(); bool purchasedItemRepairs = msg.ReadBoolean(); bool purchasedLostShuttles = msg.ReadBoolean(); UInt16 buyCrateItemCount = msg.ReadUInt16(); List <PurchasedItem> buyCrateItems = new List <PurchasedItem>(); for (int i = 0; i < buyCrateItemCount; i++) { string itemPrefabIdentifier = msg.ReadString(); int itemQuantity = msg.ReadRangedInteger(0, CargoManager.MaxQuantity); buyCrateItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity)); } UInt16 purchasedItemCount = msg.ReadUInt16(); List <PurchasedItem> purchasedItems = new List <PurchasedItem>(); for (int i = 0; i < purchasedItemCount; i++) { string itemPrefabIdentifier = msg.ReadString(); int itemQuantity = msg.ReadRangedInteger(0, CargoManager.MaxQuantity); purchasedItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity)); } UInt16 soldItemCount = msg.ReadUInt16(); List <SoldItem> soldItems = new List <SoldItem>(); for (int i = 0; i < soldItemCount; i++) { string itemPrefabIdentifier = msg.ReadString(); UInt16 id = msg.ReadUInt16(); bool removed = msg.ReadBoolean(); byte sellerId = msg.ReadByte(); soldItems.Add(new SoldItem(ItemPrefab.Prefabs[itemPrefabIdentifier], id, removed, sellerId)); } ushort purchasedUpgradeCount = msg.ReadUInt16(); List <PurchasedUpgrade> purchasedUpgrades = new List <PurchasedUpgrade>(); for (int i = 0; i < purchasedUpgradeCount; i++) { string upgradeIdentifier = msg.ReadString(); UpgradePrefab prefab = UpgradePrefab.Find(upgradeIdentifier); string categoryIdentifier = msg.ReadString(); UpgradeCategory category = UpgradeCategory.Find(categoryIdentifier); int upgradeLevel = msg.ReadByte(); if (category == null || prefab == null) { continue; } purchasedUpgrades.Add(new PurchasedUpgrade(prefab, category, upgradeLevel)); } ushort purchasedItemSwapCount = msg.ReadUInt16(); List <PurchasedItemSwap> purchasedItemSwaps = new List <PurchasedItemSwap>(); for (int i = 0; i < purchasedItemSwapCount; i++) { UInt16 itemToRemoveID = msg.ReadUInt16(); Item itemToRemove = Entity.FindEntityByID(itemToRemoveID) as Item; string itemToInstallIdentifier = msg.ReadString(); ItemPrefab itemToInstall = string.IsNullOrEmpty(itemToInstallIdentifier) ? null : ItemPrefab.Find(string.Empty, itemToInstallIdentifier); if (itemToRemove == null) { continue; } purchasedItemSwaps.Add(new PurchasedItemSwap(itemToRemove, itemToInstall)); } if (!AllowedToManageCampaign(sender)) { DebugConsole.ThrowError("Client \"" + sender.Name + "\" does not have a permission to manage the campaign"); return; } Location location = Map.CurrentLocation; int hullRepairCost = location?.GetAdjustedMechanicalCost(HullRepairCost) ?? HullRepairCost; int itemRepairCost = location?.GetAdjustedMechanicalCost(ItemRepairCost) ?? ItemRepairCost; int shuttleRetrieveCost = location?.GetAdjustedMechanicalCost(ShuttleReplaceCost) ?? ShuttleReplaceCost; if (purchasedHullRepairs != this.PurchasedHullRepairs) { if (purchasedHullRepairs && Money >= hullRepairCost) { this.PurchasedHullRepairs = true; Money -= hullRepairCost; } else if (!purchasedHullRepairs) { this.PurchasedHullRepairs = false; Money += hullRepairCost; } } if (purchasedItemRepairs != this.PurchasedItemRepairs) { if (purchasedItemRepairs && Money >= itemRepairCost) { this.PurchasedItemRepairs = true; Money -= itemRepairCost; } else if (!purchasedItemRepairs) { this.PurchasedItemRepairs = false; Money += itemRepairCost; } } if (purchasedLostShuttles != this.PurchasedLostShuttles) { if (GameMain.GameSession?.SubmarineInfo != null && GameMain.GameSession.SubmarineInfo.LeftBehindSubDockingPortOccupied) { GameMain.Server.SendDirectChatMessage(TextManager.FormatServerMessage("ReplaceShuttleDockingPortOccupied"), sender, ChatMessageType.MessageBox); } else if (purchasedLostShuttles && Money >= shuttleRetrieveCost) { this.PurchasedLostShuttles = true; Money -= shuttleRetrieveCost; } else if (!purchasedItemRepairs) { this.PurchasedLostShuttles = false; Money += shuttleRetrieveCost; } } if (currentLocIndex < Map.Locations.Count && Map.AllowDebugTeleport) { Map.SetLocation(currentLocIndex); } Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex); if (Map.SelectedLocation == null) { Map.SelectRandomLocation(preferUndiscovered: true); } if (Map.SelectedConnection != null) { Map.SelectMission(selectedMissionIndices); } CheckTooManyMissions(Map.CurrentLocation, sender); List <PurchasedItem> currentBuyCrateItems = new List <PurchasedItem>(CargoManager.ItemsInBuyCrate); currentBuyCrateItems.ForEach(i => CargoManager.ModifyItemQuantityInBuyCrate(i.ItemPrefab, -i.Quantity)); buyCrateItems.ForEach(i => CargoManager.ModifyItemQuantityInBuyCrate(i.ItemPrefab, i.Quantity)); CargoManager.SellBackPurchasedItems(new List <PurchasedItem>(CargoManager.PurchasedItems)); CargoManager.PurchaseItems(purchasedItems, false); // for some reason CargoManager.SoldItem is never cleared by the server, I've added a check to SellItems that ignores all // sold items that are removed so they should be discarded on the next message CargoManager.BuyBackSoldItems(new List <SoldItem>(CargoManager.SoldItems)); CargoManager.SellItems(soldItems); foreach (var(prefab, category, _) in purchasedUpgrades) { UpgradeManager.PurchaseUpgrade(prefab, category); // unstable logging int price = prefab.Price.GetBuyprice(UpgradeManager.GetUpgradeLevel(prefab, category), Map?.CurrentLocation); int level = UpgradeManager.GetUpgradeLevel(prefab, category); GameServer.Log($"SERVER: Purchased level {level} {category.Identifier}.{prefab.Identifier} for {price}", ServerLog.MessageType.ServerMessage); } foreach (var purchasedItemSwap in purchasedItemSwaps) { if (purchasedItemSwap.ItemToInstall == null) { UpgradeManager.CancelItemSwap(purchasedItemSwap.ItemToRemove); } else { UpgradeManager.PurchaseItemSwap(purchasedItemSwap.ItemToRemove, purchasedItemSwap.ItemToInstall); } } foreach (Item item in Item.ItemList) { if (item.PendingItemSwap != null && !purchasedItemSwaps.Any(it => it.ItemToRemove == item)) { UpgradeManager.CancelItemSwap(item); item.PendingItemSwap = null; } } }
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime) { bool launch = msg.ReadBoolean(); if (launch) { ushort userId = msg.ReadUInt16(); User = Entity.FindEntityByID(userId) as Character; Vector2 simPosition = new Vector2(msg.ReadSingle(), msg.ReadSingle()); float rotation = msg.ReadSingle(); if (User != null) { Shoot(User, simPosition, simPosition, rotation, ignoredBodies: User.AnimController.Limbs.Where(l => !l.IsSevered).Select(l => l.body.FarseerBody).ToList(), createNetworkEvent: false); } else { Launch(User, simPosition, rotation); } } bool isStuck = msg.ReadBoolean(); if (isStuck) { ushort submarineID = msg.ReadUInt16(); ushort hullID = msg.ReadUInt16(); Vector2 simPosition = new Vector2( msg.ReadSingle(), msg.ReadSingle()); Vector2 axis = new Vector2( msg.ReadSingle(), msg.ReadSingle()); UInt16 entityID = msg.ReadUInt16(); Entity entity = Entity.FindEntityByID(entityID); Submarine submarine = Entity.FindEntityByID(submarineID) as Submarine; Hull hull = Entity.FindEntityByID(hullID) as Hull; item.Submarine = submarine; item.CurrentHull = hull; item.body.SetTransform(simPosition, item.body.Rotation); if (entity is Character character) { byte limbIndex = msg.ReadByte(); if (limbIndex >= character.AnimController.Limbs.Length) { DebugConsole.ThrowError($"Failed to read a projectile update from the server. Limb index out of bounds ({limbIndex}, character: {character.ToString()})"); return; } if (character.Removed) { return; } var limb = character.AnimController.Limbs[limbIndex]; StickToTarget(limb.body.FarseerBody, axis); } else if (entity is Structure structure) { byte bodyIndex = msg.ReadByte(); if (bodyIndex == 255) { bodyIndex = 0; } if (bodyIndex >= structure.Bodies.Count) { DebugConsole.ThrowError($"Failed to read a projectile update from the server. Structure body index out of bounds ({bodyIndex}, structure: {structure.ToString()})"); return; } var body = structure.Bodies[bodyIndex]; StickToTarget(body, axis); } else if (entity is Item item) { if (item.Removed) { return; } var door = item.GetComponent <Door>(); if (door != null) { StickToTarget(door.Body.FarseerBody, axis); } else if (item.body != null) { StickToTarget(item.body.FarseerBody, axis); } } else if (entity is Submarine sub) { StickToTarget(sub.PhysicsBody.FarseerBody, axis); } else { DebugConsole.ThrowError($"Failed to read a projectile update from the server. Invalid stick target ({entity?.ToString() ?? "null"}, {entityID})"); } } else { Unstick(); } }
public void ServerReadCrew(IReadMessage msg, Client sender) { int[] pendingHires = null; bool updatePending = msg.ReadBoolean(); if (updatePending) { ushort pendingHireLength = msg.ReadUInt16(); pendingHires = new int[pendingHireLength]; for (int i = 0; i < pendingHireLength; i++) { pendingHires[i] = msg.ReadInt32(); } } bool validateHires = msg.ReadBoolean(); bool renameCharacter = msg.ReadBoolean(); int renamedIdentifier = -1; string newName = null; bool existingCrewMember = false; if (renameCharacter) { renamedIdentifier = msg.ReadInt32(); newName = msg.ReadString(); existingCrewMember = msg.ReadBoolean(); } bool fireCharacter = msg.ReadBoolean(); int firedIdentifier = -1; if (fireCharacter) { firedIdentifier = msg.ReadInt32(); } Location location = map?.CurrentLocation; List <CharacterInfo> hiredCharacters = new List <CharacterInfo>(); CharacterInfo firedCharacter = null; if (location != null && AllowedToManageCampaign(sender)) { if (fireCharacter) { firedCharacter = CrewManager.CharacterInfos.FirstOrDefault(info => info.GetIdentifier() == firedIdentifier); if (firedCharacter != null && (firedCharacter.Character?.IsBot ?? true)) { CrewManager.FireCharacter(firedCharacter); } else { DebugConsole.ThrowError($"Tried to fire an invalid character ({firedIdentifier})"); } } if (renameCharacter) { CharacterInfo characterInfo = null; if (existingCrewMember && CrewManager != null) { characterInfo = CrewManager.CharacterInfos.FirstOrDefault(info => info.GetIdentifierUsingOriginalName() == renamedIdentifier); } else if (!existingCrewMember && location.HireManager != null) { characterInfo = location.HireManager.AvailableCharacters.FirstOrDefault(info => info.GetIdentifierUsingOriginalName() == renamedIdentifier); } if (characterInfo != null && (characterInfo.Character?.IsBot ?? true)) { if (existingCrewMember) { CrewManager.RenameCharacter(characterInfo, newName); } else { location.HireManager.RenameCharacter(characterInfo, newName); } } else { DebugConsole.ThrowError($"Tried to rename an invalid character ({renamedIdentifier})"); } } if (location.HireManager != null) { if (validateHires) { foreach (CharacterInfo hireInfo in location.HireManager.PendingHires) { if (TryHireCharacter(location, hireInfo)) { hiredCharacters.Add(hireInfo); } ; } } if (updatePending) { List <CharacterInfo> pendingHireInfos = new List <CharacterInfo>(); foreach (int identifier in pendingHires) { CharacterInfo match = location.GetHireableCharacters().FirstOrDefault(info => info.GetIdentifierUsingOriginalName() == identifier); if (match == null) { DebugConsole.ThrowError($"Tried to add a character that doesn't exist ({identifier}) to pending hires"); continue; } pendingHireInfos.Add(match); if (pendingHireInfos.Count + CrewManager.CharacterInfos.Count() >= CrewManager.MaxCrewSize) { break; } } location.HireManager.PendingHires = pendingHireInfos; } location.HireManager.AvailableCharacters.ForEachMod(info => { if (!location.HireManager.PendingHires.Contains(info)) { location.HireManager.RenameCharacter(info, info.OriginalName); } }); } } // bounce back if (renameCharacter && existingCrewMember) { SendCrewState(hiredCharacters, (renamedIdentifier, newName), firedCharacter); } else { SendCrewState(hiredCharacters, default, firedCharacter);
public void ServerRead(IReadMessage msg, Client sender) { UInt16 selectedLocIndex = msg.ReadUInt16(); byte selectedMissionIndex = msg.ReadByte(); bool purchasedHullRepairs = msg.ReadBoolean(); bool purchasedItemRepairs = msg.ReadBoolean(); bool purchasedLostShuttles = msg.ReadBoolean(); UInt16 purchasedItemCount = msg.ReadUInt16(); List <PurchasedItem> purchasedItems = new List <PurchasedItem>(); for (int i = 0; i < purchasedItemCount; i++) { string itemPrefabIdentifier = msg.ReadString(); int itemQuantity = msg.ReadRangedInteger(0, CargoManager.MaxQuantity); purchasedItems.Add(new PurchasedItem(ItemPrefab.Prefabs[itemPrefabIdentifier], itemQuantity)); } if (!sender.HasPermission(ClientPermissions.ManageCampaign)) { DebugConsole.ThrowError("Client \"" + sender.Name + "\" does not have a permission to manage the campaign"); return; } if (purchasedHullRepairs != this.PurchasedHullRepairs) { if (purchasedHullRepairs && Money >= HullRepairCost) { this.PurchasedHullRepairs = true; Money -= HullRepairCost; } else if (!purchasedHullRepairs) { this.PurchasedHullRepairs = false; Money += HullRepairCost; } } if (purchasedItemRepairs != this.PurchasedItemRepairs) { if (purchasedItemRepairs && Money >= ItemRepairCost) { this.PurchasedItemRepairs = true; Money -= ItemRepairCost; } else if (!purchasedItemRepairs) { this.PurchasedItemRepairs = false; Money += ItemRepairCost; } } if (purchasedLostShuttles != this.PurchasedLostShuttles) { if (GameMain.GameSession?.SubmarineInfo != null && GameMain.GameSession.SubmarineInfo.LeftBehindSubDockingPortOccupied) { GameMain.Server.SendDirectChatMessage(TextManager.FormatServerMessage("ReplaceShuttleDockingPortOccupied"), sender, ChatMessageType.MessageBox); } else if (purchasedLostShuttles && Money >= ShuttleReplaceCost) { this.PurchasedLostShuttles = true; Money -= ShuttleReplaceCost; } else if (!purchasedItemRepairs) { this.PurchasedLostShuttles = false; Money += ShuttleReplaceCost; } } Map.SelectLocation(selectedLocIndex == UInt16.MaxValue ? -1 : selectedLocIndex); if (Map.SelectedLocation == null) { Map.SelectRandomLocation(preferUndiscovered: true); } if (Map.SelectedConnection != null) { Map.SelectMission(selectedMissionIndex); } List <PurchasedItem> currentItems = new List <PurchasedItem>(CargoManager.PurchasedItems); foreach (PurchasedItem pi in currentItems) { CargoManager.SellItem(pi, pi.Quantity); } foreach (PurchasedItem pi in purchasedItems) { CargoManager.PurchaseItem(pi.ItemPrefab, pi.Quantity); } }
public virtual void ServerRead(ClientNetObject type, IReadMessage msg, Client c) { if (GameMain.Server == null) { return; } switch (type) { case ClientNetObject.CHARACTER_INPUT: if (c.Character != this) { #if DEBUG DebugConsole.Log("Received a character update message from a client who's not controlling the character"); #endif return; } UInt16 networkUpdateID = msg.ReadUInt16(); byte inputCount = msg.ReadByte(); if (AllowInput) { Enabled = true; } for (int i = 0; i < inputCount; i++) { InputNetFlags newInput = (InputNetFlags)msg.ReadRangedInteger(0, (int)InputNetFlags.MaxVal); UInt16 newAim = 0; UInt16 newInteract = 0; if (newInput != InputNetFlags.None && newInput != InputNetFlags.FacingLeft) { c.KickAFKTimer = 0.0f; } else if (AnimController.Dir < 0.0f != newInput.HasFlag(InputNetFlags.FacingLeft)) { //character changed the direction they're facing c.KickAFKTimer = 0.0f; } newAim = msg.ReadUInt16(); if (newInput.HasFlag(InputNetFlags.Select) || newInput.HasFlag(InputNetFlags.Deselect) || newInput.HasFlag(InputNetFlags.Use) || newInput.HasFlag(InputNetFlags.Health) || newInput.HasFlag(InputNetFlags.Grab)) { newInteract = msg.ReadUInt16(); } if (NetIdUtils.IdMoreRecent((ushort)(networkUpdateID - i), LastNetworkUpdateID) && (i < 60)) { if ((i > 0 && memInput[i - 1].intAim != newAim)) { c.KickAFKTimer = 0.0f; } NetInputMem newMem = new NetInputMem { states = newInput, intAim = newAim, interact = newInteract, networkUpdateID = (ushort)(networkUpdateID - i) }; memInput.Insert(i, newMem); LastInputTime = Timing.TotalTime; } } if (NetIdUtils.IdMoreRecent(networkUpdateID, LastNetworkUpdateID)) { LastNetworkUpdateID = networkUpdateID; } else if (NetIdUtils.Difference(networkUpdateID, LastNetworkUpdateID) > 500) { #if DEBUG || UNSTABLE DebugConsole.AddWarning($"Large disrepancy between a client character's network update ID server-side and client-side (client: {networkUpdateID}, server: {LastNetworkUpdateID}). Resetting the ID."); #endif LastNetworkUpdateID = networkUpdateID; } if (memInput.Count > 60) { //deleting inputs from the queue here means the server is way behind and data needs to be dropped //we'll make the server drop down to 30 inputs for good measure memInput.RemoveRange(30, memInput.Count - 30); } break; case ClientNetObject.ENTITY_STATE: int eventType = msg.ReadRangedInteger(0, 4); switch (eventType) { case 0: Inventory.ServerRead(type, msg, c); break; case 1: bool doingCPR = msg.ReadBoolean(); if (c.Character != this) { #if DEBUG DebugConsole.Log("Received a character update message from a client who's not controlling the character"); #endif return; } AnimController.Anim = doingCPR ? AnimController.Animation.CPR : AnimController.Animation.None; break; case 2: if (c.Character != this) { #if DEBUG DebugConsole.Log("Received a character update message from a client who's not controlling the character"); #endif return; } if (IsIncapacitated) { var causeOfDeath = CharacterHealth.GetCauseOfDeath(); Kill(causeOfDeath.type, causeOfDeath.affliction); } break; case 3: // NetEntityEvent.Type.UpdateTalents if (c.Character != this) { #if DEBUG DebugConsole.Log("Received a character update message from a client who's not controlling the character"); #endif return; } // get the full list of talents from the player, only give the ones // that are not already given (or otherwise not viable) ushort talentCount = msg.ReadUInt16(); List <string> talentSelection = new List <string>(); for (int i = 0; i < talentCount; i++) { UInt32 talentIdentifier = msg.ReadUInt32(); var prefab = TalentPrefab.TalentPrefabs.Find(p => p.UIntIdentifier == talentIdentifier); if (prefab != null) { talentSelection.Add(prefab.Identifier); } } talentSelection = TalentTree.CheckTalentSelection(this, talentSelection); foreach (string talent in talentSelection) { GiveTalent(talent); } break; } break; } msg.ReadPadBits(); }