diff --git a/Subsurface/Source/Characters/Character.cs b/Subsurface/Source/Characters/Character.cs index f29b3470c..1f227c50e 100644 --- a/Subsurface/Source/Characters/Character.cs +++ b/Subsurface/Source/Characters/Character.cs @@ -49,8 +49,6 @@ namespace Barotrauma } } - public bool SpawnedMidRound; - public List SpawnItems = new List(); private bool enabled; @@ -1846,7 +1844,7 @@ namespace Barotrauma msg.Write(Info.Job == null ? "" : Info.Job.Name); } - public static Character ReadSpawnData(NetIncomingMessage inc) + public static Character ReadSpawnData(NetIncomingMessage inc, bool spawn=true) { if (GameMain.Server != null) return null; @@ -1862,6 +1860,8 @@ namespace Barotrauma if (noInfo) { + if (!spawn) return null; + character = Character.Create(configPath, position, null, true); character.ID = id; } @@ -1877,6 +1877,8 @@ namespace Barotrauma int headSpriteID = inc.ReadByte(); string jobName = inc.ReadString(); + if (!spawn) return null; + JobPrefab jobPrefab = JobPrefab.List.Find(jp => jp.Name == jobName); CharacterInfo ch = new CharacterInfo(configPath, newName, isFemale ? Gender.Female : Gender.Male, jobPrefab); @@ -1885,6 +1887,12 @@ namespace Barotrauma character = Character.Create(configPath, position, ch, true, hasAi); character.ID = id; + if (GameMain.Client.MyCharacterID == id) + { + GameMain.Client.Character = character; + Controlled = character; + } + if (configPath == Character.HumanConfigFile) { GameMain.GameSession.CrewManager.characters.Add(character); diff --git a/Subsurface/Source/Characters/HuskInfection.cs b/Subsurface/Source/Characters/HuskInfection.cs index 8622d7896..5c3600dac 100644 --- a/Subsurface/Source/Characters/HuskInfection.cs +++ b/Subsurface/Source/Characters/HuskInfection.cs @@ -171,7 +171,7 @@ namespace Barotrauma character.Enabled = false; - husk.SpawnedMidRound = true; + Entity.Spawner.AddToSpawnedList(husk); } } } diff --git a/Subsurface/Source/DebugConsole.cs b/Subsurface/Source/DebugConsole.cs index 06b01d1a1..5518f79f4 100644 --- a/Subsurface/Source/DebugConsole.cs +++ b/Subsurface/Source/DebugConsole.cs @@ -294,7 +294,7 @@ namespace Barotrauma if (spawnedCharacter != null && GameMain.Server != null) { - spawnedCharacter.SpawnedMidRound = true; + Entity.Spawner.AddToSpawnedList(spawnedCharacter); } break; diff --git a/Subsurface/Source/Items/Item.cs b/Subsurface/Source/Items/Item.cs index 156b532cd..13a040815 100644 --- a/Subsurface/Source/Items/Item.cs +++ b/Subsurface/Source/Items/Item.cs @@ -31,7 +31,7 @@ namespace Barotrauma public static List ItemList = new List(); private ItemPrefab prefab; - public static ItemSpawner Spawner = new ItemSpawner(); + public static ItemRemover Remover = new ItemRemover(); public static bool ShowLinks = true; @@ -1556,7 +1556,120 @@ namespace Barotrauma } public void ServerWrite(NetOutgoingMessage msg, Client c) { } - public void ClientRead(NetIncomingMessage msg) { } + public void ClientRead(NetIncomingMessage msg) { } + + public void WriteSpawnData(NetOutgoingMessage msg) + { + if (GameMain.Server == null) return; + + msg.Write(Prefab.Name); + msg.Write(ID); + + if (ParentInventory == null || ParentInventory.Owner == null) + { + msg.Write((ushort)0); + + msg.Write(Position.X); + msg.Write(Position.Y); + msg.Write(Submarine != null ? Submarine.ID : (ushort)0); + } + else + { + msg.Write(ParentInventory.Owner.ID); + + int index = ParentInventory.FindIndex(this); + msg.Write(index < 0 ? (byte)255 : (byte)index); + } + + if (Name == "ID Card") msg.Write(Tags); + } + + public static Item ReadSpawnData(NetIncomingMessage msg, bool spawn = true) + { + if (GameMain.Server != null) return null; + + string itemName = msg.ReadString(); + ushort itemId = msg.ReadUInt16(); + + ushort inventoryId = msg.ReadUInt16(); + + Vector2 pos = Vector2.Zero; + Submarine sub = null; + int inventorySlotIndex = -1; + + if (inventoryId > 0) + { + inventorySlotIndex = msg.ReadByte(); + } + else + { + pos = new Vector2(msg.ReadSingle(), msg.ReadSingle()); + + ushort subID = msg.ReadUInt16(); + if (subID > 0) + { + sub = Submarine.Loaded.Find(s => s.ID == subID); + } + } + + string tags = ""; + if (itemName == "ID Card") + { + tags = msg.ReadString(); + } + + if (!spawn) return null; + + //---------------------------------------- + + var prefab = MapEntityPrefab.list.Find(me => me.Name == itemName); + if (prefab == null) return null; + + var itemPrefab = prefab as ItemPrefab; + if (itemPrefab == null) return null; + + Inventory inventory = null; + + var inventoryOwner = Entity.FindEntityByID(inventoryId); + if (inventoryOwner != null) + { + if (inventoryOwner is Character) + { + inventory = (inventoryOwner as Character).Inventory; + } + else if (inventoryOwner is Item) + { + var containers = (inventoryOwner as Item).GetComponents(); + if (containers != null && containers.Any()) + { + inventory = containers.Last().Inventory; + } + } + } + + var item = new Item(itemPrefab, pos, sub); + + item.ID = itemId; + if (sub != null) + { + item.CurrentHull = Hull.FindHull(pos + sub.Position, null, true); + item.Submarine = item.CurrentHull == null ? null : item.CurrentHull.Submarine; + } + + if (!string.IsNullOrEmpty(tags)) item.Tags = tags; + + if (inventory != null) + { + if (inventorySlotIndex >= 0 && inventorySlotIndex < 255 && + inventory.TryPutItem(item, inventorySlotIndex, false)) + { + return null; + } + inventory.TryPutItem(item, item.AllowedSlots); + } + + return item; + } public static void Load(XElement element, Submarine submarine) { diff --git a/Subsurface/Source/Map/Entity.cs b/Subsurface/Source/Map/Entity.cs index 559ba92ac..021a1e73e 100644 --- a/Subsurface/Source/Map/Entity.cs +++ b/Subsurface/Source/Map/Entity.cs @@ -11,6 +11,7 @@ namespace Barotrauma { private static Dictionary dictionary = new Dictionary(); + public static EntitySpawner Spawner = new EntitySpawner(); private ushort id; diff --git a/Subsurface/Source/Map/MapEntity.cs b/Subsurface/Source/Map/MapEntity.cs index 4a614542e..fe509626c 100644 --- a/Subsurface/Source/Map/MapEntity.cs +++ b/Subsurface/Source/Map/MapEntity.cs @@ -269,7 +269,7 @@ namespace Barotrauma item.Update(cam, deltaTime); } - Item.Spawner.Update(); + Entity.Spawner.Update(); Item.Remover.Update(); } diff --git a/Subsurface/Source/Networking/Client.cs b/Subsurface/Source/Networking/Client.cs index 789b9c86d..c82a92961 100644 --- a/Subsurface/Source/Networking/Client.cs +++ b/Subsurface/Source/Networking/Client.cs @@ -36,7 +36,7 @@ namespace Barotrauma.Networking public UInt32 lastSentChatMsgID = 0; //last msg this client said public UInt32 lastRecvChatMsgID = 0; //last msg this client knows about - public UInt32 lastRecvItemSpawnID = 0; + public UInt32 lastRecvEntitySpawnID = 0; public UInt32 lastRecvItemRemoveID = 0; @@ -63,7 +63,7 @@ namespace Barotrauma.Networking lastSentChatMsgID = 0; lastRecvChatMsgID = ChatMessage.LastID; - lastRecvItemSpawnID = 0; + lastRecvEntitySpawnID = 0; lastRecvItemRemoveID = 0; } diff --git a/Subsurface/Source/Items/ItemSpawner.cs b/Subsurface/Source/Networking/EntitySpawner.cs similarity index 51% rename from Subsurface/Source/Items/ItemSpawner.cs rename to Subsurface/Source/Networking/EntitySpawner.cs index 8924029d8..bc898d7d3 100644 --- a/Subsurface/Source/Items/ItemSpawner.cs +++ b/Subsurface/Source/Networking/EntitySpawner.cs @@ -6,15 +6,22 @@ using System; namespace Barotrauma { - class ItemSpawner : IServerSerializable + class EntitySpawner : IServerSerializable { + private enum SpawnableType { Item, Character }; + public UInt32 NetStateID { get; private set; } - class ItemSpawnInfo + interface IEntitySpawnInfo + { + Entity Spawn(); + } + + class ItemSpawnInfo : IEntitySpawnInfo { public readonly ItemPrefab Prefab; @@ -40,17 +47,32 @@ namespace Barotrauma Prefab = prefab; Inventory = inventory; } + + public Entity Spawn() + { + Item spawnedItem = null; + + if (Inventory != null) + { + spawnedItem = new Item(Prefab, Vector2.Zero, null); + Inventory.TryPutItem(spawnedItem, spawnedItem.AllowedSlots); + } + else + { + spawnedItem = new Item(Prefab, Position, Submarine); + } + + return spawnedItem; + } } - private readonly Queue spawnQueue; - - - public List spawnItems = new List(); - - - public ItemSpawner() + private readonly Queue spawnQueue; + + private List spawnedEntities = new List(); + + public EntitySpawner() { - spawnQueue = new Queue(); + spawnQueue = new Queue(); } public void QueueItem(ItemPrefab itemPrefab, Vector2 worldPosition, bool isNetworkMessage = false) @@ -80,45 +102,27 @@ namespace Barotrauma public void Update() { if (!spawnQueue.Any()) return; - - List items = new List(); //List inventories = new List(); while (spawnQueue.Count>0) { - var itemInfo = spawnQueue.Dequeue(); + var entitySpawnInfo = spawnQueue.Dequeue(); - Item spawnedItem = null; + var spawnedEntity = entitySpawnInfo.Spawn(); - if (itemInfo.Inventory != null) - { - spawnedItem = new Item(itemInfo.Prefab, Vector2.Zero, null); - itemInfo.Inventory.TryPutItem(spawnedItem, spawnedItem.AllowedSlots); - } - else - { - spawnedItem = new Item(itemInfo.Prefab, itemInfo.Position, itemInfo.Submarine); - } - - AddToSpawnedList(spawnedItem); - items.Add(spawnedItem); + if (spawnedEntity!= null) AddToSpawnedList(spawnedEntity); } //if (GameMain.Server != null) GameMain.Server.SendItemSpawnMessage(items); } - public void AddToSpawnedList(List items) + public void AddToSpawnedList(Entity entity) { - foreach (Item item in items) - { - AddToSpawnedList(item); - } - } + if (GameMain.Server == null) return; + if (entity == null) return; - public void AddToSpawnedList(Item item) - { - spawnItems.Add(item); - NetStateID = (UInt32)spawnItems.Count; + spawnedEntities.Add(entity); + NetStateID = (UInt32)spawnedEntities.Count; } public void ServerWrite(Lidgren.Network.NetOutgoingMessage message, Client client) @@ -126,35 +130,22 @@ namespace Barotrauma if (GameMain.Server == null) return; //skip items that the client already knows about - List items = spawnItems.Skip((int)client.lastRecvItemSpawnID).ToList(); + List entities = spawnedEntities.Skip((int)client.lastRecvEntitySpawnID).ToList(); - message.Write((UInt32)spawnItems.Count); + message.Write((UInt32)spawnedEntities.Count); - message.Write((byte)items.Count); - for (int i = 0; i < items.Count; i++) + message.Write((byte)entities.Count); + for (int i = 0; i < entities.Count; i++) { - message.Write(items[i].Prefab.Name); - message.Write(items[i].ID); - - if (items[i].ParentInventory == null || items[i].ParentInventory.Owner == null) + if (entities[i] is Item) { - message.Write((ushort)0); - - message.Write(items[i].Position.X); - message.Write(items[i].Position.Y); - message.Write(items[i].Submarine != null ? items[i].Submarine.ID : (ushort)0); + message.Write((byte)SpawnableType.Item); + ((Item)entities[i]).WriteSpawnData(message); } - else + else if (entities[i] is Character) { - message.Write(items[i].ParentInventory.Owner.ID); - - int index = items[i].ParentInventory.FindIndex(items[i]); - message.Write(index < 0 ? (byte)255 : (byte)index); - } - - if (items[i].Name == "ID Card") - { - message.Write(items[i].Tags); + message.Write((byte)SpawnableType.Character); + ((Character)entities[i]).WriteSpawnData(message); } } } @@ -165,102 +156,38 @@ namespace Barotrauma UInt32 ID = message.ReadUInt32(); - var itemCount = message.ReadByte(); - for (int i = 0; i < itemCount; i++) + var entityCount = message.ReadByte(); + for (int i = 0; i < entityCount; i++) { - string itemName = message.ReadString(); - ushort itemId = message.ReadUInt16(); - - ushort inventoryId = message.ReadUInt16(); - - Vector2 pos = Vector2.Zero; - Submarine sub = null; - int inventorySlotIndex = -1; - - if (inventoryId > 0) + switch (message.ReadByte()) { - inventorySlotIndex = message.ReadByte(); - } - else - { - pos = new Vector2(message.ReadSingle(), message.ReadSingle()); - - ushort subID = message.ReadUInt16(); - if (subID > 0) - { - sub = Submarine.Loaded.Find(s => s.ID == subID); - } + case (byte)SpawnableType.Item: + Item.ReadSpawnData(message, ID - entityCount + i >= NetStateID); + break; + case (byte)SpawnableType.Character: + Character.ReadSpawnData(message, ID - entityCount + i >= NetStateID); + break; + default: + DebugConsole.ThrowError("Received invalid entity spawn message (unknown spawnable type)"); + break; } - string tags = ""; - if (itemName == "ID Card") - { - tags = message.ReadString(); - } - - if (ID - itemCount + i < NetStateID) continue; - - //---------------------------------------- - - var prefab = MapEntityPrefab.list.Find(me => me.Name == itemName); - if (prefab == null) continue; - - var itemPrefab = prefab as ItemPrefab; - if (itemPrefab == null) continue; - - Inventory inventory = null; - - var inventoryOwner = Entity.FindEntityByID(inventoryId); - if (inventoryOwner != null) - { - if (inventoryOwner is Character) - { - inventory = (inventoryOwner as Character).Inventory; - } - else if (inventoryOwner is Item) - { - var containers = (inventoryOwner as Item).GetComponents(); - if (containers!=null && containers.Any()) - { - inventory = containers.Last().Inventory; - } - } - } - - var item = new Item(itemPrefab, pos, sub); - - item.ID = itemId; - if (sub != null) - { - item.CurrentHull = Hull.FindHull(pos + sub.Position, null, true); - item.Submarine = item.CurrentHull == null ? null : item.CurrentHull.Submarine; - } - - if (!string.IsNullOrEmpty(tags)) item.Tags = tags; - - if (inventory != null) - { - if (inventorySlotIndex >= 0 && inventorySlotIndex < 255 && - inventory.TryPutItem(item, inventorySlotIndex, false)) - { - continue; - } - inventory.TryPutItem(item, item.AllowedSlots); - } } NetStateID = Math.Max(ID, NetStateID); } + public void Clear() { NetStateID = 0; spawnQueue.Clear(); - spawnItems.Clear(); + spawnedEntities.Clear(); } } + //todo: turn into a generic EntityRemover class + sync class ItemRemover : IServerSerializable { public UInt32 NetStateID diff --git a/Subsurface/Source/Networking/GameClient.cs b/Subsurface/Source/Networking/GameClient.cs index 78ed11e19..8f7097c1a 100644 --- a/Subsurface/Source/Networking/GameClient.cs +++ b/Subsurface/Source/Networking/GameClient.cs @@ -44,6 +44,13 @@ namespace Barotrauma.Networking get { return myID; } } + public ushort MyCharacterID + { + get; + private set; + } + + public override List ConnectedClients { get @@ -570,6 +577,8 @@ namespace Barotrauma.Networking bool respawnAllowed = inc.ReadBoolean(); bool loadSecondSub = inc.ReadBoolean(); + MyCharacterID = inc.ReadUInt16(); + GameModePreset gameMode = GameModePreset.list.Find(gm => gm.Name == modeName); if (gameMode == null) @@ -595,16 +604,16 @@ namespace Barotrauma.Networking if (respawnAllowed) respawnManager = new RespawnManager(this, GameMain.NetLobbyScreen.SelectedShuttle); - int characterCount = inc.ReadByte(); - for (int i = 0; i < characterCount; i++) - { - var character = Character.ReadSpawnData(inc); - if (inc.ReadBoolean()) - { - myCharacter = character; - Character.Controlled = character; - } - } + //int characterCount = inc.ReadByte(); + //for (int i = 0; i < characterCount; i++) + //{ + // var character = Character.ReadSpawnData(inc); + // if (inc.ReadBoolean()) + // { + // myCharacter = character; + // Character.Controlled = character; + // } + //} gameStarted = true; @@ -704,7 +713,7 @@ namespace Barotrauma.Networking case ServerNetObject.CHAT_MESSAGE: ChatMessage.ClientRead(inc); break; - case ServerNetObject.ITEM_SPAWN: + case ServerNetObject.ENTITY_SPAWN: Item.Spawner.ClientRead(inc); break; } diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index 5c0a436d7..ddfae63bf 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -601,7 +601,7 @@ namespace Barotrauma.Networking c.lastRecvGeneralUpdate = Math.Max(c.lastRecvGeneralUpdate, inc.ReadUInt32()); c.lastRecvChatMsgID = Math.Max(c.lastRecvChatMsgID, inc.ReadUInt32()); - c.lastRecvItemSpawnID = Math.Max(c.lastRecvItemSpawnID, inc.ReadUInt32()); + c.lastRecvEntitySpawnID = Math.Max(c.lastRecvEntitySpawnID, inc.ReadUInt32()); break; case ClientNetObject.CHAT_MESSAGE: @@ -656,9 +656,9 @@ namespace Barotrauma.Networking outmsg.WritePadBits(); } - if (Item.Spawner.NetStateID > c.lastRecvItemSpawnID) + if (Item.Spawner.NetStateID > c.lastRecvEntitySpawnID) { - outmsg.Write((byte)ServerNetObject.ITEM_SPAWN); + outmsg.Write((byte)ServerNetObject.ENTITY_SPAWN); Item.Spawner.ServerWrite(outmsg, c); outmsg.WritePadBits(); } @@ -893,7 +893,9 @@ namespace Barotrauma.Networking foreach (Character c in GameMain.GameSession.CrewManager.characters) { - Item.Spawner.AddToSpawnedList(c.SpawnItems); + Entity.Spawner.AddToSpawnedList(c); + + c.SpawnItems.ForEach(item => Entity.Spawner.AddToSpawnedList(item)); } SendStartMessage(roundStartSeed, Submarine.MainSub, GameMain.GameSession.gameMode.Preset, connectedClients); @@ -947,23 +949,7 @@ namespace Barotrauma.Networking msg.Write(AllowRespawn); msg.Write(Submarine.MainSubs[1] != null); //loadSecondSub - var clientsWithCharacter = clients.FindAll(c => c.Character != null); - - int characterCount = clientsWithCharacter.Count; - if (myCharacter != null) characterCount++; - - msg.Write((byte)characterCount); - foreach (Client c in clientsWithCharacter) - { - c.Character.WriteSpawnData(msg); - msg.Write(c == client); - } - - if (myCharacter != null) - { - myCharacter.WriteSpawnData(msg); - msg.Write(false); - } + msg.Write(client.Character.ID); server.SendMessage(msg, client.Connection, NetDeliveryMethod.ReliableUnordered); } diff --git a/Subsurface/Source/Networking/NetworkMember.cs b/Subsurface/Source/Networking/NetworkMember.cs index 36c26a497..2a3623289 100644 --- a/Subsurface/Source/Networking/NetworkMember.cs +++ b/Subsurface/Source/Networking/NetworkMember.cs @@ -47,7 +47,7 @@ namespace Barotrauma.Networking CHARACTER_POSITION, ITEM_STATE, - ITEM_SPAWN + ENTITY_SPAWN } enum VoteType diff --git a/Subsurface/Source/Screens/GameScreen.cs b/Subsurface/Source/Screens/GameScreen.cs index 1d741e248..c91b2ea56 100644 --- a/Subsurface/Source/Screens/GameScreen.cs +++ b/Subsurface/Source/Screens/GameScreen.cs @@ -67,10 +67,12 @@ namespace Barotrauma if (Character.Controlled!=null) { cam.Position = Character.Controlled.WorldPosition; + cam.UpdateTransform(); } else if (Submarine.MainSub != null) { cam.Position = Submarine.MainSub.WorldPosition; + cam.UpdateTransform(); } foreach (MapEntity entity in MapEntity.mapEntityList)