diff --git a/NitroxClient/GameLogic/InitialSync/PlayerInitialSyncProcessor.cs b/NitroxClient/GameLogic/InitialSync/PlayerInitialSyncProcessor.cs index 3c02cd40d9..8bf74bb024 100644 --- a/NitroxClient/GameLogic/InitialSync/PlayerInitialSyncProcessor.cs +++ b/NitroxClient/GameLogic/InitialSync/PlayerInitialSyncProcessor.cs @@ -23,30 +23,42 @@ public PlayerInitialSyncProcessor(Items item, ItemContainers itemContainers) public override IEnumerator Process(InitialPlayerSync packet, WaitScreen.ManualWaitItem waitScreenItem) { - SetPlayerPermissions(packet.Permissions); - waitScreenItem.SetProgress(0.16f); + LocalPlayer localPlayer = NitroxServiceLocator.LocateService(); + + SetPlayerPermissions(localPlayer, packet.Permissions); + waitScreenItem.SetProgress(0.14f); + yield return null; + + SetPlayerIntroCinematicMode(localPlayer, packet.IntroCinematicMode); + waitScreenItem.SetProgress(0.28f); yield return null; SetPlayerGameObjectId(packet.PlayerGameObjectId); - waitScreenItem.SetProgress(0.33f); + waitScreenItem.SetProgress(0.42f); yield return null; yield return AddStartingItemsToPlayer(packet.FirstTimeConnecting); - waitScreenItem.SetProgress(0.50f); + waitScreenItem.SetProgress(0.56f); yield return null; SetPlayerStats(packet.PlayerStatsData); - waitScreenItem.SetProgress(0.66f); + waitScreenItem.SetProgress(0.7f); yield return null; SetPlayerGameMode(packet.GameMode); - waitScreenItem.SetProgress(0.83f); + waitScreenItem.SetProgress(0.84f); yield return null; } - private void SetPlayerPermissions(Perms permissions) + private void SetPlayerPermissions(LocalPlayer localPlayer, Perms permissions) + { + localPlayer.Permissions = permissions; + } + + private void SetPlayerIntroCinematicMode(LocalPlayer localPlayer, IntroCinematicMode introCinematicMode) { - NitroxServiceLocator.LocateService().Permissions = permissions; + localPlayer.IntroCinematicMode = introCinematicMode; + Log.Info($"Received initial sync player IntroCinematicMode: {introCinematicMode}"); } private void SetPlayerGameObjectId(NitroxId id) diff --git a/NitroxClient/GameLogic/LocalPlayer.cs b/NitroxClient/GameLogic/LocalPlayer.cs index 93b6a10c3e..bd39d90759 100644 --- a/NitroxClient/GameLogic/LocalPlayer.cs +++ b/NitroxClient/GameLogic/LocalPlayer.cs @@ -37,7 +37,8 @@ public class LocalPlayer : ILocalNitroxPlayer public PlayerSettings PlayerSettings => multiplayerSession.PlayerSettings; public Perms Permissions; - + public IntroCinematicMode IntroCinematicMode; + public LocalPlayer(IMultiplayerSession multiplayerSession, IPacketSender packetSender, ThrottledPacketSender throttledPacketSender) { this.multiplayerSession = multiplayerSession; @@ -47,6 +48,7 @@ public LocalPlayer(IMultiplayerSession multiplayerSession, IPacketSender packetS playerModel = new Lazy(() => Body.RequireGameObject("player_view")); bodyPrototype = new Lazy(CreateBodyPrototype); Permissions = Perms.PLAYER; + IntroCinematicMode = IntroCinematicMode.NONE; } public void BroadcastLocation(Vector3 location, Vector3 velocity, Quaternion bodyRotation, Quaternion aimingRotation, Optional vehicle) diff --git a/NitroxClient/GameLogic/PlayerLogic/PlayerCinematics.cs b/NitroxClient/GameLogic/PlayerLogic/PlayerCinematics.cs index 74f3dc2835..96bdd88e94 100644 --- a/NitroxClient/GameLogic/PlayerLogic/PlayerCinematics.cs +++ b/NitroxClient/GameLogic/PlayerLogic/PlayerCinematics.cs @@ -1,5 +1,6 @@ using NitroxClient.Communication.Abstract; using NitroxModel.DataStructures; +using NitroxModel.DataStructures.GameLogic; using NitroxModel.Packets; namespace NitroxClient.GameLogic.PlayerLogic; @@ -7,10 +8,12 @@ namespace NitroxClient.GameLogic.PlayerLogic; public class PlayerCinematics { private readonly IPacketSender packetSender; + private readonly LocalPlayer localPlayer; - public PlayerCinematics(IPacketSender packetSender) + public PlayerCinematics(IPacketSender packetSender, LocalPlayer localPlayer) { this.packetSender = packetSender; + this.localPlayer = localPlayer; } public void StartCinematicMode(ushort playerId, NitroxId controllerID, int controllerNameHash, string key) @@ -22,4 +25,10 @@ public void EndCinematicMode(ushort playerId, NitroxId controllerID, int control { packetSender.Send(new PlayerCinematicControllerCall(playerId, controllerID, controllerNameHash, key, false)); } + + public void SetLocalIntroCinematicMode(IntroCinematicMode introCinematicMode) + { + localPlayer.IntroCinematicMode = introCinematicMode; + packetSender.Send(new SetIntroCinematicMode(localPlayer.PlayerId, introCinematicMode)); + } } diff --git a/NitroxModel/DataStructures/GameLogic/IntroCinematicMode.cs b/NitroxModel/DataStructures/GameLogic/IntroCinematicMode.cs new file mode 100644 index 0000000000..4e260d8c93 --- /dev/null +++ b/NitroxModel/DataStructures/GameLogic/IntroCinematicMode.cs @@ -0,0 +1,10 @@ +namespace NitroxModel.DataStructures.GameLogic; + +public enum IntroCinematicMode : byte +{ + NONE, + LOADING, + WAITING, + START, + COMPLETED +} diff --git a/NitroxModel/MultiplayerSession/PlayerContext.cs b/NitroxModel/MultiplayerSession/PlayerContext.cs index 90760aecee..9e75054b49 100644 --- a/NitroxModel/MultiplayerSession/PlayerContext.cs +++ b/NitroxModel/MultiplayerSession/PlayerContext.cs @@ -1,6 +1,6 @@ using System; using NitroxModel.DataStructures; -using NitroxModel.Packets; +using NitroxModel.DataStructures.GameLogic; namespace NitroxModel.MultiplayerSession; @@ -11,11 +11,11 @@ public class PlayerContext public ushort PlayerId { get; } public NitroxId PlayerNitroxId { get; } public bool WasBrandNewPlayer { get; } - public SetIntroCinematicMode.IntroCinematicMode IntroCinematicMode { get; set; } + public IntroCinematicMode IntroCinematicMode { get; set; } public PlayerSettings PlayerSettings { get; } public bool IsMuted { get; set; } - public PlayerContext(string playerName, ushort playerId, NitroxId playerNitroxId, bool wasBrandNewPlayer, SetIntroCinematicMode.IntroCinematicMode introCinematicMode, PlayerSettings playerSettings, bool isMuted) + public PlayerContext(string playerName, ushort playerId, NitroxId playerNitroxId, bool wasBrandNewPlayer, IntroCinematicMode introCinematicMode, PlayerSettings playerSettings, bool isMuted) { PlayerName = playerName; PlayerId = playerId; diff --git a/NitroxModel/Packets/InitialPlayerSync.cs b/NitroxModel/Packets/InitialPlayerSync.cs index 89f450153b..b8817cd49a 100644 --- a/NitroxModel/Packets/InitialPlayerSync.cs +++ b/NitroxModel/Packets/InitialPlayerSync.cs @@ -12,7 +12,7 @@ namespace NitroxModel.Packets { [Serializable] public class InitialPlayerSync : Packet - { + { public NitroxId AssignedEscapePodId { get; } public List EquippedItems { get; } public List BasePieces { get; } @@ -31,6 +31,7 @@ public class InitialPlayerSync : Packet public List InitialSimulationOwnerships { get; } public ServerGameMode GameMode { get; } public Perms Permissions { get; } + public IntroCinematicMode IntroCinematicMode { get; } public SubnauticaPlayerPreferences Preferences { get; } public TimeData TimeData { get; } @@ -52,6 +53,7 @@ public InitialPlayerSync(NitroxId playerGameObjectId, IEnumerable initialSimulationOwnerships, ServerGameMode gameMode, Perms perms, + IntroCinematicMode introCinematicMode, SubnauticaPlayerPreferences preferences, TimeData timeData) { @@ -73,6 +75,7 @@ public InitialPlayerSync(NitroxId playerGameObjectId, InitialSimulationOwnerships = initialSimulationOwnerships.ToList(); GameMode = gameMode; Permissions = perms; + IntroCinematicMode = introCinematicMode; Preferences = preferences; TimeData = timeData; } @@ -97,6 +100,7 @@ public InitialPlayerSync( List initialSimulationOwnerships, ServerGameMode gameMode, Perms permissions, + IntroCinematicMode introCinematicMode, SubnauticaPlayerPreferences preferences, TimeData timeData) { @@ -118,6 +122,7 @@ public InitialPlayerSync( InitialSimulationOwnerships = initialSimulationOwnerships; GameMode = gameMode; Permissions = permissions; + IntroCinematicMode = introCinematicMode; Preferences = preferences; TimeData = timeData; } diff --git a/NitroxModel/Packets/SetIntroCinematicMode.cs b/NitroxModel/Packets/SetIntroCinematicMode.cs index 0ec1c79085..88e0382f02 100644 --- a/NitroxModel/Packets/SetIntroCinematicMode.cs +++ b/NitroxModel/Packets/SetIntroCinematicMode.cs @@ -1,17 +1,11 @@ using System; +using NitroxModel.DataStructures.GameLogic; namespace NitroxModel.Packets; [Serializable] public class SetIntroCinematicMode : Packet { - public enum IntroCinematicMode - { - LOADING, - WAITING, - START, - COMPLETED - } public ushort PlayerId { get; } public IntroCinematicMode Mode { get; } diff --git a/NitroxPatcher/Patches/Dynamic/EscapePod_StopIntroCinematic_Patch.cs b/NitroxPatcher/Patches/Dynamic/EscapePod_StopIntroCinematic_Patch.cs new file mode 100644 index 0000000000..abc5dcd648 --- /dev/null +++ b/NitroxPatcher/Patches/Dynamic/EscapePod_StopIntroCinematic_Patch.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using HarmonyLib; +using NitroxClient.GameLogic.PlayerLogic; +using NitroxModel.DataStructures.GameLogic; +using NitroxModel.Helper; + +namespace NitroxPatcher.Patches.Dynamic; + +public class EscapePod_StopIntroCinematic_Patch : NitroxPatch, IDynamicPatch +{ + private static readonly MethodInfo targetMethod = Reflect.Method((EscapePod e) => e.StopIntroCinematic(default(bool))); + + public static void Postfix(bool isInterrupted) + { + if (isInterrupted) + { + //TODO: Currently this is sent as an unauthenticated packet when skipping the intro up front (Configuration=Debug) and is therefor not processed by the server. + Resolve().SetLocalIntroCinematicMode(IntroCinematicMode.COMPLETED); + } + } + + public override void Patch(Harmony harmony) + { + PatchPostfix(harmony, targetMethod); + } +} diff --git a/NitroxPatcher/Patches/Dynamic/uGUI_SceneIntro_IntroSequence_Patch.cs b/NitroxPatcher/Patches/Dynamic/uGUI_SceneIntro_IntroSequence_Patch.cs index 544a29e594..94d5edde2c 100644 --- a/NitroxPatcher/Patches/Dynamic/uGUI_SceneIntro_IntroSequence_Patch.cs +++ b/NitroxPatcher/Patches/Dynamic/uGUI_SceneIntro_IntroSequence_Patch.cs @@ -4,10 +4,10 @@ using System.Reflection; using System.Reflection.Emit; using HarmonyLib; -using NitroxClient.Communication.Abstract; using NitroxClient.GameLogic; +using NitroxClient.GameLogic.PlayerLogic; +using NitroxModel.DataStructures.GameLogic; using NitroxModel.Helper; -using NitroxModel.Packets; using UnityEngine; namespace NitroxPatcher.Patches.Dynamic; @@ -19,6 +19,7 @@ public class uGUI_SceneIntro_IntroSequence_Patch : NitroxPatch, IDynamicPatch public static IEnumerable Transpiler(IEnumerable instructions) { return new CodeMatcher(instructions) + // Insert custom check if cinematic should be started => waiting for other player & enable skip functionality .MatchEndForward( new CodeMatch(OpCodes.Ldfld, Reflect.Field((uGUI_SceneIntro si) => si.moveNext)), new CodeMatch(OpCodes.Brfalse)) @@ -27,6 +28,7 @@ public static IEnumerable Transpiler(IEnumerable IsRemoteCinematicReady(default))), new CodeInstruction(OpCodes.And) ) + // Run our prepare code when cinematic starts .MatchEndForward( new CodeMatch(OpCodes.Ldsfld, Reflect.Field(() => EscapePod.main)), new CodeMatch(OpCodes.Callvirt, Reflect.Method(() => EscapePod.main.TriggerIntroCinematic())) @@ -35,10 +37,26 @@ public static IEnumerable Transpiler(IEnumerable StartRemoteCinematic())) ) + // Disable cinematic skip when cinematic already started + .MatchStartForward( + new CodeMatch(OpCodes.Call, Reflect.Property(() => Time.time).GetMethod) + ) + .SetInstructionAndAdvance( + new CodeInstruction(OpCodes.Ldc_R4, -1f) + ) + .Advance(1) + .Insert( + new CodeInstruction(OpCodes.Ldloc_1), + new CodeInstruction(OpCodes.Ldfld, Reflect.Field((uGUI_SceneIntro si) => si.skipText)), + new CodeInstruction(OpCodes.Ldc_R4, 0f), + new CodeInstruction(OpCodes.Call, Reflect.Method(() => TMProExtensions.SetAlpha(default, default(float)))) + ) + // Replace intro text .MatchEndForward( new CodeMatch(OpCodes.Ldstr, "IntroUWEPresents") ) .SetOperandAndAdvance("Nitrox_IntroUWEPresents") + // Run our cleanup code when cinematic ends .MatchEndForward( new CodeMatch(OpCodes.Ldloc_1), new CodeMatch(OpCodes.Ldc_I4_0), @@ -59,22 +77,30 @@ public static IEnumerable Transpiler(IEnumerable().IntroCinematicMode == IntroCinematicMode.COMPLETED) + { + uGuiSceneIntro.Stop(true); + EndRemoteCinematic(); + } + if (!uGuiSceneIntro.moveNext) return false; if (!packetSend) { + uGuiSceneIntro.skipHintStartTime = Time.time; uGuiSceneIntro.mainText.SetText("Waiting for partner to join"); uGuiSceneIntro.mainText.SetState(true); - ushort playerId = Resolve().PlayerId; - Resolve().Send(new SetIntroCinematicMode(playerId, SetIntroCinematicMode.IntroCinematicMode.WAITING)); + Resolve().SetLocalIntroCinematicMode(IntroCinematicMode.WAITING); packetSend = true; return false; } - if (Resolve().GetAllRemotePlayers().Any(r => r.PlayerContext.IntroCinematicMode == SetIntroCinematicMode.IntroCinematicMode.START)) + RemotePlayer[] allReadyRemotePlayers = Resolve().GetAllRemotePlayers().Where(r => r.PlayerContext.IntroCinematicMode == IntroCinematicMode.START).ToArray(); + if (allReadyRemotePlayers.Length > 0) { - partner = Resolve().GetAllRemotePlayers().First(r => r.PlayerContext.IntroCinematicMode == SetIntroCinematicMode.IntroCinematicMode.START); + partner = allReadyRemotePlayers.First(); uGuiSceneIntro.moveNext = false; uGuiSceneIntro.mainText.FadeOut(0.2f, uGuiSceneIntro.Callback); @@ -133,7 +159,7 @@ public void Awake() seatRight.Find("life_pod_seat_01_right_damaged_jnt3") }; - seatArmRestLeft = modelRoot.Find("life_pod_seat_01_left_damaged_jnt4/life_pod_seat_01_left_damaged_jnt5"); + seatArmRestLeft = modelRoot.Find("life_pod_seat_01_left_damaged_jnt4/life_pod_seat_01_left_damaged_jnt5"); seatArmRestRight = modelRoot.Find("life_pod_seat_01_right_damaged_jnt4/life_pod_seat_01_right_damaged_jnt5"); seatBarRendererRight = modelRoot.parent.Find("lifepod_damaged_03_geo/life_pod_seat_01_R").GetComponentsInChildren(); @@ -223,6 +249,7 @@ Quaternion InverseRotateAroundEscapePod(Quaternion from) { seatPartsRight[i].localRotation = seatPartsLeft[i].localRotation; } + seatArmRestRight.localPosition = -seatArmRestLeft.localPosition; } @@ -255,8 +282,7 @@ private static void EndRemoteCinematic() partner.ArmsController.enabled = true; partner.AnimationController.UpdatePlayerAnimations = true; - ushort playerId = Resolve().PlayerId; - Resolve().Send(new SetIntroCinematicMode(playerId, SetIntroCinematicMode.IntroCinematicMode.COMPLETED)); + Resolve().SetLocalIntroCinematicMode(IntroCinematicMode.COMPLETED); } public override void Patch(Harmony harmony) diff --git a/NitroxPatcher/Patches/Persistent/MainGameController_ShouldPlayIntro_Patch.cs b/NitroxPatcher/Patches/Persistent/MainGameController_ShouldPlayIntro_Patch.cs index 0ccecb2b6e..5b1a386c09 100644 --- a/NitroxPatcher/Patches/Persistent/MainGameController_ShouldPlayIntro_Patch.cs +++ b/NitroxPatcher/Patches/Persistent/MainGameController_ShouldPlayIntro_Patch.cs @@ -1,3 +1,4 @@ +#if DEBUG using System.Reflection; using HarmonyLib; using NitroxModel.Helper; @@ -10,7 +11,7 @@ public class MainGameController_ShouldPlayIntro_Patch : NitroxPatch, IPersistent public static void Postfix(ref bool __result) { - //__result = true; + __result = false; } public override void Patch(Harmony harmony) @@ -18,3 +19,4 @@ public override void Patch(Harmony harmony) PatchPostfix(harmony, TARGET_METHOD); } } +#endif diff --git a/NitroxServer/Communication/Packets/Processors/PlayerJoiningMultiplayerSessionProcessor.cs b/NitroxServer/Communication/Packets/Processors/PlayerJoiningMultiplayerSessionProcessor.cs index a9e23214aa..0e4fcd51d7 100644 --- a/NitroxServer/Communication/Packets/Processors/PlayerJoiningMultiplayerSessionProcessor.cs +++ b/NitroxServer/Communication/Packets/Processors/PlayerJoiningMultiplayerSessionProcessor.cs @@ -85,6 +85,7 @@ public override void Process(PlayerJoiningMultiplayerSession packet, NitroxConne simulations, world.GameMode, player.Permissions, + wasBrandNewPlayer ? IntroCinematicMode.LOADING : IntroCinematicMode.COMPLETED, new(new(player.PingInstancePreferences), player.PinnedRecipePreferences.ToList()), storyManager.GetTimeData() ); diff --git a/NitroxServer/Communication/Packets/Processors/SetIntroCinematicModeProcessor.cs b/NitroxServer/Communication/Packets/Processors/SetIntroCinematicModeProcessor.cs index c6488a716d..b44be63fde 100644 --- a/NitroxServer/Communication/Packets/Processors/SetIntroCinematicModeProcessor.cs +++ b/NitroxServer/Communication/Packets/Processors/SetIntroCinematicModeProcessor.cs @@ -1,4 +1,5 @@ using System.Linq; +using NitroxModel.DataStructures.GameLogic; using NitroxModel.Packets; using NitroxServer.Communication.Packets.Processors.Abstract; using NitroxServer.GameLogic; @@ -18,15 +19,14 @@ public override void Process(SetIntroCinematicMode packet, Player player) player.PlayerContext.IntroCinematicMode = packet.Mode; playerManager.SendPacketToOtherPlayers(packet, player); - if (playerManager.GetAllPlayers().Count(p => p.PlayerContext?.IntroCinematicMode == SetIntroCinematicMode.IntroCinematicMode.WAITING) >= 2) + Player[] allWaitingPlayers = playerManager.ConnectedPlayers().Where(p => p.PlayerContext.IntroCinematicMode == IntroCinematicMode.WAITING).ToArray(); + if (allWaitingPlayers.Length >= 2) { - Log.Info("Starting Cinematic"); + Log.Info($"Starting IntroCinematic for {allWaitingPlayers[0].PlayerContext.PlayerName} and {allWaitingPlayers[1].PlayerContext.PlayerName}"); - Player[] pairedPlayers = playerManager.GetAllPlayers().Where(p => p.PlayerContext.IntroCinematicMode == SetIntroCinematicMode.IntroCinematicMode.WAITING).Take(2).ToArray(); - - pairedPlayers[0].PlayerContext.IntroCinematicMode = pairedPlayers[1].PlayerContext.IntroCinematicMode = SetIntroCinematicMode.IntroCinematicMode.START; - pairedPlayers[0].SendPacket(new SetIntroCinematicMode(pairedPlayers[1].Id, SetIntroCinematicMode.IntroCinematicMode.START)); - pairedPlayers[1].SendPacket(new SetIntroCinematicMode(pairedPlayers[0].Id, SetIntroCinematicMode.IntroCinematicMode.START)); + allWaitingPlayers[0].PlayerContext.IntroCinematicMode = allWaitingPlayers[1].PlayerContext.IntroCinematicMode = IntroCinematicMode.START; + allWaitingPlayers[0].SendPacket(new SetIntroCinematicMode(allWaitingPlayers[1].Id, IntroCinematicMode.START)); + allWaitingPlayers[1].SendPacket(new SetIntroCinematicMode(allWaitingPlayers[0].Id, IntroCinematicMode.START)); } } } diff --git a/NitroxServer/GameLogic/PlayerManager.cs b/NitroxServer/GameLogic/PlayerManager.cs index 4cc31e133c..4c1f2a500e 100644 --- a/NitroxServer/GameLogic/PlayerManager.cs +++ b/NitroxServer/GameLogic/PlayerManager.cs @@ -115,7 +115,7 @@ public MultiplayerSessionReservation ReservePlayerContext( bool hasSeenPlayerBefore = player != null; ushort playerId = hasSeenPlayerBefore ? player.Id : ++currentPlayerId; NitroxId playerNitroxId = hasSeenPlayerBefore ? player.GameObjectId : new NitroxId(); - SetIntroCinematicMode.IntroCinematicMode introCinematicMode = hasSeenPlayerBefore ? SetIntroCinematicMode.IntroCinematicMode.COMPLETED : SetIntroCinematicMode.IntroCinematicMode.LOADING; + IntroCinematicMode introCinematicMode = hasSeenPlayerBefore ? IntroCinematicMode.COMPLETED : IntroCinematicMode.LOADING; // TODO: At some point, store the muted state of a player PlayerContext playerContext = new(playerName, playerId, playerNitroxId, !hasSeenPlayerBefore, introCinematicMode, playerSettings, false); @@ -324,7 +324,7 @@ public void SendPacketToOtherPlayers(Packet packet, Player sendingPlayer) } } - private IEnumerable ConnectedPlayers() + public IEnumerable ConnectedPlayers() { return assetsByConnection.Values .Where(assetPackage => assetPackage.Player != null)