public ClientManager(NetworkManager networkManager, PlayerManager playerManager, AnimationManager animationManager, MapManager mapManager, Settings.GameSettings gameSettings, PacketManager packetManager) { _netClient = networkManager.GetNetClient(); _playerManager = playerManager; _animationManager = animationManager; _mapManager = mapManager; _gameSettings = gameSettings; _entityManager = new EntityManager(_netClient); _heartBeatReceiveStopwatch = new Stopwatch(); // Register packet handlers packetManager.RegisterClientPacketHandler <ServerShutdownPacket>(PacketId.ServerShutdown, OnServerShutdown); packetManager.RegisterClientPacketHandler <ClientPlayerConnectPacket>(PacketId.PlayerConnect, OnPlayerConnect); packetManager.RegisterClientPacketHandler <ClientPlayerDisconnectPacket>(PacketId.PlayerDisconnect, OnPlayerDisconnect); packetManager.RegisterClientPacketHandler <ClientAlreadyInScenePacket>(PacketId.AlreadyInScene, OnAlreadyInScene); packetManager.RegisterClientPacketHandler <ClientPlayerEnterScenePacket>(PacketId.PlayerEnterScene, OnPlayerEnterScene); packetManager.RegisterClientPacketHandler <ClientPlayerLeaveScenePacket>(PacketId.PlayerLeaveScene, OnPlayerLeaveScene); packetManager.RegisterClientPacketHandler <ClientUpdatePacket>( PacketId.PlayerUpdate, OnUpdatePacket); packetManager.RegisterClientPacketHandler <ClientPlayerTeamUpdatePacket>(PacketId.PlayerTeamUpdate, OnPlayerTeamUpdate); packetManager.RegisterClientPacketHandler <GameSettingsUpdatePacket>(PacketId.GameSettingsUpdated, OnGameSettingsUpdated); // Register the Hero Controller Start, which is when the local player spawns On.HeroController.Start += (orig, self) => { // Execute the original method orig(self); // If we are connect to a server, add a username to the player object if (networkManager.GetNetClient().IsConnected) { _playerManager.AddNameToPlayer(HeroController.instance.gameObject, _username, _playerManager.LocalPlayerTeam); } }; networkManager.GetNetClient().RegisterOnConnect(() => { // We should only be able to connect during a gameplay scene, // which is when the player is spawned already, so we can add the username _playerManager.AddNameToPlayer(HeroController.instance.gameObject, _username, _playerManager.LocalPlayerTeam); }); // Register handlers for scene change and player update UnityEngine.SceneManagement.SceneManager.activeSceneChanged += OnSceneChange; On.HeroController.Update += OnPlayerUpdate; // Register client connect handler _netClient.RegisterOnConnect(OnClientConnect); // Register application quit handler ModHooks.Instance.ApplicationQuitHook += OnApplicationQuit; // Prevent changing the timescale if the client is connected to ensure synchronisation between clients On.GameManager.SetTimeScale_float += (orig, self, scale) => { if (!_netClient.IsConnected) { orig(self, scale); } else { // Always put the time scale to 1.0, thus never allowing the game to change speed // This is to prevent desyncs in multiplayer orig(self, 1.0f); } }; // Register pause callback to make sure the player doesn't keep dashing or moving On.HeroController.Pause += (orig, self) => { if (!_netClient.IsConnected) { orig(self); return; } // We simply call the private ResetInput method to prevent the knight from continuing movement // while the game is paused typeof(HeroController).InvokeMember( "ResetInput", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod, null, HeroController.instance, null ); }; // To make sure that if we are paused, and we enter a screen transition, // we still go through it. So we unpause first, then execute the original method On.TransitionPoint.OnTriggerEnter2D += (orig, self, obj) => { // Unpause if paused if (UIManager.instance != null) { if (UIManager.instance.uiState.Equals(UIState.PAUSED)) { UIManager.instance.TogglePauseGame(); } } // Execute original method orig(self, obj); }; }