Files
Nitrox/NitroxServer/Communication/Packets/Processors/PlayerJoiningMultiplayerSessionProcessor.cs
2025-07-06 00:23:46 +02:00

129 lines
5.7 KiB
C#

using System.Collections.Generic;
using System.Linq;
using NitroxModel.DataStructures;
using NitroxModel.DataStructures.GameLogic;
using NitroxModel.DataStructures.GameLogic.Entities;
using NitroxModel.DataStructures.Unity;
using NitroxModel.DataStructures.Util;
using NitroxModel.MultiplayerSession;
using NitroxModel.Networking;
using NitroxModel.Packets;
using NitroxModel.Serialization;
using NitroxServer.Communication.Packets.Processors.Abstract;
using NitroxServer.GameLogic;
using NitroxServer.GameLogic.Bases;
using NitroxServer.GameLogic.Entities;
using NitroxServer.Serialization.World;
namespace NitroxServer.Communication.Packets.Processors
{
public class PlayerJoiningMultiplayerSessionProcessor : UnauthenticatedPacketProcessor<PlayerJoiningMultiplayerSession>
{
private readonly PlayerManager playerManager;
private readonly ScheduleKeeper scheduleKeeper;
private readonly StoryManager storyManager;
private readonly World world;
private readonly EntityRegistry entityRegistry;
private readonly SubnauticaServerConfig serverConfig;
private readonly NtpSyncer ntpSyncer;
private readonly SessionSettings sessionSettings;
public PlayerJoiningMultiplayerSessionProcessor(ScheduleKeeper scheduleKeeper, StoryManager storyManager, PlayerManager playerManager, World world, EntityRegistry entityRegistry, SubnauticaServerConfig serverConfig, NtpSyncer ntpSyncer, SessionSettings sessionSettings)
{
this.scheduleKeeper = scheduleKeeper;
this.storyManager = storyManager;
this.playerManager = playerManager;
this.world = world;
this.entityRegistry = entityRegistry;
this.serverConfig = serverConfig;
this.ntpSyncer = ntpSyncer;
this.sessionSettings = sessionSettings;
}
public override void Process(PlayerJoiningMultiplayerSession packet, INitroxConnection connection)
{
Player player = playerManager.PlayerConnected(connection, packet.ReservationKey, out bool wasBrandNewPlayer);
NitroxId assignedEscapePodId = world.EscapePodManager.AssignPlayerToEscapePod(player.Id, out Optional<EscapePodWorldEntity> newlyCreatedEscapePod);
if (wasBrandNewPlayer)
{
player.SubRootId = assignedEscapePodId;
}
if (newlyCreatedEscapePod.HasValue)
{
SpawnEntities spawnNewEscapePod = new(newlyCreatedEscapePod.Value);
playerManager.SendPacketToOtherPlayers(spawnNewEscapePod, player);
}
// Make players on localhost admin by default.
if (connection.Endpoint.Address.IsLocalhost())
{
Log.Info($"Granted admin to '{player.Name}' because they're playing on the host machine");
player.Permissions = Perms.ADMIN;
}
List<SimulatedEntity> simulations = world.EntitySimulation.AssignGlobalRootEntitiesAndGetData(player);
player.Entity = wasBrandNewPlayer ? SetupPlayerEntity(player) : RespawnExistingEntity(player);
List<GlobalRootEntity> globalRootEntities = world.WorldEntityManager.GetGlobalRootEntities(true);
bool isFirstPlayer = playerManager.GetConnectedPlayers().Count == 1;
InitialPlayerSync initialPlayerSync = new(player.GameObjectId,
wasBrandNewPlayer,
assignedEscapePodId,
player.EquippedItems,
player.UsedItems,
player.QuickSlotsBindingIds,
world.GameData.PDAState.GetInitialPDAData(),
world.GameData.StoryGoals.GetInitialStoryGoalData(scheduleKeeper, player),
player.Position,
player.Rotation,
player.SubRootId,
player.Stats,
GetOtherPlayers(player),
globalRootEntities,
simulations,
player.GameMode,
player.Permissions,
wasBrandNewPlayer ? IntroCinematicMode.LOADING : IntroCinematicMode.COMPLETED,
new(new(player.PingInstancePreferences), player.PinnedRecipePreferences.ToList()),
storyManager.GetTimeData(),
isFirstPlayer,
BuildingManager.GetEntitiesOperations(globalRootEntities),
serverConfig.KeepInventoryOnDeath,
sessionSettings
);
player.SendPacket(initialPlayerSync);
}
private IEnumerable<PlayerContext> GetOtherPlayers(Player player)
{
return playerManager.GetConnectedPlayers().Where(p => p != player)
.Select(p => p.PlayerContext);
}
private PlayerWorldEntity SetupPlayerEntity(Player player)
{
NitroxTransform transform = new(player.Position, player.Rotation, NitroxVector3.One);
PlayerWorldEntity playerEntity = new PlayerWorldEntity(transform, 0, null, false, player.GameObjectId, NitroxTechType.None, null, null, new List<Entity>());
entityRegistry.AddOrUpdate(playerEntity);
world.WorldEntityManager.TrackEntityInTheWorld(playerEntity);
return playerEntity;
}
private PlayerWorldEntity RespawnExistingEntity(Player player)
{
if (entityRegistry.TryGetEntityById(player.PlayerContext.PlayerNitroxId, out PlayerWorldEntity playerWorldEntity))
{
return playerWorldEntity;
}
Log.Error($"Unable to find player entity for {player.Name}. Re-creating one");
return SetupPlayerEntity(player);
}
}
}