Files
Eero 9474f7654c CBT2.0.1 Fix event reset and temp cell clearing logic
Changed ResetReceivedEvents from partial to regular method in EntitySpawner to ensure proper event queue clearing. Updated Level.cs to clear tempCellsLocal instead of tempCells, addressing potential issues with thread-local storage.
2025-12-29 18:37:13 +08:00

102 lines
4.5 KiB
C#

using Barotrauma.Items.Components;
using Barotrauma.Networking;
using System.Collections.Concurrent;
using System.Collections.Generic;
namespace Barotrauma
{
partial class EntitySpawner : Entity, IServerSerializable
{
/// <summary>
/// Thread-safe queue for received entity spawn/remove events from the server.
/// </summary>
private readonly ConcurrentQueue<(Entity entity, bool isRemoval)> receivedEventsQueue = new ConcurrentQueue<(Entity entity, bool isRemoval)>();
/// <summary>
/// Gets a thread-safe snapshot of received events.
/// </summary>
public IEnumerable<(Entity entity, bool isRemoval)> GetReceivedEventsSnapshot()
{
return receivedEventsQueue.ToArray();
}
/// <summary>
/// Clears all received events from the queue.
/// </summary>
void ResetReceivedEvents()
{
while (receivedEventsQueue.TryDequeue(out _)) { }
}
public void ClientEventRead(IReadMessage message, float sendingTime)
{
bool remove = message.ReadBoolean();
if (remove)
{
ushort entityId = message.ReadUInt16();
var entity = FindEntityByID(entityId);
if (entity != null)
{
DebugConsole.Log($"Received entity removal message for \"{entity}\".");
if (entity is Item item && item.Container?.GetComponent<Deconstructor>() != null)
{
if (item.Prefab.ContentPackage == ContentPackageManager.VanillaCorePackage &&
/* we don't need info of every deconstructed item, we can get a good sample size just by logging 5% */
Rand.Range(0.0f, 1.0f) < 0.05f)
{
GameAnalyticsManager.AddDesignEvent("ItemDeconstructed:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "none".ToIdentifier()) + ":" + item.Prefab.Identifier);
}
}
entity.Remove();
}
else
{
DebugConsole.Log("Received entity removal message for ID " + entityId + ". Entity with a matching ID not found.");
}
receivedEventsQueue.Enqueue((entity, true));
}
else
{
switch (message.ReadByte())
{
case (byte)SpawnableType.Item:
var newItem = Item.ReadSpawnData(message, true);
if (newItem == null)
{
DebugConsole.ThrowError("Received an item spawn message, but spawning the item failed.");
}
else
{
if (newItem.Container?.GetComponent<Fabricator>() != null)
{
if (newItem.Prefab.ContentPackage == ContentPackageManager.VanillaCorePackage &&
/* we don't need info of every fabricated item, we can get a good sample size just by logging 5% */
Rand.Range(0.0f, 1.0f) < 0.05f)
{
GameAnalyticsManager.AddDesignEvent("ItemFabricated:" + (GameMain.GameSession?.GameMode?.Preset.Identifier ?? "none".ToIdentifier()) + ":" + newItem.Prefab.Identifier);
}
}
receivedEventsQueue.Enqueue((newItem, false));
}
break;
case (byte)SpawnableType.Character:
var character = Character.ReadSpawnData(message);
if (character == null)
{
DebugConsole.ThrowError("Received character spawn message, but spawning the character failed.");
}
else
{
receivedEventsQueue.Enqueue((character, false));
}
break;
default:
DebugConsole.ThrowError("Received invalid entity spawn message (unknown spawnable type)");
break;
}
}
}
}
}