diff --git a/Subsurface/Source/Characters/Character.cs b/Subsurface/Source/Characters/Character.cs index be72137b6..ee7e8e600 100644 --- a/Subsurface/Source/Characters/Character.cs +++ b/Subsurface/Source/Characters/Character.cs @@ -497,17 +497,18 @@ namespace Barotrauma return null; } #endif - + + Character newCharacter = null; if (file != humanConfigFile) { - var enemyCharacter = new AICharacter(file, position, characterInfo, isRemotePlayer); - var ai = new EnemyAIController(enemyCharacter, file); - enemyCharacter.SetAI(ai); + var aiCharacter = new AICharacter(file, position, characterInfo, isRemotePlayer); + var ai = new EnemyAIController(aiCharacter, file); + aiCharacter.SetAI(ai); - enemyCharacter.minHealth = 0.0f; - - return enemyCharacter; + aiCharacter.minHealth = 0.0f; + + newCharacter = aiCharacter; } else if (hasAi) { @@ -517,13 +518,18 @@ namespace Barotrauma aiCharacter.minHealth = -100.0f; - return aiCharacter; + newCharacter = aiCharacter; + } + else + { + newCharacter = new Character(file, position, characterInfo, isRemotePlayer); + newCharacter.minHealth = -100.0f; } - var character = new Character(file, position, characterInfo, isRemotePlayer); - character.minHealth = -100.0f; + if (GameMain.Server != null && Entity.Spawner != null) + Entity.Spawner.CreateNetworkEvent(newCharacter, false); - return character; + return newCharacter; } protected Character(string file, Vector2 position, CharacterInfo characterInfo = null, bool isRemotePlayer = false) @@ -1911,6 +1917,21 @@ namespace Barotrauma if (AnimController != null) AnimController.Remove(); if (Lights.LightManager.ViewTarget == this) Lights.LightManager.ViewTarget = null; + + if (selectedItems[0] != null) selectedItems[0].Drop(this); + if (selectedItems[1] != null) selectedItems[1].Drop(this); + + if (GameMain.GameSession?.CrewManager != null && + GameMain.GameSession.CrewManager.characters.Contains(this)) + { + GameMain.GameSession.CrewManager.characters.Remove(this); + } + + foreach (Character c in CharacterList) + { + if (c.closestCharacter == this) c.closestCharacter = null; + if (c.selectedCharacter == this) c.selectedCharacter = null; + } } } } diff --git a/Subsurface/Source/Characters/HuskInfection.cs b/Subsurface/Source/Characters/HuskInfection.cs index 95c9525d6..9345aaf5d 100644 --- a/Subsurface/Source/Characters/HuskInfection.cs +++ b/Subsurface/Source/Characters/HuskInfection.cs @@ -178,9 +178,7 @@ namespace Barotrauma } character.Enabled = false; - Entity.Spawner.AddToRemoveQueue(character); - Entity.Spawner.AddToSpawnedList(husk); } } } diff --git a/Subsurface/Source/Characters/Jobs/Job.cs b/Subsurface/Source/Characters/Jobs/Job.cs index 642ad7369..3848eddcd 100644 --- a/Subsurface/Source/Characters/Jobs/Job.cs +++ b/Subsurface/Source/Characters/Jobs/Job.cs @@ -106,6 +106,11 @@ namespace Barotrauma } Item item = new Item(itemPrefab, character.Position, null); + + if (GameMain.Server != null && Entity.Spawner != null) + { + Entity.Spawner.CreateNetworkEvent(item, false); + } if (ToolBox.GetAttributeBool(itemElement, "equip", false)) { diff --git a/Subsurface/Source/DebugConsole.cs b/Subsurface/Source/DebugConsole.cs index 2320a6661..c9ad48414 100644 --- a/Subsurface/Source/DebugConsole.cs +++ b/Subsurface/Source/DebugConsole.cs @@ -320,11 +320,6 @@ namespace Barotrauma + "/" + commands[1].ToLower() + ".xml", spawnPosition); } - if (spawnedCharacter != null && GameMain.Server != null) - { - Entity.Spawner.AddToSpawnedList(spawnedCharacter); - } - break; case "spawnitem": if (commands.Length < 2) return; diff --git a/Subsurface/Source/GameSession/GameSession.cs b/Subsurface/Source/GameSession/GameSession.cs index cf5772361..39ef3e848 100644 --- a/Subsurface/Source/GameSession/GameSession.cs +++ b/Subsurface/Source/GameSession/GameSession.cs @@ -109,9 +109,7 @@ namespace Barotrauma CrewManager = new CrewManager(); TaskManager = new TaskManager(this); - - Entity.Spawner.Clear(); - + this.saveFile = saveFile; infoButton = new GUIButton(new Rectangle(10, 10, 100, 20), "Info", GUI.Style, null); @@ -179,7 +177,7 @@ namespace Barotrauma DebugConsole.ThrowError("Couldn't start game session, submarine not selected"); return; } - + if (reloadSub || Submarine.MainSub != submarine) submarine.Load(true); Submarine.MainSub = submarine; if (loadSecondSub) @@ -194,18 +192,13 @@ namespace Barotrauma Submarine.MainSubs[1].Load(false); } } - - //var secondSub = new Submarine(submarine.FilePath, submarine.MD5Hash.Hash); - //secondSub.Load(false); - + if (level != null) { level.Generate(); submarine.SetPosition(submarine.FindSpawnPos(level.StartPosition - new Vector2(0.0f, 2000.0f))); - - //secondSub.SetPosition(level.EndPosition - new Vector2(0.0f, 2000.0f)); - + GameMain.GameScreen.BackgroundCreatureManager.SpawnSprites(80); } @@ -224,6 +217,8 @@ namespace Barotrauma if (gameMode != null) gameMode.MsgBox(); + Entity.Spawner = new EntitySpawner(); + GameMain.GameScreen.ColorFade(Color.Black, Color.TransparentBlack, 5.0f); SoundPlayer.SwitchMusic(); } diff --git a/Subsurface/Source/Items/Item.cs b/Subsurface/Source/Items/Item.cs index 1b96a8b83..95a0abacb 100644 --- a/Subsurface/Source/Items/Item.cs +++ b/Subsurface/Source/Items/Item.cs @@ -944,9 +944,9 @@ namespace Barotrauma else if (body.Enabled) { var holdable = GetComponent(); - if (holdable!=null && holdable.Picker !=null) + if (holdable != null && holdable.Picker?.AnimController != null) { - if (holdable.Picker.SelectedItems[0]==this) + if (holdable.Picker.SelectedItems[0] == this) { depth = holdable.Picker.AnimController.GetLimb(LimbType.RightHand).sprite.Depth + 0.000001f; } @@ -960,7 +960,7 @@ namespace Barotrauma else { body.Draw(spriteBatch, prefab.sprite, color, depth); - } + } } } diff --git a/Subsurface/Source/Map/Entity.cs b/Subsurface/Source/Map/Entity.cs index 8ee834dc8..d811c6ab0 100644 --- a/Subsurface/Source/Map/Entity.cs +++ b/Subsurface/Source/Map/Entity.cs @@ -11,7 +11,7 @@ namespace Barotrauma { private static Dictionary dictionary = new Dictionary(); - public static EntitySpawner Spawner = new EntitySpawner(); + public static EntitySpawner Spawner; private ushort id; diff --git a/Subsurface/Source/Networking/Client.cs b/Subsurface/Source/Networking/Client.cs index c1a463a66..c35e1023c 100644 --- a/Subsurface/Source/Networking/Client.cs +++ b/Subsurface/Source/Networking/Client.cs @@ -38,9 +38,7 @@ namespace Barotrauma.Networking public UInt16 lastSentEntityEventID = 0; public UInt16 lastRecvEntityEventID = 0; - - public UInt16 lastRecvEntitySpawnID = 0; - + public List chatMsgQueue = new List(); public UInt16 lastChatMsgQueueID; public float ChatSpamSpeed; @@ -76,8 +74,7 @@ namespace Barotrauma.Networking lastRecvChatMsgID = ChatMessage.LastID; lastRecvGeneralUpdate = 0; - - lastRecvEntitySpawnID = 0; + lastRecvEntityEventID = 0; UnreceivedEntityEventCount = 0; diff --git a/Subsurface/Source/Networking/EntitySpawner.cs b/Subsurface/Source/Networking/EntitySpawner.cs index 5ee54bc5e..229fe6ab8 100644 --- a/Subsurface/Source/Networking/EntitySpawner.cs +++ b/Subsurface/Source/Networking/EntitySpawner.cs @@ -6,18 +6,12 @@ using System.Linq; namespace Barotrauma { - class EntitySpawner : IServerSerializable + class EntitySpawner : Entity, IServerSerializable { const int MaxEntitiesPerWrite = 10; private enum SpawnableType { Item, Character }; - - public UInt16 NetStateID - { - get; - private set; - } - + interface IEntitySpawnInfo { Entity Spawn(); @@ -84,9 +78,8 @@ namespace Barotrauma } } - private List spawnHistory = new List(); - public EntitySpawner() + : base(null) { spawnQueue = new Queue(); removeQueue = new Queue(); @@ -132,6 +125,13 @@ namespace Barotrauma } } + public void CreateNetworkEvent(Entity entity, bool remove) + { + if (GameMain.Server != null && entity != null) + { + GameMain.Server.CreateEntityEvent(this, new object[] { new SpawnOrRemove(entity, remove) }); + } + } public void Update() { @@ -142,38 +142,22 @@ namespace Barotrauma var entitySpawnInfo = spawnQueue.Dequeue(); var spawnedEntity = entitySpawnInfo.Spawn(); - if (spawnedEntity != null) AddToSpawnedList(spawnedEntity); + if (spawnedEntity != null) + { + CreateNetworkEvent(spawnedEntity, false); + } } while (removeQueue.Count > 0) { - var entity = removeQueue.Dequeue(); - spawnHistory.Add(new SpawnOrRemove(entity, true)); + var removedEntity = removeQueue.Dequeue(); - entity.Remove(); - NetStateID = (UInt16)spawnHistory.Count; - } - } + if (GameMain.Server != null) + { + CreateNetworkEvent(removedEntity, true); + } - public void AddToSpawnedList(Entity entity) - { - if (GameMain.Server == null) return; - if (entity == null) return; - - spawnHistory.Add(new SpawnOrRemove(entity, false)); - - NetStateID = (UInt16)spawnHistory.Count; - } - - public void AddToSpawnedList(IEnumerable entities) - { - if (GameMain.Server == null) return; - if (entities == null) return; - - foreach (Entity entity in entities) - { - spawnHistory.Add(new SpawnOrRemove(entity, false)); - NetStateID = (UInt16)spawnHistory.Count; + removedEntity.Remove(); } } @@ -181,89 +165,60 @@ namespace Barotrauma { if (GameMain.Server == null) return; - //skip items that the client already knows about - List entities = spawnHistory.Skip((int)client.lastRecvEntitySpawnID).ToList(); + SpawnOrRemove entities = (SpawnOrRemove)extraData[0]; + + message.Write(entities.Remove); - if (entities.Count > MaxEntitiesPerWrite) + if (entities.Remove) { - entities = entities.GetRange(0, MaxEntitiesPerWrite); + message.Write(entities.Entity.ID); } - - message.Write((UInt16)(spawnHistory.IndexOf(entities[0])+1)); - message.WriteRangedInteger(0, MaxEntitiesPerWrite, entities.Count); - - for (int i = 0; i < entities.Count; i++) + else { - message.Write(entities[i].Remove); - - if (entities[i].Remove) + if (entities.Entity is Item) { - message.Write(entities[i].Entity.ID); + message.Write((byte)SpawnableType.Item); + ((Item)entities.Entity).WriteSpawnData(message); } - else + else if (entities.Entity is Character) { - if (entities[i].Entity is Item) - { - message.Write((byte)SpawnableType.Item); - ((Item)entities[i].Entity).WriteSpawnData(message); - } - else if (entities[i].Entity is Character) - { - message.Write((byte)SpawnableType.Character); - ((Character)entities[i].Entity).WriteSpawnData(message); - } + message.Write((byte)SpawnableType.Character); + ((Character)entities.Entity).WriteSpawnData(message); } - } + } } public void ClientRead(ServerNetObject type, Lidgren.Network.NetBuffer message, float sendingTime) { if (GameMain.Server != null) return; - UInt16 ID = message.ReadUInt16(); - var entityCount = message.ReadRangedInteger(0, MaxEntitiesPerWrite); - for (int i = 0; i < entityCount; i++) + bool remove = message.ReadBoolean(); + + if (remove) { - bool remove = message.ReadBoolean(); + ushort entityId = message.ReadUInt16(); - if (remove) + var entity = FindEntityByID(entityId); + if (entity != null) { - ushort entityId = message.ReadUInt16(); - - var entity = Entity.FindEntityByID(entityId); - if (entity != null && NetIdUtils.IdMoreRecent((UInt16)(ID + i), NetStateID)) - { - entity.Remove(); - } - } - else - { - switch (message.ReadByte()) - { - case (byte)SpawnableType.Item: - Item.ReadSpawnData(message, NetIdUtils.IdMoreRecent((UInt16)(ID + i), NetStateID)); - break; - case (byte)SpawnableType.Character: - Character.ReadSpawnData(message, NetIdUtils.IdMoreRecent((UInt16)(ID + i), NetStateID)); - break; - default: - DebugConsole.ThrowError("Received invalid entity spawn message (unknown spawnable type)"); - break; - } + entity.Remove(); + } + } + else + { + switch (message.ReadByte()) + { + case (byte)SpawnableType.Item: + Item.ReadSpawnData(message, true); + break; + case (byte)SpawnableType.Character: + Character.ReadSpawnData(message, true); + break; + default: + DebugConsole.ThrowError("Received invalid entity spawn message (unknown spawnable type)"); + break; } } - - NetStateID = Math.Max((UInt16)(ID + entityCount - 1), NetStateID); - } - - - public void Clear() - { - NetStateID = 0; - - spawnQueue.Clear(); - removeQueue.Clear(); - spawnHistory.Clear(); } } } diff --git a/Subsurface/Source/Networking/GameClient.cs b/Subsurface/Source/Networking/GameClient.cs index 07ed46c29..631a26fec 100644 --- a/Subsurface/Source/Networking/GameClient.cs +++ b/Subsurface/Source/Networking/GameClient.cs @@ -2,11 +2,8 @@ using Lidgren.Network; using Microsoft.Xna.Framework; using System.Collections.Generic; -using FarseerPhysics; using System.IO; -using System.Linq; using System.Text; -using Barotrauma.Items.Components; using System.ComponentModel; namespace Barotrauma.Networking @@ -635,8 +632,7 @@ namespace Barotrauma.Networking //enable spectate button in case we fail to start the round now //(for example, due to a missing sub file or an error) GameMain.NetLobbyScreen.ShowSpectateButton(); - - Entity.Spawner.Clear(); + entityEventManager.Clear(); LastSentEntityEventID = 0; @@ -921,10 +917,6 @@ namespace Barotrauma.Networking case ServerNetObject.CHAT_MESSAGE: ChatMessage.ClientRead(inc); break; - case ServerNetObject.ENTITY_SPAWN: - Item.Spawner.ClientRead(objHeader, inc, sendingTime); - inc.ReadPadBits(); - break; default: DebugConsole.ThrowError("Error while reading update from server (unknown object header \""+objHeader+"\"!)"); break; @@ -959,7 +951,6 @@ namespace Barotrauma.Networking outmsg.Write((byte)ClientNetObject.SYNC_IDS); //outmsg.Write(GameMain.NetLobbyScreen.LastUpdateID); outmsg.Write(ChatMessage.LastID); - outmsg.Write(Entity.Spawner.NetStateID); outmsg.Write(entityEventManager.LastReceivedID); chatMsgQueue.RemoveAll(cMsg => !NetIdUtils.IdMoreRecent(cMsg.NetStateID, lastSentChatMsgID)); diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index 64dca5769..9931b4029 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -680,11 +680,9 @@ namespace Barotrauma.Networking //TODO: might want to use a clever class for this UInt16 lastRecvChatMsgID = inc.ReadUInt16(); - UInt16 lastRecvEntitySpawnID = inc.ReadUInt16(); UInt16 lastRecvEntityEventID = inc.ReadUInt16(); //last msgs we've created/sent, the client IDs should never be higher than these - UInt16 lastEntitySpawnID = Entity.Spawner.NetStateID; UInt16 lastEntityEventID = entityEventManager.Events.Count == 0 ? (UInt16)0 : entityEventManager.Events.Last().ID; if (c.NeedsMidRoundSync) @@ -705,20 +703,14 @@ namespace Barotrauma.Networking //client thinks they've received a msg we haven't sent yet (corrupted packet, msg read/written incorrectly?) if (NetIdUtils.IdMoreRecent(lastRecvChatMsgID, c.lastChatMsgQueueID)) DebugConsole.ThrowError("client.lastRecvChatMsgID > lastChatMsgQueueID"); - - if (lastRecvEntitySpawnID > lastEntitySpawnID) - DebugConsole.ThrowError("client.lastRecvEntitySpawnID > lastEntitySpawnID"); - + if (lastRecvEntityEventID > lastEntityEventID) DebugConsole.ThrowError("client.lastRecvEntityEventID > lastEntityEventID"); #endif if (NetIdUtils.IdMoreRecent(lastRecvChatMsgID, c.lastRecvChatMsgID)) c.lastRecvChatMsgID = lastRecvChatMsgID; if (NetIdUtils.IdMoreRecent(c.lastRecvChatMsgID, c.lastChatMsgQueueID)) c.lastRecvChatMsgID = c.lastChatMsgQueueID; - - if (NetIdUtils.IdMoreRecent(lastRecvEntitySpawnID, c.lastRecvEntitySpawnID)) c.lastRecvEntitySpawnID = lastRecvEntitySpawnID; - if (NetIdUtils.IdMoreRecent(c.lastRecvEntitySpawnID, lastEntitySpawnID)) c.lastRecvEntitySpawnID = lastEntitySpawnID; - + if (NetIdUtils.IdMoreRecent(lastRecvEntityEventID, c.lastRecvEntityEventID)) c.lastRecvEntityEventID = lastRecvEntityEventID; if (NetIdUtils.IdMoreRecent(c.lastRecvEntityEventID, lastEntityEventID)) c.lastRecvEntityEventID = lastEntityEventID; @@ -839,14 +831,7 @@ namespace Barotrauma.Networking { cMsg.ServerWrite(outmsg, c); } - - if (Item.Spawner.NetStateID > c.lastRecvEntitySpawnID) - { - outmsg.Write((byte)ServerNetObject.ENTITY_SPAWN); - Item.Spawner.ServerWrite(outmsg, c); - outmsg.WritePadBits(); - } - + foreach (Character character in Character.CharacterList) { if (!character.Enabled) continue; @@ -1054,8 +1039,7 @@ namespace Barotrauma.Networking private IEnumerable StartGame(Submarine selectedSub, Submarine selectedShuttle, GameModePreset selectedMode) { initiatedStartGame = true; - - Item.Spawner.Clear(); + entityEventManager.Clear(); GameMain.NetLobbyScreen.StartButton.Enabled = false; @@ -1103,9 +1087,7 @@ namespace Barotrauma.Networking foreach (Client client in teamClients) { client.NeedsMidRoundSync = false; - - client.lastRecvEntitySpawnID = 0; - + client.entityEventLastSent.Clear(); client.lastSentEntityEventID = 0; client.lastRecvEntityEventID = 0; @@ -1149,13 +1131,7 @@ namespace Barotrauma.Networking GameMain.GameSession.CrewManager.characters.Add(myCharacter); } } - - foreach (Character c in GameMain.GameSession.CrewManager.characters) - { - Entity.Spawner.AddToSpawnedList(c); - Entity.Spawner.AddToSpawnedList(c.SpawnItems); - } - + TraitorManager = null; if (TraitorsEnabled == YesNoMaybe.Yes || (TraitorsEnabled == YesNoMaybe.Maybe && Rand.Range(0.0f, 1.0f) < 0.5f)) @@ -1268,8 +1244,7 @@ namespace Barotrauma.Networking myCharacter = null; GameMain.GameScreen.Cam.TargetPos = Vector2.Zero; GameMain.LightManager.LosEnabled = false; - - Item.Spawner.Clear(); + entityEventManager.Clear(); foreach (Client c in connectedClients) { diff --git a/Subsurface/Source/Networking/NetworkMember.cs b/Subsurface/Source/Networking/NetworkMember.cs index 06bdf5136..2c9336859 100644 --- a/Subsurface/Source/Networking/NetworkMember.cs +++ b/Subsurface/Source/Networking/NetworkMember.cs @@ -54,9 +54,7 @@ namespace Barotrauma.Networking VOTE, ENTITY_POSITION, ENTITY_EVENT, - ENTITY_EVENT_INITIAL, - - ENTITY_SPAWN + ENTITY_EVENT_INITIAL } enum VoteType diff --git a/Subsurface/Source/Networking/RespawnManager.cs b/Subsurface/Source/Networking/RespawnManager.cs index 5835cc50b..06d1c10b5 100644 --- a/Subsurface/Source/Networking/RespawnManager.cs +++ b/Subsurface/Source/Networking/RespawnManager.cs @@ -428,7 +428,6 @@ namespace Barotrauma.Networking bool myCharacter = i >= clients.Count; var character = Character.Create(characterInfos[i], shuttleSpawnPoints[i].WorldPosition, !myCharacter, false); - Entity.Spawner.AddToSpawnedList(character); character.TeamID = 1; @@ -448,29 +447,25 @@ namespace Barotrauma.Networking if (divingSuitPrefab != null && oxyPrefab != null) { var divingSuit = new Item(divingSuitPrefab, pos, respawnShuttle); + Entity.Spawner.CreateNetworkEvent(divingSuit, false); + var oxyTank = new Item(oxyPrefab, pos, respawnShuttle); - - divingSuit.Combine(oxyTank); - - spawnedItems.Add(divingSuit); - spawnedItems.Add(oxyTank); + Entity.Spawner.CreateNetworkEvent(oxyTank, false); + divingSuit.Combine(oxyTank); } if (scooterPrefab != null && batteryPrefab != null) { var scooter = new Item(scooterPrefab, pos, respawnShuttle); + Entity.Spawner.CreateNetworkEvent(scooter, false); + var battery = new Item(batteryPrefab, pos, respawnShuttle); + Entity.Spawner.CreateNetworkEvent(battery, false); scooter.Combine(battery); - - spawnedItems.Add(scooter); - spawnedItems.Add(battery); } - + character.GiveJobItems(mainSubSpawnPoints[i]); - Entity.Spawner.AddToSpawnedList(character.SpawnItems); - Entity.Spawner.AddToSpawnedList(spawnedItems); - GameMain.GameSession.CrewManager.characters.Add(character); }