using NitroxModel.DataStructures.GameLogic; using NitroxModel.DataStructures.GameLogic.Entities.Metadata; using NitroxModel.Packets; using NitroxServer.Communication.Packets.Processors.Abstract; using NitroxServer.GameLogic; using NitroxServer.GameLogic.Entities; namespace NitroxServer.Communication.Packets.Processors; public class EntityMetadataUpdateProcessor : AuthenticatedPacketProcessor { private readonly PlayerManager playerManager; private readonly EntityRegistry entityRegistry; public EntityMetadataUpdateProcessor(PlayerManager playerManager, EntityRegistry entityRegistry) { this.playerManager = playerManager; this.entityRegistry = entityRegistry; } public override void Process(EntityMetadataUpdate packet, Player sendingPlayer) { if (!entityRegistry.TryGetEntityById(packet.Id, out Entity entity)) { Log.Error($"Entity metadata {packet.NewValue.GetType()} updated on an entity unknown to the server {packet.Id}"); return; } if (TryProcessMetadata(sendingPlayer, entity, packet.NewValue)) { entity.Metadata = packet.NewValue; SendUpdateToVisiblePlayers(packet, sendingPlayer, entity); } } private void SendUpdateToVisiblePlayers(EntityMetadataUpdate packet, Player sendingPlayer, Entity entity) { foreach (Player player in playerManager.GetConnectedPlayers()) { bool updateVisibleToPlayer = player.CanSee(entity); // Always sync container/storage metadata to all visible players bool isContainerMetadata = IsContainerRelatedMetadata(packet.NewValue); if (player != sendingPlayer && (updateVisibleToPlayer || isContainerMetadata)) { player.SendPacket(packet); } } } private bool IsContainerRelatedMetadata(EntityMetadata metadata) { // Check if metadata is related to containers/storage return metadata.GetType().Name.Contains("Container") || metadata.GetType().Name.Contains("Storage") || metadata.GetType().Name.Contains("Inventory"); } private bool TryProcessMetadata(Player sendingPlayer, Entity entity, EntityMetadata metadata) { return metadata switch { PlayerMetadata playerMetadata => ProcessPlayerMetadata(sendingPlayer, entity, playerMetadata), // Always allow container/storage metadata updates for proper sync _ when IsContainerRelatedMetadata(metadata) => true, // Allow metadata updates from any player by default _ => true }; } private bool ProcessPlayerMetadata(Player sendingPlayer, Entity entity, PlayerMetadata metadata) { if (sendingPlayer.GameObjectId == entity.Id) { sendingPlayer.EquippedItems.Clear(); foreach (PlayerMetadata.EquippedItem item in metadata.EquippedItems) { sendingPlayer.EquippedItems.Add(item.Slot, item.Id); } return true; } Log.WarnOnce($"Player {sendingPlayer.Name} tried updating metadata of another player's entity {entity.Id}"); return false; } }