EntityEventManager sends an empty event with an ID of 0 if the entity doesn't exist anymore when writing the event. The recipient should simply skip over these messages and read the next one, but clients only did so if the event is the next unreceived one, causing the rest of the messages to be read incorrectly (which could lead to various sorts of problems in addition to the header and buffer size errors). The server didn't deal with the empty events correctly either, it increased the last received ID even if the received event was not the one the server is waiting for, potentially causing other non-empty events to be ignored. + Added an error message if the size of an NetEntityEvent is larger than 255 bytes. Not only is that excessively large for an event, but the length of the event is written as a byte and larger ones may again cause messages to be read incorrectly. Most events should be nowhere near 255 bytes, but now that the descriptions and tags of a spawned item are included in the item spawn messages there's the possibility that some events are too large.
87 lines
3.4 KiB
C#
87 lines
3.4 KiB
C#
using Lidgren.Network;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Barotrauma.Networking
|
|
{
|
|
abstract class NetEntityEventManager
|
|
{
|
|
public const int MaxEventBufferLength = 1024;
|
|
public const int MaxEventsPerWrite = 64;
|
|
|
|
/// <summary>
|
|
/// Write the events to the outgoing message. The recipient parameter is only needed for ServerEntityEventManager
|
|
/// </summary>
|
|
protected void Write(NetOutgoingMessage msg, List<NetEntityEvent> eventsToSync, Client recipient = null)
|
|
{
|
|
//write into a temporary buffer so we can write the number of events before the actual data
|
|
NetBuffer tempBuffer = new NetBuffer();
|
|
|
|
int eventCount = 0;
|
|
foreach (NetEntityEvent e in eventsToSync)
|
|
{
|
|
//write into a temporary buffer so we can write the length before the actual data
|
|
NetBuffer tempEventBuffer = new NetBuffer();
|
|
try
|
|
{
|
|
WriteEvent(tempEventBuffer, e, recipient);
|
|
}
|
|
|
|
catch (Exception exception)
|
|
{
|
|
DebugConsole.ThrowError("Failed to write an event for the entity \"" + e.Entity + "\"", exception);
|
|
|
|
//write an empty event to avoid messing up IDs
|
|
//(otherwise the clients might read the next event in the message and think its ID
|
|
//is consecutive to the previous one, even though we skipped over this broken event)
|
|
tempBuffer.Write((UInt16)0);
|
|
tempBuffer.WritePadBits();
|
|
eventCount++;
|
|
continue;
|
|
}
|
|
|
|
if (msg.LengthBytes + tempBuffer.LengthBytes + tempEventBuffer.LengthBytes > NetPeerConfiguration.kDefaultMTU - 20)
|
|
{
|
|
//no more room in this packet
|
|
break;
|
|
}
|
|
|
|
if (tempEventBuffer.LengthBytes > 255)
|
|
{
|
|
DebugConsole.ThrowError("Too much data in network event for entity \"" + e.Entity.ToString() + "\" (" + tempEventBuffer.LengthBytes + " bytes");
|
|
}
|
|
|
|
//the ID has been taken by another entity (the original entity has been removed) -> write an empty event
|
|
if (Entity.FindEntityByID(e.Entity.ID) != e.Entity)
|
|
{
|
|
//technically the clients don't have any use for these, but removing events and shifting the IDs of all
|
|
//consecutive ones is so error-prone that I think this is a safer option
|
|
tempBuffer.Write((UInt16)0);
|
|
tempBuffer.WritePadBits();
|
|
}
|
|
else
|
|
{
|
|
tempBuffer.Write((UInt16)e.Entity.ID);
|
|
tempBuffer.Write((byte)tempEventBuffer.LengthBytes);
|
|
tempBuffer.Write(tempEventBuffer);
|
|
tempBuffer.WritePadBits();
|
|
}
|
|
|
|
eventCount++;
|
|
}
|
|
|
|
if (eventCount > 0)
|
|
{
|
|
msg.Write(eventsToSync[0].ID);
|
|
msg.Write((byte)eventCount);
|
|
msg.Write(tempBuffer);
|
|
}
|
|
}
|
|
|
|
protected virtual void WriteEvent(NetBuffer buffer, NetEntityEvent entityEvent, Client recipient = null)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
}
|