Files
LuaCsForBarotraumaEP/Barotrauma/BarotraumaClient/Source/Networking/NetEntityEvent/ClientEntityEventManager.cs
Joonas Rikkonen 27917ee376 7b471b5...483f2ad
commit 483f2ad4fd9d91b9763d25df592a899cdf39ba67
Author: Joonas Rikkonen <poe.regalis@gmail.com>
Date:   Sun Mar 24 19:19:01 2019 +0200

    Instead of making coilgun bolts continuously deteriorate to give them a lifetime of 5 seconds, simply create a delayed status effect that removes them after 5 seconds of being launched.

commit 00b8d48d4d1d933e76a6c0d7df5320c50dc0a07d
Author: Joonas Rikkonen <poe.regalis@gmail.com>
Date:   Sun Mar 24 19:16:40 2019 +0200

    Wait at least 0.15 seconds before creating a new condition update event for an item. Some rapidly deteriorating items (e.g. coilgun bolt, faraday artifacts) would otherwise cause new events to be created at an excessively high rate.

commit 84e6948a4898dd040b2a84eb5f1ad97c20dfc69f
Author: Joonas Rikkonen <poe.regalis@gmail.com>
Date:   Sun Mar 24 19:14:58 2019 +0200

    Server can send multiple network event packets per update if there's too many events to fit in one packet (up to 4 packets per update).

commit 40797e91d67f965ac6d292367fef5386214abbdb
Author: Joonas Rikkonen <poe.regalis@gmail.com>
Date:   Sun Mar 24 17:34:49 2019 +0200

    NetEntityEvent changes:
    - Don't restrict the number of events per message, but instead write as many events as the packet can fit (up to a maximum of 1024 bytes to leave some space for other types of data (event IDs, chat messages and such)).
    - Decrease the delay after which events can be resent (RTT * 1.5 -> RTT).

commit bfefbb5d7da3ce6a5fe9cb7ff733ec5df37a8a15
Author: Joonas Rikkonen <poe.regalis@gmail.com>
Date:   Sun Mar 24 14:31:03 2019 +0200

    Fixed FixDurationLowSkill & FixDurationHighSkill parameters in Repairable being case-sensitive, causing almost none of the xml values to be used. + Moved client-specific repairable methods to the client project, and server-specific to the server project.

commit 311f67c6c6b8d2cd9f4f4ca820e42316938c4f17
Author: Joonas Rikkonen <poe.regalis@gmail.com>
Date:   Sun Mar 24 14:09:10 2019 +0200

    Fixed "trying to add a dead character to crewmanager" errors when attempting to revive a character killed by some other affliction than internal damage, bleeding or burns. Closes #1341
2019-03-24 19:21:41 +02:00

255 lines
9.5 KiB
C#

using Lidgren.Network;
using System;
using System.Collections.Generic;
namespace Barotrauma.Networking
{
class ClientEntityEventManager : NetEntityEventManager
{
private List<ClientEntityEvent> events;
private UInt16 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<UInt16, float> eventLastSent;
public UInt16 LastReceivedID
{
get { return lastReceivedID; }
}
private UInt16 lastReceivedID;
public bool MidRoundSyncing
{
get { return firstNewID.HasValue; }
}
public ClientEntityEventManager(GameClient client)
{
events = new List<ClientEntityEvent>();
eventLastSent = new Dictionary<UInt16, float>();
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;
}
if (((Entity)entity).Removed)
{
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;
}
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 || serverConnection == null) return;
List<NetEntityEvent> eventsToSync = new List<NetEntityEvent>();
//find the index of the first event the server hasn't received
int startIndex = events.Count;
while (startIndex > 0 &&
NetIdUtils.IdMoreRecent(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 roundtriptime or at all
eventLastSent.TryGetValue(events[i].ID, out float lastSent);
if (lastSent > NetTime.Now - serverConnection.AverageRoundtripTime)
{
continue;
}
eventsToSync.AddRange(events.GetRange(i, events.Count - i));
break;
}
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, out _);
}
private UInt16? firstNewID;
/// <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;
}
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;
firstNewID = null;
events.Clear();
eventLastSent.Clear();
}
}
}