Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Join queue despaghettification #2006

Open
wants to merge 36 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
b4e462a
Draft 1
SpaceMonkeyy86 Dec 6, 2022
2521bac
Finished join queue loop
SpaceMonkeyy86 Dec 8, 2022
d6404e6
Enter join queue after base game finishes loading
SpaceMonkeyy86 Dec 8, 2022
02ee6c6
Changed OnLateUpdate to UpdatePings (#1913)
Coding-Hen Dec 10, 2022
e43c176
Merge master
SpaceMonkeyy86 Jan 10, 2023
f161aac
Merge master (again)
SpaceMonkeyy86 Mar 18, 2023
4cb25d9
Move initial sync code to the new spot after the merge reset it
SpaceMonkeyy86 Mar 18, 2023
eddd834
Fix broken in-game log for join queue stage
SpaceMonkeyy86 Mar 18, 2023
5dc17a9
Use WaitScreen to stay on loading screen
SpaceMonkeyy86 Mar 18, 2023
1717850
Reduce default initial sync timeout
SpaceMonkeyy86 Mar 18, 2023
ef14264
Merge branch 'master' into join-queue-fix
SpaceMonkeyy86 Mar 22, 2023
86010a6
Remove unnecessary using
SpaceMonkeyy86 Apr 15, 2023
d613ec9
Merge master (this is so not going to work)
SpaceMonkeyy86 Jan 2, 2024
17e9c09
It almost worked (it works now)
SpaceMonkeyy86 Jan 2, 2024
5c9ffef
Show modal when timed out
SpaceMonkeyy86 Feb 7, 2024
5fd6aa5
Merge master
SpaceMonkeyy86 Feb 7, 2024
932edb9
Tweaks and fixes
SpaceMonkeyy86 Feb 8, 2024
708c8cf
Instantly continue the queue if a client disconnects while syncing
SpaceMonkeyy86 Feb 9, 2024
2eb654a
Send info about the queue to the client
SpaceMonkeyy86 Feb 9, 2024
1b5920f
Remove join queue "lobby"
SpaceMonkeyy86 Feb 12, 2024
708f753
Some cleanups
SpaceMonkeyy86 Feb 12, 2024
5d618c7
Merge branch 'master' into join-queue-fix
SpaceMonkeyy86 Feb 12, 2024
34b2c18
Revert a file that wasn't actually modified
SpaceMonkeyy86 Feb 12, 2024
906d1c6
I love merge conflicts
SpaceMonkeyy86 Mar 20, 2024
131bac7
Merge branch 'master' into join-queue-fix
SpaceMonkeyy86 Aug 14, 2024
eb1ce78
Fix another conflict (did not test but it's probably fine)
SpaceMonkeyy86 Sep 2, 2024
099f8f2
Fix merge conflicts
SpaceMonkeyy86 Nov 12, 2024
201d221
Merge branch 'join-queue-fix' of https://github.com/TwinBuilderOne/Ni…
SpaceMonkeyy86 Nov 12, 2024
a8f742f
Merge branch 'master' into join-queue-fix
SpaceMonkeyy86 Nov 16, 2024
76e5a9d
Merge branch 'master' into join-queue-fix
SpaceMonkeyy86 Jan 7, 2025
efba962
Apply Jannify's patch
SpaceMonkeyy86 Jan 7, 2025
ef9d8d5
Small tweaks
SpaceMonkeyy86 Jan 7, 2025
a1cf75b
Fix DI for JoiningManager
SpaceMonkeyy86 Jan 7, 2025
4de4fa7
Handle case where client receives a timeout after finishing the sync
SpaceMonkeyy86 Jan 7, 2025
8043c13
Remove thread unsafety when a joining player disconnects
SpaceMonkeyy86 Jan 12, 2025
95fc884
Some cleanup
SpaceMonkeyy86 Jan 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using NitroxClient.Communication.Abstract;
using NitroxModel.Packets;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using NitroxClient.Communication.Abstract;
Expand Down Expand Up @@ -77,26 +77,13 @@ public void ProcessSessionPolicy(MultiplayerSessionPolicy policy)

public void RequestSessionReservation(PlayerSettings playerSettings, AuthenticationContext authenticationContext)
{
// If a reservation has already been sent (in which case the client is enqueued in the join queue)
if (CurrentState.CurrentStage == MultiplayerSessionConnectionStage.AWAITING_SESSION_RESERVATION)
{
Log.InGame(Language.main.Get("Nitrox_Waiting"));
return;
}

PlayerSettings = playerSettings;
AuthenticationContext = authenticationContext;
CurrentState.NegotiateReservationAsync(this);
}

public void ProcessReservationResponsePacket(MultiplayerSessionReservation reservation)
{
if (reservation.ReservationState == MultiplayerSessionReservationState.ENQUEUED_IN_JOIN_QUEUE)
{
Log.InGame(Language.main.Get("Nitrox_Waiting"));
return;
}

Reservation = reservation;
CurrentState.NegotiateReservationAsync(this);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using NitroxClient.Communication.Abstract;
Expand Down Expand Up @@ -31,6 +31,7 @@ public InitialPlayerSyncProcessor(IPacketSender packetSender, IEnumerable<Initia
public override void Process(InitialPlayerSync packet)
{
this.packet = packet;
Multiplayer.Main.InsideJoinQueue = false;
loadingMultiplayerWaitItem = WaitScreen.Add(Language.main.Get("Nitrox_SyncingWorld"));
cumulativeProcessorsRan = 0;
Multiplayer.Main.StartCoroutine(ProcessInitialSyncPacket(this, null));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.Collections;
using NitroxClient.Communication.Packets.Processors.Abstract;
using NitroxClient.MonoBehaviours;
using NitroxModel.Packets;
using UnityEngine;

namespace NitroxClient.Communication.Packets.Processors;

public class PlayerSyncTimeoutProcessor : ClientPacketProcessor<PlayerSyncTimeout>
{
public override void Process(PlayerSyncTimeout packet)
{
Multiplayer.Main.StartCoroutine(TimeoutRoutine());
SpaceMonkeyy86 marked this conversation as resolved.
Show resolved Hide resolved
}

private IEnumerator TimeoutRoutine()
{
Log.InGame("Error: Initial sync timeout, closing game");
SpaceMonkeyy86 marked this conversation as resolved.
Show resolved Hide resolved
yield return new WaitForSecondsRealtime(5);
IngameMenu.main.QuitGame(false);
}
}
6 changes: 6 additions & 0 deletions NitroxClient/MonoBehaviours/Multiplayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public class Multiplayer : MonoBehaviour
private GameLogic.Terrain terrain;

public bool InitialSyncCompleted { get; set; }
public bool InsideJoinQueue { get; set; }

/// <summary>
/// True if multiplayer is loaded and client is connected to a server.
Expand Down Expand Up @@ -101,6 +102,11 @@ public static IEnumerator LoadAsync()
yield return Main.StartCoroutine(Main.StartSession());
WaitScreen.Remove(item);

WaitScreen.ManualWaitItem joinQueueItem = WaitScreen.Add(Language.main.Get("Nitrox_Waiting"));
Main.InsideJoinQueue = true;
yield return new WaitUntil(() => !Main.InsideJoinQueue);
SpaceMonkeyy86 marked this conversation as resolved.
Show resolved Hide resolved
WaitScreen.Remove(joinQueueItem);

yield return new WaitUntil(() => Main.InitialSyncCompleted);

SetLoadingComplete();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,8 @@ public enum MultiplayerSessionReservationState
[Description("The server is using hardcore gamemode, player is dead.")]
HARDCORE_PLAYER_DEAD = 1 << 4,

[Description("Another user is currently joining the server.")]
ENQUEUED_IN_JOIN_QUEUE = 1 << 5,

[Description("The player name is invalid, It must not contain any space or doubtful characters\n Allowed characters : A-Z a-z 0-9 _ . -\nLength : [3, 25]")]
INCORRECT_USERNAME = 1 << 6
INCORRECT_USERNAME = 1 << 5
}

public static class MultiplayerSessionReservationStateExtensions
Expand Down
3 changes: 3 additions & 0 deletions NitroxModel/Packets/PlayerSyncTimeout.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace NitroxModel.Packets;

public class PlayerSyncTimeout : Packet { }
SpaceMonkeyy86 marked this conversation as resolved.
Show resolved Hide resolved
11 changes: 6 additions & 5 deletions NitroxServer/Communication/Packets/PacketHandler.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System;
using System.Linq;
using System.Collections.Generic;
using NitroxModel.Core;
using NitroxModel.Packets;
Expand All @@ -25,13 +26,13 @@ public PacketHandler(PlayerManager playerManager, DefaultServerPacketProcessor p
public void Process(Packet packet, NitroxConnection connection)
{
Player player = playerManager.GetPlayer(connection);
if (player == null)
if (player != null)
{
ProcessUnauthenticated(packet, connection);
ProcessAuthenticated(packet, player);
}
else
else if (!playerManager.GetQueuedPlayers().Contains(connection))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do we want to do if it doesn't contain the connection in the queued players for some reason won't we silently fail here?

SpaceMonkeyy86 marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would like to add an else with error logging here

{
ProcessAuthenticated(packet, player);
ProcessUnauthenticated(packet, connection);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,126 +1,21 @@
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Numerics;
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.Packets;
using NitroxServer.Communication.Packets.Processors.Abstract;
using NitroxServer.GameLogic;
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;

public PlayerJoiningMultiplayerSessionProcessor(ScheduleKeeper scheduleKeeper, StoryManager storyManager, PlayerManager playerManager, World world, EntityRegistry entityRegistry)
public PlayerJoiningMultiplayerSessionProcessor(PlayerManager playerManager)
{
this.scheduleKeeper = scheduleKeeper;
this.storyManager = storyManager;
this.playerManager = playerManager;
this.world = world;
this.entityRegistry = entityRegistry;
}

public override void Process(PlayerJoiningMultiplayerSession packet, NitroxConnection connection)
{
Player player = playerManager.PlayerConnected(connection, packet.ReservationKey, out bool wasBrandNewPlayer);
NitroxId assignedEscapePodId = world.EscapePodManager.AssignPlayerToEscapePod(player.Id, out Optional<EscapePodWorldEntity> newlyCreatedEscapePod);

if (newlyCreatedEscapePod.HasValue)
{
CellEntities spawnNewEscapePod = new(newlyCreatedEscapePod.Value);
playerManager.SendPacketToOtherPlayers(spawnNewEscapePod, player);
}

List<EquippedItemData> equippedItems = player.GetEquipment();
List<NitroxTechType> techTypes = equippedItems.Select(equippedItem => equippedItem.TechType).ToList();

PlayerJoinedMultiplayerSession playerJoinedPacket = new(player.PlayerContext, player.SubRootId, techTypes);
playerManager.SendPacketToOtherPlayers(playerJoinedPacket, player);

// Make players on localhost admin by default.
if (IPAddress.IsLoopback(connection.Endpoint.Address))
{
player.Permissions = Perms.ADMIN;
}

List<NitroxId> simulations = world.EntitySimulation.AssignGlobalRootEntities(player).ToList();

if (wasBrandNewPlayer)
{
SetupPlayerEntity(player);
}
else
{
RespawnExistingEntity(player);
}

InitialPlayerSync initialPlayerSync = new(player.GameObjectId,
wasBrandNewPlayer,
assignedEscapePodId,
equippedItems,
world.BaseManager.GetBasePiecesForNewlyConnectedPlayer(),
world.InventoryManager.GetAllStorageSlotItems(),
player.UsedItems,
player.QuickSlotsBindingIds,
world.GameData.PDAState.GetInitialPDAData(),
world.GameData.StoryGoals.GetInitialStoryGoalData(scheduleKeeper, player),
player.Position,
player.Rotation,
player.SubRootId,
player.Stats,
GetOtherPlayers(player),
world.WorldEntityManager.GetGlobalRootEntities(),
simulations,
world.GameMode,
player.Permissions,
new(new(player.PingInstancePreferences), player.PinnedRecipePreferences.ToList()),
storyManager.GetTimeData()
);

player.SendPacket(initialPlayerSync);
}

private IEnumerable<PlayerContext> GetOtherPlayers(Player player)
{
return playerManager.GetConnectedPlayers().Where(p => p != player)
.Select(p => p.PlayerContext);
}

private void SetupPlayerEntity(Player player)
{
NitroxTransform transform = new(player.Position, player.Rotation, NitroxVector3.One);

PlayerWorldEntity playerEntity = new PlayerWorldEntity(transform, 0, null, false, null, true, player.GameObjectId, NitroxTechType.None, null, null, new List<Entity>());
entityRegistry.AddEntity(playerEntity);
world.WorldEntityManager.TrackEntityInTheWorld(playerEntity);
playerManager.SendPacketToOtherPlayers(new CellEntities(playerEntity), player);
}

private void RespawnExistingEntity(Player player)
{
Optional<Entity> playerEntity = entityRegistry.GetEntityById(player.PlayerContext.PlayerNitroxId);

if (playerEntity.HasValue)
{
playerManager.SendPacketToOtherPlayers(new CellEntities(playerEntity.Value, true), player);
}
else
{
Log.Error($"Unable to find player entity for {player.Name}");
}
playerManager.AddToJoinQueue(connection, packet.ReservationKey);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public override void Process(PlayerSyncFinished packet, Player player)
Server.Instance.ResumeServer();
}

playerManager.FinishProcessingReservation(player);
playerManager.SyncFinishedCallback?.Invoke();
}
}
}
35 changes: 0 additions & 35 deletions NitroxServer/GameLogic/InitialSyncTimerData.cs

This file was deleted.

Loading