The server maintains a list of unique EntityEvents created during the round and sends them to clients who join mid-round
This commit is contained in:
@@ -480,7 +480,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
memPos.Insert(index, posInfo);
|
memPos.Insert(index, posInfo);
|
||||||
break;
|
break;
|
||||||
case ServerNetObject.ENTITY_STATE:
|
case ServerNetObject.ENTITY_EVENT:
|
||||||
bool isInventoryUpdate = msg.ReadBoolean();
|
bool isInventoryUpdate = msg.ReadBoolean();
|
||||||
|
|
||||||
if (isInventoryUpdate)
|
if (isInventoryUpdate)
|
||||||
|
|||||||
@@ -47,6 +47,10 @@ namespace Barotrauma.Networking
|
|||||||
public float ChatSpamTimer;
|
public float ChatSpamTimer;
|
||||||
public int ChatSpamCount;
|
public int ChatSpamCount;
|
||||||
|
|
||||||
|
public bool NeedsMidRoundSync;
|
||||||
|
//how many unique events the client missed before joining the server
|
||||||
|
public UInt32 UnreceivedEntityEventCount;
|
||||||
|
|
||||||
private List<Client> kickVoters;
|
private List<Client> kickVoters;
|
||||||
|
|
||||||
//when was a specific entity event last sent to the client
|
//when was a specific entity event last sent to the client
|
||||||
@@ -73,6 +77,9 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
lastRecvEntitySpawnID = 0;
|
lastRecvEntitySpawnID = 0;
|
||||||
lastRecvEntityEventID = 0;
|
lastRecvEntityEventID = 0;
|
||||||
|
|
||||||
|
UnreceivedEntityEventCount = 0;
|
||||||
|
NeedsMidRoundSync = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int KickVoteCount
|
public int KickVoteCount
|
||||||
|
|||||||
@@ -900,8 +900,9 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
inc.ReadPadBits();
|
inc.ReadPadBits();
|
||||||
break;
|
break;
|
||||||
case ServerNetObject.ENTITY_STATE:
|
case ServerNetObject.ENTITY_EVENT:
|
||||||
entityEventManager.Read(inc, sendingTime);
|
case ServerNetObject.ENTITY_EVENT_INITIAL:
|
||||||
|
entityEventManager.Read(objHeader, inc, sendingTime);
|
||||||
break;
|
break;
|
||||||
case ServerNetObject.CHAT_MESSAGE:
|
case ServerNetObject.CHAT_MESSAGE:
|
||||||
ChatMessage.ClientRead(inc);
|
ChatMessage.ClientRead(inc);
|
||||||
|
|||||||
@@ -53,6 +53,12 @@ namespace Barotrauma.Networking
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ServerEntityEventManager EntityEventManager
|
||||||
|
{
|
||||||
|
get { return entityEventManager; }
|
||||||
|
}
|
||||||
|
|
||||||
public GameServer(string name, int port, bool isPublic = false, string password = "", bool attemptUPnP = false, int maxPlayers = 10)
|
public GameServer(string name, int port, bool isPublic = false, string password = "", bool attemptUPnP = false, int maxPlayers = 10)
|
||||||
{
|
{
|
||||||
name = name.Replace(":", "");
|
name = name.Replace(":", "");
|
||||||
@@ -527,6 +533,7 @@ namespace Barotrauma.Networking
|
|||||||
//game already started -> send start message immediately
|
//game already started -> send start message immediately
|
||||||
if (gameStarted)
|
if (gameStarted)
|
||||||
{
|
{
|
||||||
|
connectedClient.NeedsMidRoundSync = true;
|
||||||
SendStartMessage(roundStartSeed, Submarine.MainSub, GameMain.GameSession.gameMode.Preset, connectedClient);
|
SendStartMessage(roundStartSeed, Submarine.MainSub, GameMain.GameSession.gameMode.Preset, connectedClient);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -606,7 +613,16 @@ namespace Barotrauma.Networking
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gameStarted) c.inGame = true;
|
if (gameStarted)
|
||||||
|
{
|
||||||
|
if (!c.inGame && c.NeedsMidRoundSync)
|
||||||
|
{
|
||||||
|
//client joined mid-round and has just started up the game
|
||||||
|
//check which unique messages they've missed
|
||||||
|
entityEventManager.InitClientMidRoundSync(c);
|
||||||
|
}
|
||||||
|
c.inGame = true;
|
||||||
|
}
|
||||||
|
|
||||||
ClientNetObject objHeader;
|
ClientNetObject objHeader;
|
||||||
while ((objHeader = (ClientNetObject)inc.ReadByte()) != ClientNetObject.END_OF_MESSAGE)
|
while ((objHeader = (ClientNetObject)inc.ReadByte()) != ClientNetObject.END_OF_MESSAGE)
|
||||||
@@ -622,7 +638,21 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
//last msgs we've created/sent, the client IDs should never be higher than these
|
//last msgs we've created/sent, the client IDs should never be higher than these
|
||||||
UInt32 lastEntitySpawnID = Entity.Spawner.NetStateID;
|
UInt32 lastEntitySpawnID = Entity.Spawner.NetStateID;
|
||||||
UInt32 lastEntityEventID = entityEventManager.Events.Count() == 0 ? 0 : entityEventManager.Events.Last().ID;
|
UInt32 lastEntityEventID = entityEventManager.Events.Count == 0 ? 0 : entityEventManager.Events.Last().ID;
|
||||||
|
|
||||||
|
if (c.NeedsMidRoundSync)
|
||||||
|
{
|
||||||
|
//received all the old events -> client in sync, we can switch to normal behavior
|
||||||
|
if (lastRecvEntityEventID >= c.UnreceivedEntityEventCount - 1 ||
|
||||||
|
c.UnreceivedEntityEventCount == 0)
|
||||||
|
{
|
||||||
|
c.NeedsMidRoundSync = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lastEntityEventID = (uint)c.UnreceivedEntityEventCount - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
//client thinks they've received a msg we haven't sent yet (corrupted packet, msg read/written incorrectly?)
|
//client thinks they've received a msg we haven't sent yet (corrupted packet, msg read/written incorrectly?)
|
||||||
@@ -636,11 +666,10 @@ namespace Barotrauma.Networking
|
|||||||
DebugConsole.ThrowError("client.lastRecvEntityEventID > lastEntityEventID");
|
DebugConsole.ThrowError("client.lastRecvEntityEventID > lastEntityEventID");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
c.lastRecvChatMsgID = Math.Min(Math.Max(c.lastRecvChatMsgID, lastRecvChatMsgID), c.lastChatMsgQueueID);
|
c.lastRecvChatMsgID = NetIdUtils.Clamp(c.lastRecvChatMsgID, lastRecvChatMsgID, c.lastChatMsgQueueID);
|
||||||
c.lastRecvEntitySpawnID = Math.Min(Math.Max(c.lastRecvEntitySpawnID, lastRecvEntitySpawnID), lastEntitySpawnID);
|
c.lastRecvEntitySpawnID = Math.Min(Math.Max(c.lastRecvEntitySpawnID, lastRecvEntitySpawnID), lastEntitySpawnID);
|
||||||
c.lastRecvEntityEventID = Math.Min(Math.Max(c.lastRecvEntityEventID, lastRecvEntityEventID), lastEntityEventID);
|
c.lastRecvEntityEventID = Math.Min(Math.Max(c.lastRecvEntityEventID, lastRecvEntityEventID), lastEntityEventID);
|
||||||
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case ClientNetObject.CHAT_MESSAGE:
|
case ClientNetObject.CHAT_MESSAGE:
|
||||||
ChatMessage.ServerRead(inc, c);
|
ChatMessage.ServerRead(inc, c);
|
||||||
|
|||||||
@@ -101,8 +101,17 @@ namespace Barotrauma.Networking
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read the events from the message, ignoring ones we've already received
|
/// Read the events from the message, ignoring ones we've already received
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Read(NetIncomingMessage msg, float sendingTime)
|
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();
|
UInt32 firstEventID = msg.ReadUInt32();
|
||||||
int eventCount = msg.ReadByte();
|
int eventCount = msg.ReadByte();
|
||||||
|
|
||||||
@@ -148,6 +157,15 @@ namespace Barotrauma.Networking
|
|||||||
}
|
}
|
||||||
msg.ReadPadBits();
|
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)
|
protected override void WriteEvent(NetBuffer buffer, NetEntityEvent entityEvent, Client recipient = null)
|
||||||
@@ -160,7 +178,7 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
protected void ReadEvent(NetIncomingMessage buffer, IServerSerializable entity, float sendingTime)
|
protected void ReadEvent(NetIncomingMessage buffer, IServerSerializable entity, float sendingTime)
|
||||||
{
|
{
|
||||||
entity.ClientRead(ServerNetObject.ENTITY_STATE, buffer, sendingTime);
|
entity.ClientRead(ServerNetObject.ENTITY_EVENT, buffer, sendingTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ namespace Barotrauma.Networking
|
|||||||
{
|
{
|
||||||
private List<ServerEntityEvent> events;
|
private List<ServerEntityEvent> events;
|
||||||
|
|
||||||
|
//list of unique events (i.e. !IsDuplicate) created during the round
|
||||||
|
//used for syncing clients who join mid-round
|
||||||
|
private List<ServerEntityEvent> uniqueEvents;
|
||||||
|
|
||||||
private UInt32 lastSentToAll;
|
private UInt32 lastSentToAll;
|
||||||
|
|
||||||
public List<ServerEntityEvent> Events
|
public List<ServerEntityEvent> Events
|
||||||
@@ -56,6 +60,8 @@ namespace Barotrauma.Networking
|
|||||||
this.server = server;
|
this.server = server;
|
||||||
|
|
||||||
bufferedEvents = new List<BufferedEvent>();
|
bufferedEvents = new List<BufferedEvent>();
|
||||||
|
|
||||||
|
uniqueEvents = new List<ServerEntityEvent>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CreateEvent(IServerSerializable entity, object[] extraData = null)
|
public void CreateEvent(IServerSerializable entity, object[] extraData = null)
|
||||||
@@ -69,7 +75,7 @@ namespace Barotrauma.Networking
|
|||||||
var newEvent = new ServerEntityEvent(entity, ID + 1);
|
var newEvent = new ServerEntityEvent(entity, ID + 1);
|
||||||
if (extraData != null) newEvent.SetData(extraData);
|
if (extraData != null) newEvent.SetData(extraData);
|
||||||
|
|
||||||
events.RemoveAll(e => e.ID <= lastSentToAll && e.IsDuplicate(newEvent)); //remove outdated events, they are redundant now
|
events.RemoveAll(e => e.ID <= lastSentToAll); //remove events that , they are redundant now
|
||||||
for (int i = events.Count - 1; i >= 0; i--)
|
for (int i = events.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
//we already have an identical event that's waiting to be sent
|
//we already have an identical event that's waiting to be sent
|
||||||
@@ -80,6 +86,15 @@ namespace Barotrauma.Networking
|
|||||||
ID++;
|
ID++;
|
||||||
|
|
||||||
events.Add(newEvent);
|
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, (uint)uniqueEvents.Count);
|
||||||
|
uniqueEvent.SetData(extraData);
|
||||||
|
|
||||||
|
uniqueEvents.Add(uniqueEvent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update(List<Client> clients)
|
public void Update(List<Client> clients)
|
||||||
@@ -140,30 +155,16 @@ namespace Barotrauma.Networking
|
|||||||
{
|
{
|
||||||
if (events.Count == 0) return;
|
if (events.Count == 0) return;
|
||||||
|
|
||||||
List<NetEntityEvent> eventsToSync = new List<NetEntityEvent>();
|
List<NetEntityEvent> eventsToSync = null;
|
||||||
|
if (client.NeedsMidRoundSync)
|
||||||
//find the index of the first event the client hasn't received
|
|
||||||
int startIndex = events.Count;
|
|
||||||
while (startIndex > 0 &&
|
|
||||||
events[startIndex-1].ID > client.lastRecvEntityEventID)
|
|
||||||
{
|
{
|
||||||
startIndex--;
|
eventsToSync = GetEventsToSync(client, uniqueEvents);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eventsToSync = GetEventsToSync(client, events);
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
client.entityEventLastSent.TryGetValue(events[i].ID, out lastSent);
|
|
||||||
|
|
||||||
if (lastSent > NetTime.Now - client.Connection.AverageRoundtripTime * 1.5f)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
eventsToSync.AddRange(events.GetRange(i, events.Count - i));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (eventsToSync.Count == 0) return;
|
if (eventsToSync.Count == 0) return;
|
||||||
|
|
||||||
//too many events for one packet
|
//too many events for one packet
|
||||||
@@ -178,8 +179,73 @@ namespace Barotrauma.Networking
|
|||||||
client.entityEventLastSent[entityEvent.ID] = (float)NetTime.Now;
|
client.entityEventLastSent[entityEvent.ID] = (float)NetTime.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
msg.Write((byte)ServerNetObject.ENTITY_STATE);
|
if (client.NeedsMidRoundSync)
|
||||||
Write(msg, eventsToSync, client);
|
{
|
||||||
|
msg.Write((byte)ServerNetObject.ENTITY_EVENT_INITIAL);
|
||||||
|
//how many (unique) events the clients had missed before joining
|
||||||
|
msg.Write(client.UnreceivedEntityEventCount);
|
||||||
|
|
||||||
|
//ID of the first event sent after the client joined
|
||||||
|
//(after the client has been synced they'll switch their lastReceivedID
|
||||||
|
//to the one before this, and the eventmanagers will start to function "normally")
|
||||||
|
msg.Write(events.Count == 0 ? 0 : events[events.Count - 1].ID);
|
||||||
|
Write(msg, eventsToSync, client);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
msg.Write((byte)ServerNetObject.ENTITY_EVENT);
|
||||||
|
Write(msg, eventsToSync, client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a list of events that should be sent to the client from the eventList
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="client"></param>
|
||||||
|
/// <param name="eventList"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private List<NetEntityEvent> GetEventsToSync(Client client, List<ServerEntityEvent> eventList)
|
||||||
|
{
|
||||||
|
List<NetEntityEvent> eventsToSync = new List<NetEntityEvent>();
|
||||||
|
|
||||||
|
//find the index of the first event the client hasn't received
|
||||||
|
int startIndex = eventList.Count;
|
||||||
|
while (startIndex > 0 &&
|
||||||
|
eventList[startIndex - 1].ID > client.lastRecvEntityEventID)
|
||||||
|
{
|
||||||
|
startIndex--;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = startIndex; i < eventList.Count; i++)
|
||||||
|
{
|
||||||
|
//find the first event that hasn't been sent in 1.5 * roundtriptime or at all
|
||||||
|
float lastSent = 0;
|
||||||
|
client.entityEventLastSent.TryGetValue(eventList[i].ID, out lastSent);
|
||||||
|
|
||||||
|
if (lastSent > NetTime.Now - client.Connection.AverageRoundtripTime * 1.5f)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
eventsToSync.AddRange(eventList.GetRange(i, eventList.Count - i));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return eventsToSync;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InitClientMidRoundSync(Client client)
|
||||||
|
{
|
||||||
|
if (uniqueEvents.Count > 0)
|
||||||
|
{
|
||||||
|
client.UnreceivedEntityEventCount = (UInt32)uniqueEvents.Count;
|
||||||
|
client.NeedsMidRoundSync = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
client.UnreceivedEntityEventCount = 0;
|
||||||
|
client.NeedsMidRoundSync = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -248,6 +314,8 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
bufferedEvents.Clear();
|
bufferedEvents.Clear();
|
||||||
|
|
||||||
|
uniqueEvents.Clear();
|
||||||
|
|
||||||
server.ConnectedClients.ForEach(c => c.entityEventLastSent.Clear());
|
server.ConnectedClients.ForEach(c => c.entityEventLastSent.Clear());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,8 @@ namespace Barotrauma.Networking
|
|||||||
CHAT_MESSAGE,
|
CHAT_MESSAGE,
|
||||||
VOTE,
|
VOTE,
|
||||||
ENTITY_POSITION,
|
ENTITY_POSITION,
|
||||||
ENTITY_STATE,
|
ENTITY_EVENT,
|
||||||
|
ENTITY_EVENT_INITIAL,
|
||||||
|
|
||||||
ENTITY_SPAWN
|
ENTITY_SPAWN
|
||||||
}
|
}
|
||||||
@@ -127,7 +128,6 @@ namespace Barotrauma.Networking
|
|||||||
get { return inGameHUD; }
|
get { return inGameHUD; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public virtual List<Client> ConnectedClients
|
public virtual List<Client> ConnectedClients
|
||||||
{
|
{
|
||||||
get { return null; }
|
get { return null; }
|
||||||
|
|||||||
Reference in New Issue
Block a user