using Lidgren.Network; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Barotrauma.Networking { class ClientEntityEventManager : NetEntityEventManager { private List events; private UInt32 ID; private GameClient thisClient; //when was a specific entity event last sent to the client // key = event id, value = NetTime.Now when sending public Dictionary eventLastSent; public UInt32 LastReceivedID { get { return lastReceivedID; } } private UInt32 lastReceivedID; public ClientEntityEventManager(GameClient client) { events = new List(); eventLastSent = new Dictionary(); thisClient = client; } public void CreateEvent(IClientSerializable entity, object[] extraData = null) { if (GameMain.Client == null || GameMain.Client.Character == null) return; if (!(entity is Entity)) { DebugConsole.ThrowError("Can't create an entity event for " + entity + "!"); return; } ID++; var newEvent = new ClientEntityEvent(entity, ID); newEvent.CharacterStateID = GameMain.Client.Character.LastNetworkUpdateID; if (extraData != null) newEvent.SetData(extraData); events.Add(newEvent); } public void Write(NetOutgoingMessage msg, NetConnection serverConnection) { if (events.Count == 0) return; List eventsToSync = new List(); //find the index of the first event the server hasn't received int startIndex = events.Count; while (startIndex > 0 && events[startIndex-1].ID > thisClient.LastSentEntityEventID) { startIndex--; } for (int i = startIndex; i < events.Count; i++) { //find the first event that hasn't been sent in 1.5 * roundtriptime or at all float lastSent = 0; eventLastSent.TryGetValue(events[i].ID, out lastSent); if (lastSent > NetTime.Now - serverConnection.AverageRoundtripTime * 1.5f) { continue; } eventsToSync.AddRange(events.GetRange(i, events.Count - i)); break; } if (eventsToSync.Count == 0) return; //too many events for one packet if (eventsToSync.Count > MaxEventsPerWrite) { eventsToSync.RemoveRange(MaxEventsPerWrite, eventsToSync.Count - MaxEventsPerWrite); } if (eventsToSync.Count == 0) return; foreach (NetEntityEvent entityEvent in eventsToSync) { eventLastSent[entityEvent.ID] = (float)NetTime.Now; } msg.Write((byte)ClientNetObject.ENTITY_STATE); Write(msg, eventsToSync); } /// /// Read the events from the message, ignoring ones we've already received /// public void Read(ServerNetObject type, NetIncomingMessage msg, float sendingTime) { UInt32 unreceivedEntityEventCount = 0; UInt32 firstNewID = 0; if (type == ServerNetObject.ENTITY_EVENT_INITIAL) { unreceivedEntityEventCount = msg.ReadUInt32(); firstNewID = msg.ReadUInt32(); } UInt32 firstEventID = msg.ReadUInt32(); int eventCount = msg.ReadByte(); for (int i = 0; i < eventCount; i++) { UInt32 thisEventID = firstEventID + (UInt32)i; UInt16 entityID = msg.ReadUInt16(); byte msgLength = msg.ReadByte(); IServerSerializable entity = Entity.FindEntityByID(entityID) as IServerSerializable; //skip the event if we've already received it or if the entity isn't found if (thisEventID != lastReceivedID + 1 || entity == null) { if (thisEventID != lastReceivedID + 1) { DebugConsole.NewMessage("received msg " + thisEventID, Microsoft.Xna.Framework.Color.Red); } else if (entity == null) { DebugConsole.NewMessage("received msg " + thisEventID + ", entity " + entityID + " not found", Microsoft.Xna.Framework.Color.Red); } msg.Position += msgLength * 8; } else { long msgPosition = msg.Position; DebugConsole.NewMessage("received msg " + thisEventID + " (" + entity.ToString() + ")", Microsoft.Xna.Framework.Color.Green); lastReceivedID++; try { ReadEvent(msg, entity, sendingTime); } catch (Exception e) { #if DEBUG DebugConsole.ThrowError("Failed to read event for entity \"" + entity.ToString() + "\"!", e); #endif msg.Position = msgPosition + msgLength * 8; } } msg.ReadPadBits(); } if (type == ServerNetObject.ENTITY_EVENT_INITIAL) { if (lastReceivedID == unreceivedEntityEventCount - 1 || unreceivedEntityEventCount == 0) { lastReceivedID = firstNewID - 1; } } } protected override void WriteEvent(NetBuffer buffer, NetEntityEvent entityEvent, Client recipient = null) { var clientEvent = entityEvent as ClientEntityEvent; if (clientEvent == null) return; clientEvent.Write(buffer); } protected void ReadEvent(NetIncomingMessage buffer, IServerSerializable entity, float sendingTime) { entity.ClientRead(ServerNetObject.ENTITY_EVENT, buffer, sendingTime); } public void Clear() { ID = 0; lastReceivedID = 0; events.Clear(); eventLastSent.Clear(); } } }