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

Intro cinematic sync #2043

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 1 addition & 11 deletions Nitrox.Test/Client/Communication/DeferredPacketReceiverTest.cs
Original file line number Diff line number Diff line change
@@ -1,30 +1,20 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Nitrox.Test.Client.Communication;
using NitroxClient.Map;
using NitroxModel.DataStructures.GameLogic;
using NitroxModel.DataStructures.Unity;
using Nitrox.Test.Client.Communication;
using NitroxModel.Packets;

namespace NitroxClient.Communication;

[TestClass]
public class DeferredPacketReceiverTest
{
private readonly VisibleCells visibleCells = new();
private PacketReceiver packetReceiver;

// Test Data
private const ushort PLAYER_ID = 1;
private const int CELL_LEVEL = 3;
private readonly NitroxVector3 loadedActionPosition = new(50, 50, 50);
private AbsoluteEntityCell loadedCell;

[TestInitialize]
public void TestInitialize()
{
packetReceiver = new PacketReceiver();
loadedCell = new AbsoluteEntityCell(loadedActionPosition, CELL_LEVEL);
visibleCells.Add(loadedCell);
}

[TestMethod]
Expand Down
28 changes: 18 additions & 10 deletions Nitrox.Test/Helper/Faker/NitroxCollectionFaker.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace Nitrox.Test.Helper.Faker;

public class NitroxCollectionFaker : NitroxFaker, INitroxFaker
Expand Down Expand Up @@ -67,7 +61,7 @@ public static bool TryGetCollectionTypes(Type type, out Type[] types)

if (collectionType == CollectionType.ARRAY)
{
types = new[] {type.GetElementType() };
types = new[] { type.GetElementType() };
return true;
}

Expand Down Expand Up @@ -121,7 +115,7 @@ public NitroxCollectionFaker(Type type, CollectionType collectionType)
for (int i = 0; i < GenerateSize; i++)
{
MethodInfo castMethod = CastMethodBase.MakeGenericMethod(OutputType);
dynamic castedObject = castMethod.Invoke(null, new[] { elementFaker.GenerateUnsafe(typeTree)});
dynamic castedObject = castMethod.Invoke(null, new[] { elementFaker.GenerateUnsafe(typeTree) });
list.Add(castedObject);
}

Expand All @@ -140,10 +134,24 @@ public NitroxCollectionFaker(Type type, CollectionType collectionType)
{
typeTree.Add(dicType[0]);
typeTree.Add(dicType[1]);
IDictionary dict = (IDictionary) Activator.CreateInstance(type);
IDictionary dict = (IDictionary)Activator.CreateInstance(type);
for (int i = 0; i < GenerateSize; i++)
{
dict.Add(keyFaker.GenerateUnsafe(typeTree), valueFaker.GenerateUnsafe(typeTree));
for (int tries = 0; tries < 10; tries++)
{
object key = keyFaker.GenerateUnsafe(typeTree);

if (!dict.Contains(key))
{
dict.Add(key, valueFaker.GenerateUnsafe(typeTree));
break;
}

if (tries == 9)
{
throw new InvalidOperationException($"While generating action for filling Dictionary an unique key of {dicType[0]} couldn't be generated even after 10 tries");
}
}
}

typeTree.Remove(dicType[0]);
Expand Down
1 change: 1 addition & 0 deletions Nitrox.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=cinematics/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Coroutine/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Deconstructable/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=escapepod/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Exosuit/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=exosuits/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=FMOD/@EntryIndexedValue">True</s:Boolean>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using NitroxClient.Communication.Packets.Processors.Abstract;
using NitroxClient.GameLogic;
using NitroxClient.GameLogic.PlayerLogic;
using NitroxModel.Packets;

namespace NitroxClient.Communication.Packets.Processors;

public class SetIntroCinematicModeProcessor : ClientPacketProcessor<SetIntroCinematicMode>
{
private readonly PlayerManager playerManager;
private readonly PlayerCinematics playerCinematics;
private readonly LocalPlayer localPlayer;

public SetIntroCinematicModeProcessor(PlayerManager playerManager, PlayerCinematics playerCinematics, LocalPlayer localPlayer)
{
this.playerManager = playerManager;
this.playerCinematics = playerCinematics;
this.localPlayer = localPlayer;
}

public override void Process(SetIntroCinematicMode packet)
{
if (!packet.PlayerId.HasValue)
{
Log.Error("playerId of SetIntroCinematicMode packet is null which is not expected.");
return;
}

if (localPlayer.PlayerId == packet.PlayerId)
{
if (packet.PartnerId.HasValue)
{
playerCinematics.IntroCinematicPartnerId = packet.PartnerId;
}

localPlayer.IntroCinematicMode = packet.Mode;
return;
}

if (playerManager.TryFind(packet.PlayerId.Value, out RemotePlayer remotePlayer))
{
remotePlayer.PlayerContext.IntroCinematicMode = packet.Mode;
return;
}

Log.Debug($"SetIntroCinematicMode couldn't find Player with id {packet.PlayerId}. This is normal if player has not yet officially joined.");
}
}
8 changes: 4 additions & 4 deletions NitroxClient/GameLogic/HUD/PdaTabs/uGUI_PlayerListTab.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,14 @@ public Sprite GetSprite(string assetName)
public new void OnEnable()
{
// Enter events for player join and disconnect
playerManager.onCreate += OnAdd;
playerManager.onRemove += OnRemove;
playerManager.OnCreate += OnAdd;
playerManager.OnRemove += OnRemove;
}

public new void OnDestroy()
{
playerManager.onCreate -= OnAdd;
playerManager.onRemove -= OnRemove;
playerManager.OnCreate -= OnAdd;
playerManager.OnRemove -= OnRemove;
}

public override void OnLanguageChanged()
Expand Down
10 changes: 10 additions & 0 deletions NitroxClient/GameLogic/InitialSync/PlayerInitialSyncProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public PlayerInitialSyncProcessor(Items item, ItemContainers itemContainers, Loc
this.localPlayer = localPlayer;

AddStep(sync => SetPlayerPermissions(sync.Permissions));
AddStep(sync => SetPlayerIntroCinematicMode(sync.IntroCinematicMode));
AddStep(sync => SetPlayerGameObjectId(sync.PlayerGameObjectId));
AddStep(sync => AddStartingItemsToPlayer(sync.FirstTimeConnecting));
AddStep(sync => SetPlayerStats(sync.PlayerStatsData));
Expand All @@ -39,6 +40,15 @@ private void SetPlayerPermissions(Perms permissions)
localPlayer.Permissions = permissions;
}

private void SetPlayerIntroCinematicMode(IntroCinematicMode introCinematicMode)
{
if (localPlayer.IntroCinematicMode < introCinematicMode)
{
localPlayer.IntroCinematicMode = introCinematicMode;
Log.Info($"Received initial sync player IntroCinematicMode: {introCinematicMode}");
}
}

private void SetPlayerGameObjectId(NitroxId id)
{
NitroxEntity.SetNewId(Player.mainObject, id);
Expand Down
2 changes: 2 additions & 0 deletions NitroxClient/GameLogic/LocalPlayer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public class LocalPlayer : ILocalNitroxPlayer
public PlayerSettings PlayerSettings => multiplayerSession.PlayerSettings;

public Perms Permissions;
public IntroCinematicMode IntroCinematicMode;

public LocalPlayer(IMultiplayerSession multiplayerSession, IPacketSender packetSender, ThrottledPacketSender throttledPacketSender)
{
Expand All @@ -49,6 +50,7 @@ public LocalPlayer(IMultiplayerSession multiplayerSession, IPacketSender packetS
playerModel = new Lazy<GameObject>(() => Body.RequireGameObject("player_view"));
bodyPrototype = new Lazy<GameObject>(CreateBodyPrototype);
Permissions = Perms.PLAYER;
IntroCinematicMode = IntroCinematicMode.NONE;
}

public void BroadcastLocation(Vector3 location, Vector3 velocity, Quaternion bodyRotation, Quaternion aimingRotation, Optional<VehicleMovementData> vehicle)
Expand Down
40 changes: 36 additions & 4 deletions NitroxClient/GameLogic/PlayerLogic/PlayerCinematics.cs
Original file line number Diff line number Diff line change
@@ -1,25 +1,57 @@
using NitroxClient.Communication.Abstract;
using System.Collections.Generic;
using NitroxClient.Communication.Abstract;
using NitroxModel.DataStructures;
using NitroxModel.DataStructures.GameLogic;
using NitroxModel.Packets;

namespace NitroxClient.GameLogic.PlayerLogic;

public class PlayerCinematics
{
private readonly IPacketSender packetSender;
private readonly LocalPlayer localPlayer;

public PlayerCinematics(IPacketSender packetSender)
public ushort? IntroCinematicPartnerId = null;

/// <summary>
/// Some cinematics should not be played. Example the intro as it's completely handled by a dedicated system.
/// </summary>
private readonly HashSet<string> blacklistedKeys = ["escapepod_intro"];

public PlayerCinematics(IPacketSender packetSender, LocalPlayer localPlayer)
{
this.packetSender = packetSender;
this.localPlayer = localPlayer;
}

public void StartCinematicMode(ushort playerId, NitroxId controllerID, int controllerNameHash, string key)
{
packetSender.Send(new PlayerCinematicControllerCall(playerId, controllerID, controllerNameHash, key, true));
if (!blacklistedKeys.Contains(key))
{
packetSender.Send(new PlayerCinematicControllerCall(playerId, controllerID, controllerNameHash, key, true));
}
}

public void EndCinematicMode(ushort playerId, NitroxId controllerID, int controllerNameHash, string key)
{
packetSender.Send(new PlayerCinematicControllerCall(playerId, controllerID, controllerNameHash, key, false));
if (!blacklistedKeys.Contains(key))
{
packetSender.Send(new PlayerCinematicControllerCall(playerId, controllerID, controllerNameHash, key, false));
}
}

public void SetLocalIntroCinematicMode(IntroCinematicMode introCinematicMode)
{
if (!localPlayer.PlayerId.HasValue)
{
Log.Error($"PlayerId was null while setting IntroCinematicMode to {introCinematicMode}");
return;
}

if (localPlayer.IntroCinematicMode != introCinematicMode)
{
localPlayer.IntroCinematicMode = introCinematicMode;
packetSender.Send(new SetIntroCinematicMode(localPlayer.PlayerId, introCinematicMode));
}
}
}
27 changes: 13 additions & 14 deletions NitroxClient/GameLogic/PlayerManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ public class PlayerManager
private readonly FMODWhitelist fmodWhitelist;
private readonly Dictionary<ushort, RemotePlayer> playersById = new();

public OnCreate onCreate;
public OnRemove onRemove;
public OnCreateDelegate OnCreate;
public OnRemoveDelegate OnRemove;

public PlayerManager(PlayerModelManager playerModelManager, PlayerVitalsManager playerVitalsManager, FMODWhitelist fmodWhitelist)
{
Expand All @@ -35,6 +35,8 @@ public Optional<RemotePlayer> Find(ushort playerId)
return Optional.OfNullable(player);
}

public bool TryFind(ushort playerId, out RemotePlayer remotePlayer) => playersById.TryGetValue(playerId, out remotePlayer);

public Optional<RemotePlayer> Find(NitroxId playerNitroxId)
{
RemotePlayer remotePlayer = playersById.Select(idToPlayer => idToPlayer.Value)
Expand All @@ -43,7 +45,7 @@ public Optional<RemotePlayer> Find(NitroxId playerNitroxId)
return Optional.OfNullable(remotePlayer);
}

internal IEnumerable<RemotePlayer> GetAll()
public IEnumerable<RemotePlayer> GetAll()
{
return playersById.Values;
}
Expand All @@ -63,7 +65,7 @@ public RemotePlayer Create(PlayerContext playerContext)
RemotePlayer remotePlayer = new(playerContext, playerModelManager, playerVitalsManager, fmodWhitelist);

playersById.Add(remotePlayer.PlayerId, remotePlayer);
onCreate(remotePlayer.PlayerId.ToString(), remotePlayer);
OnCreate(remotePlayer.PlayerId.ToString(), remotePlayer);

DiscordClient.UpdatePartySize(GetTotalPlayerCount());

Expand All @@ -72,21 +74,18 @@ public RemotePlayer Create(PlayerContext playerContext)

public void RemovePlayer(ushort playerId)
{
Optional<RemotePlayer> opPlayer = Find(playerId);
if (opPlayer.HasValue)
if (playersById.TryGetValue(playerId, out RemotePlayer player))
{
opPlayer.Value.Destroy();
player.Destroy();
playersById.Remove(playerId);
onRemove(playerId.ToString(), opPlayer.Value);
OnRemove(playerId.ToString(), player);
DiscordClient.UpdatePartySize(GetTotalPlayerCount());
}
}

public int GetTotalPlayerCount()
{
return playersById.Count + 1; //Multiplayer-player(s) + you
}
/// <returns>Remote players + You => X + 1</returns>
public int GetTotalPlayerCount() => playersById.Count + 1;

public delegate void OnCreate(string playerId, RemotePlayer remotePlayer);
public delegate void OnRemove(string playerId, RemotePlayer remotePlayer);
public delegate void OnCreateDelegate(string playerId, RemotePlayer remotePlayer);
public delegate void OnRemoveDelegate(string playerId, RemotePlayer remotePlayer);
}
15 changes: 0 additions & 15 deletions NitroxClient/GameLogic/RemotePlayerManagerExtensions.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ private GameObject CreateNewEscapePod(EscapePodWorldEntity escapePodEntity)
// TODO: When we want to implement multiple escape pods, instantiate the prefab. Backlog task: #1945
// This will require some work as instantiating the prefab as-is will not make it visible.
//GameObject escapePod = Object.Instantiate(EscapePod.main.gameObject);

GameObject escapePod = EscapePod.main.gameObject;
UnityEngine.Component.DestroyImmediate(escapePod.GetComponent<NitroxEntity>()); // if template has a pre-existing NitroxEntity, remove.
NitroxEntity.SetNewId(escapePod, escapePodEntity.Id);
Expand All @@ -69,13 +69,6 @@ private GameObject CreateNewEscapePod(EscapePodWorldEntity escapePodEntity)

FixStartMethods(escapePod);

// Start() isn't executed for the EscapePod, why? Idk, maybe because it's a scene...
MultiplayerCinematicReference reference = escapePod.AddComponent<MultiplayerCinematicReference>();
foreach (PlayerCinematicController controller in escapePod.GetComponentsInChildren<PlayerCinematicController>())
{
reference.AddController(controller);
}

return escapePod;
}

Expand All @@ -84,15 +77,21 @@ private GameObject CreateNewEscapePod(EscapePodWorldEntity escapePodEntity)
/// </summary>
private static void FixStartMethods(GameObject escapePod)
{
foreach (FMOD_CustomEmitter customEmitter in escapePod.GetComponentsInChildren<FMOD_CustomEmitter>())
foreach (FMOD_CustomEmitter customEmitter in escapePod.GetComponentsInChildren<FMOD_CustomEmitter>(true))
{
customEmitter.Start();
}

foreach (FMOD_StudioEventEmitter studioEventEmitter in escapePod.GetComponentsInChildren<FMOD_StudioEventEmitter>())
foreach (FMOD_StudioEventEmitter studioEventEmitter in escapePod.GetComponentsInChildren<FMOD_StudioEventEmitter>(true))
{
studioEventEmitter.Start();
}

MultiplayerCinematicReference reference = escapePod.AddComponent<MultiplayerCinematicReference>();
foreach (PlayerCinematicController controller in escapePod.GetComponentsInChildren<PlayerCinematicController>(true))
{
reference.AddController(controller);
}
}

public bool SpawnsOwnChildren()
Expand Down
Loading