public ServerEntityEvent(IServerSerializable entity, UInt16 id) : base(entity, id) { serializable = entity; createTime = Timing.TotalTime; }
public void CreateEvent(IServerSerializable entity, object[] extraData = null) { if (entity == null || !(entity is Entity)) { DebugConsole.ThrowError("Can't create an entity event for " + entity + "!"); return; } if (((Entity)entity).Removed && !(entity is Level)) { DebugConsole.ThrowError("Can't create an entity event for " + entity + " - the entity has been removed.\n" + Environment.StackTrace); return; } if (((Entity)entity).IdFreed) { DebugConsole.ThrowError("Can't create an entity event for " + entity + " - the ID of the entity has been freed.\n" + Environment.StackTrace); return; } var newEvent = new ServerEntityEvent(entity, (UInt16)(ID + 1)); if (extraData != null) { newEvent.SetData(extraData); } //remove events that have been sent to all clients, they are redundant now //keep at least one event in the list (lastSentToAll == e.ID) so we can use it to keep track of the latest ID events.RemoveAll(e => NetIdUtils.IdMoreRecent(lastSentToAll, e.ID)); if (server.ConnectedClients.Count(c => c.InGame) == 0 && events.Count > 1) { events.RemoveRange(0, events.Count - 1); } for (int i = events.Count - 1; i >= 0; i--) { //we already have an identical event that's waiting to be sent // -> no need to add a new one if (events[i].IsDuplicate(newEvent) && !events[i].Sent) { return; } } ID++; events.Add(newEvent); if (!uniqueEvents.Any(e => e.IsDuplicate(newEvent))) { //create a copy of the event and give it a new ID var uniqueEvent = new ServerEntityEvent(entity, (UInt16)(uniqueEvents.Count + 1)); uniqueEvent.SetData(extraData); uniqueEvents.Add(uniqueEvent); } }
public ServerEntityEvent(IServerSerializable serializableEntity, UInt16 id) : base(serializableEntity, id) { serializable = serializableEntity; createTime = Timing.TotalTime; #if DEBUG StackTrace = Environment.StackTrace.CleanupStackTrace(); #endif }
public ServerEntityEvent(IServerSerializable entity, UInt16 id) : base(entity, id) { serializable = entity; createTime = Timing.TotalTime; #if DEBUG StackTrace = Environment.StackTrace.ToString(); #endif }
public void CreateEvent(IServerSerializable entity, object[] extraData = null) { if (entity == null || !(entity is Entity)) { DebugConsole.ThrowError("Can't create an entity event for " + entity + "!"); return; } if (((Entity)entity).Removed && !(entity is Level)) { DebugConsole.ThrowError("Can't create an entity event for " + entity + " - the entity has been removed.\n"+Environment.StackTrace); return; } if (((Entity)entity).IdFreed) { DebugConsole.ThrowError("Can't create an entity event for " + entity + " - the ID of the entity has been freed.\n"+Environment.StackTrace); return; } var newEvent = new ServerEntityEvent(entity, (UInt16)(ID + 1)); if (extraData != null) newEvent.SetData(extraData); bool inGameClientsPresent = server.ConnectedClients.Count(c => c.InGame) > 0; //remove old events that have been sent to all clients, they are redundant now // keep at least one event in the list (lastSentToAll == e.ID) so we can use it to keep track of the latest ID // and events less than 15 seconds old to give disconnected clients a bit of time to reconnect without getting desynced if (Timing.TotalTime > GameMain.GameSession.RoundStartTime + NetConfig.RoundStartSyncDuration) { events.RemoveAll(e => (NetIdUtils.IdMoreRecent(lastSentToAll, e.ID) || !inGameClientsPresent) && e.CreateTime < Timing.TotalTime - NetConfig.EventRemovalTime); } for (int i = events.Count - 1; i >= 0; i--) { //we already have an identical event that's waiting to be sent // -> no need to add a new one if (events[i].IsDuplicate(newEvent) && !events[i].Sent) return; } ID++; events.Add(newEvent); if (!uniqueEvents.Any(e => e.IsDuplicate(newEvent))) { //create a copy of the event and give it a new ID var uniqueEvent = new ServerEntityEvent(entity, (UInt16)(uniqueEvents.Count + 1)); uniqueEvent.SetData(extraData); uniqueEvents.Add(uniqueEvent); } }
private void TrySendNetworkUpdate(ISerializableEntity entity, SerializableProperty property) { if (GameMain.Server != null) { IServerSerializable serverSerializable = entity as IServerSerializable; if (serverSerializable != null) { GameMain.Server.CreateEntityEvent(serverSerializable, new object[] { NetEntityEvent.Type.ChangeProperty, property }); } } else if (GameMain.Client != null) { IClientSerializable clientSerializable = entity as IClientSerializable; if (clientSerializable != null) { GameMain.Client.CreateEntityEvent(clientSerializable, new object[] { NetEntityEvent.Type.ChangeProperty, property }); } } }
protected void ReadEvent(IReadMessage buffer, IServerSerializable entity, float sendingTime) { entity.ClientRead(ServerNetObject.ENTITY_EVENT, buffer, sendingTime); }
/// <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 { MidRoundSyncingDone = true; 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++) { //16 = entity ID, 8 = msg length if (msg.BitPosition + 16 + 8 > msg.LengthBits) { string errorMsg = $"Error while reading a message from the server. Entity event data exceeds the size of the buffer (current position: {msg.BitPosition}, length: {msg.LengthBits})."; errorMsg += "\nPrevious entities:"; for (int j = entities.Count - 1; j >= 0; j--) { errorMsg += "\n" + (entities[j] == null ? "NULL" : entities[j].ToString()); } DebugConsole.ThrowError(errorMsg); return(false); } 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; } int msgLength = (int)msg.ReadVariableUInt32(); 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)) ? GUI.Style.Red : Microsoft.Xna.Framework.Color.Yellow); } } else if (entity == null) { DebugConsole.NewMessage( "Received msg " + thisEventID + ", entity " + entityID + " not found", GUI.Style.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.CleanupStackTrace(); errorMsg += "\nPrevious entities:"; for (int j = entities.Count - 2; j >= 0; j--) { errorMsg += "\n" + (entities[j] == null ? "NULL" : entities[j].ToString()); } 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); }
/// <summary> /// Read the events from the message, ignoring ones we've already received /// </summary> public void Read(ServerNetObject type, NetIncomingMessage 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 == 0) { msg.ReadPadBits(); 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 (GameSettings.VerboseLogging) { if (thisEventID != (UInt16)(lastReceivedID + 1)) { DebugConsole.NewMessage( "received msg " + thisEventID + " (waiting for " + (lastReceivedID + 1) + ")", thisEventID < lastReceivedID + 1 ? Microsoft.Xna.Framework.Color.Yellow : 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; if (GameSettings.VerboseLogging) { DebugConsole.NewMessage("received msg " + thisEventID + " (" + entity.ToString() + ")", Microsoft.Xna.Framework.Color.Green); } lastReceivedID++; try { ReadEvent(msg, entity, sendingTime); } catch (Exception e) { 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, "Failed to read event for entity \"" + entity.ToString() + "\"!\n" + e.StackTrace); msg.Position = msgPosition + msgLength * 8; } } msg.ReadPadBits(); } }
/// <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, NetIncomingMessage 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.Position += msgLength * 8; } else { long msgPosition = msg.Position; if (GameSettings.VerboseLogging) { DebugConsole.NewMessage("received msg " + thisEventID + " (" + entity.ToString() + ")", Microsoft.Xna.Framework.Color.Green); } lastReceivedID++; try { ReadEvent(msg, entity, sendingTime); } 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.Position = msgPosition + msgLength * 8; } } msg.ReadPadBits(); } return(true); }