Updated CreateEvent() to make it thread safe

This commit is contained in:
NotAlwaysTrue
2025-12-22 00:07:01 +08:00
parent f87e943a1a
commit 220ccb2eac
@@ -1,8 +1,12 @@
using Barotrauma.Extensions; using Barotrauma.Extensions;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using static Barotrauma.EosInterface.Ownership;
namespace Barotrauma.Networking namespace Barotrauma.Networking
{ {
@@ -44,6 +48,8 @@ namespace Barotrauma.Networking
class ServerEntityEventManager : NetEntityEventManager class ServerEntityEventManager : NetEntityEventManager
{ {
static public ServerEntityEventManager SEM;
private readonly List<ServerEntityEvent> events; private readonly List<ServerEntityEvent> events;
//list of unique events (i.e. !IsDuplicate) created during the round //list of unique events (i.e. !IsDuplicate) created during the round
@@ -102,6 +108,20 @@ namespace Barotrauma.Networking
private readonly GameServer server; private readonly GameServer server;
private double lastEventCountHighWarning; private double lastEventCountHighWarning;
private class PendingCreateEvent
{
public IServerSerializable Entity;
public NetEntityEvent.IData Data;
public PendingCreateEvent(IServerSerializable entity, NetEntityEvent.IData data)
{
Entity = entity;
Data = data;
}
}
private readonly ConcurrentQueue<PendingCreateEvent> pendingCreateQueue;
private readonly Task createEventTask;
public ServerEntityEventManager(GameServer server) public ServerEntityEventManager(GameServer server)
{ {
@@ -113,18 +133,38 @@ namespace Barotrauma.Networking
uniqueEvents = new List<ServerEntityEvent>(); uniqueEvents = new List<ServerEntityEvent>();
pendingCreateQueue = new ConcurrentQueue<PendingCreateEvent>();
lastWarningTime = -10.0; lastWarningTime = -10.0;
SEM = this;
createEventTask = Task.Run(async () => await CreateEventProcessorLoop());
}
private Task CreateEventProcessorLoop()
{
while (true)
{
ProcessPendingCreateEvents();
}
} }
public void CreateEvent(IServerSerializable entity, NetEntityEvent.IData extraData = null) private void ProcessPendingCreateEvents()
{ {
if (!ValidateEntity(entity)) { return; } // Dequeue and process all pending events currently in the queue.
// Use a lock to synchronize modifications to shared lists / ID.
while (pendingCreateQueue.TryDequeue(out PendingCreateEvent pending))
{
// The original CreateEvent logic (mostly unchanged) but executed under a lock
if (pending == null || pending.Entity == null) { continue; }
var entity = pending.Entity;
var extraData = pending.Data;
var newEvent = new ServerEntityEvent(entity, (UInt16)(ID + 1)); var newEvent = new ServerEntityEvent(entity, (UInt16)(ID + 1));
if (extraData != null) newEvent.SetData(extraData); if (extraData != null) newEvent.SetData(extraData);
bool inGameClientsPresent = server.ConnectedClients.Count(c => c.InGame) > 0; bool inGameClientsPresent = server.ConnectedClients.Count(c => c.InGame) > 0;
//remove old events that have been sent to all clients, they are redundant now //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 // 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 // and events less than 15 seconds old to give disconnected clients a bit of time to reconnect without getting desynced
@@ -135,12 +175,19 @@ namespace Barotrauma.Networking
e.CreateTime < Timing.TotalTime - server.ServerSettings.EventRemovalTime); e.CreateTime < Timing.TotalTime - server.ServerSettings.EventRemovalTime);
} }
bool duplicateFound = false;
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
// -> no need to add a new one // -> no need to add a new one
if (events[i].IsDuplicate(newEvent) && !events[i].Sent) return; if (events[i].IsDuplicate(newEvent) && !events[i].Sent)
{
duplicateFound = true;
break;
} }
}
if (duplicateFound) { continue; }
ID++; ID++;
@@ -155,9 +202,17 @@ namespace Barotrauma.Networking
uniqueEvents.Add(uniqueEvent); uniqueEvents.Add(uniqueEvent);
} }
} }
}
public void CreateEvent(IServerSerializable entity, NetEntityEvent.IData extraData = null)
{
if (!ValidateEntity(entity)) { return; }
// enqueue and let background task handle the rest
pendingCreateQueue.Enqueue(new PendingCreateEvent(entity, extraData));
}
public void Update(List<Client> clients) public void Update(List<Client> clients)
{ {
foreach (BufferedEvent bufferedEvent in bufferedEvents) foreach (BufferedEvent bufferedEvent in bufferedEvents)
{ {
if (bufferedEvent.Character == null || bufferedEvent.Character.IsDead) if (bufferedEvent.Character == null || bufferedEvent.Character.IsDead)