diff --git a/CelesteNet.Client/Components/CelesteNetMainComponent.cs b/CelesteNet.Client/Components/CelesteNetMainComponent.cs index c6c19a28..52d57257 100644 --- a/CelesteNet.Client/Components/CelesteNetMainComponent.cs +++ b/CelesteNet.Client/Components/CelesteNetMainComponent.cs @@ -129,15 +129,9 @@ protected override void Dispose(bool disposing) { } public void Cleanup() { - Player = null; - PlayerBody = null; - Session = null; - WasIdle = false; - WasInteractive = false; + ResetState(); - foreach (Ghost ghost in Ghosts.Values) - ghost?.RemoveSelf(); - Ghosts.Clear(); + RemoveAllGhosts(); if (IsGrabbed && Player.StateMachine.State == Player.StFrozen) Player.StateMachine.State = Player.StNormal; @@ -169,8 +163,7 @@ public void Handle(CelesteNetConnection con, DataPlayerInfo player) { return; if (string.IsNullOrEmpty(player.DisplayName)) { - ghost.RunOnUpdate(ghost => ghost.NameTag.Name = ""); - Ghosts.TryRemove(player.ID, out _); + RemoveGhost(player); LastFrames.TryRemove(player.ID, out _); Client.Data.FreeOrder(player.ID); return; @@ -179,9 +172,7 @@ public void Handle(CelesteNetConnection con, DataPlayerInfo player) { public void Handle(CelesteNetConnection con, DataChannelMove move) { if (move.Player.ID == Client.PlayerInfo.ID) { - foreach (Ghost ghost in Ghosts.Values) - ghost?.RemoveSelf(); - Ghosts.Clear(); + RemoveAllGhosts(); // The server resends all bound data anyway. foreach (DataPlayerInfo other in Client.Data.GetRefs()) { @@ -194,13 +185,9 @@ public void Handle(CelesteNetConnection con, DataChannelMove move) { } } else { - if (!Ghosts.TryGetValue(move.Player.ID, out Ghost ghost) || - ghost == null) + if (!RemoveGhost(move.Player)) return; - ghost.RunOnUpdate(ghost => ghost.NameTag.Name = ""); - Ghosts.TryRemove(move.Player.ID, out _); - foreach (DataType data in Client.Data.GetBoundRefs(move.Player)) if (data.TryGet(Client.Data, out MetaPlayerPrivateState state)) Client.Data.FreeBoundRef(data); @@ -225,8 +212,7 @@ public void Handle(CelesteNetConnection con, DataPlayerState state) { Session session = Session; if (session != null && (state.SID != session.Area.SID || state.Mode != session.Area.Mode || state.Level == LevelDebugMap)) { - ghost.RunOnUpdate(ghost => ghost.NameTag.Name = ""); - Ghosts.TryRemove(id, out _); + RemoveGhost(ghost.PlayerInfo); return; } @@ -244,8 +230,11 @@ public void Handle(CelesteNetConnection con, DataPlayerGraphics graphics) { } Level level = PlayerBody?.Scene as Level; - if (ghost == null && !IsGhostOutside(Session, level, graphics.Player, out _)) + if (ghost == null && !IsGhostOutside(Session, level, graphics.Player, out _)) { ghost = CreateGhost(level, graphics.Player, graphics); + if (graphics.Player.ID == uint.MaxValue) + Logger.Log(LogLevel.WRN, "META", $"Handle graphics: Created ghost for ID uint.MaxValue ({graphics.Player.ID}) - should this be happening?"); + } if (ghost != null) ghost.UpdateGraphics(graphics); @@ -273,6 +262,8 @@ public void Handle(CelesteNetConnection con, DataPlayerFrame frame) { if (!Client.Data.TryGetBoundRef(frame.Player, out DataPlayerGraphics graphics) || graphics == null) return; ghost = CreateGhost(level, frame.Player, graphics); + if (frame.Player.ID == uint.MaxValue) + Logger.Log(LogLevel.WRN, "META", $"Handle frame: Created ghost for ID uint.MaxValue ({frame.Player.ID}) - should this be happening?"); } ghost.RunOnUpdate(ghost => { @@ -596,7 +587,6 @@ protected Ghost CreateGhost(Level level, DataPlayerInfo player, DataPlayerGraphi UnsupportedSpriteModes.Add(graphics.SpriteMode); RunOnMainThread(() => { level.Add(ghost); - level.OnEndOfFrame += () => ghost.Active = true; ghost.UpdateGraphics(graphics); }); ghost.UpdateGraphics(graphics); @@ -604,9 +594,19 @@ protected Ghost CreateGhost(Level level, DataPlayerInfo player, DataPlayerGraphi return ghost; } - protected void RemoveGhost(DataPlayerInfo info) { - Ghosts.TryRemove(info.ID, out Ghost ghost); + protected bool RemoveGhost(DataPlayerInfo player) { + if (!Ghosts.TryRemove(player.ID, out Ghost ghost)) + return false; ghost?.RunOnUpdate(g => g.NameTag.Name = ""); + return true; + } + + protected void RemoveAllGhosts() { + lock (Ghosts) { + foreach (Ghost ghost in Ghosts.Values) + ghost?.RemoveSelf(); + Ghosts.Clear(); + } } public void UpdateIdleTag(Entity target, ref GhostEmote idleTag, bool idle) { @@ -672,16 +672,16 @@ public EventInstance PlayAudio(Ghost ghost, string sound, Vector2? at, string pa public override void Update(GameTime gameTime) { base.Update(gameTime); + foreach (Ghost g in Ghosts.Values) + if (g != null) + g.Active = true; + bool ready = Client != null && Client.IsReady && Client.PlayerInfo != null; if (Engine.Scene is not Level level || !ready) { GrabbedBy = null; if (ready && Engine.Scene is MapEditor) { - Player = null; - PlayerBody = null; - Session = null; - WasIdle = false; - WasInteractive = false; + ResetState(); AreaKey area = (AreaKey) f_MapEditor_area.GetValue(null); if (MapEditorArea == null || MapEditorArea.Value.SID != area.SID || MapEditorArea.Value.Mode != area.Mode) { @@ -691,11 +691,7 @@ public override void Update(GameTime gameTime) { } if (Player != null && MapEditorArea == null) { - Player = null; - PlayerBody = null; - Session = null; - WasIdle = false; - WasInteractive = false; + ResetState(); SendState(); } return; @@ -732,12 +728,9 @@ public override void Update(GameTime gameTime) { } if (Player == null || Player.Scene != level) { - Player = level.Tracker.GetEntity(); - if (Player != null) { - PlayerBody = Player; - Session = level.Session; - WasIdle = false; - WasInteractive = false; + Player player = level.Tracker.GetEntity(); + if (player != Player) { + ResetState(player, level.Session); StateUpdated |= true; SendGraphics(); } @@ -807,24 +800,19 @@ public void OnSetActualDepth(On.Monocle.Scene.orig_SetActualDepth orig, Scene sc public void OnLoadLevel(On.Celeste.Level.orig_LoadLevel orig, Level level, Player.IntroTypes playerIntro, bool isFromLoader = false) { orig(level, playerIntro, isFromLoader); - Session = level.Session; - WasIdle = false; - WasInteractive = false; + Player player = null; - if (Client == null) - return; + if (Client != null) + player = level.Tracker.GetEntity(); - Player = level.Tracker.GetEntity(); - PlayerBody = Player; + ResetState(player, level.Session); - SendState(); + if (Client != null) + SendState(); } public void OnExitLevel(Level level, LevelExit exit, LevelExit.Mode mode, Session session, HiresSnow snow) { - Session = null; - WasIdle = false; - WasInteractive = false; - + // the first thing Cleanup does is call ResetState. Cleanup(); SendState(); @@ -842,17 +830,14 @@ private Player OnLoadNewPlayer(On.Celeste.Level.orig_LoadNewPlayer orig, Vector2 private void OnPlayerAdded(On.Celeste.Player.orig_Added orig, Player self, Scene scene) { orig(self, scene); - Session = (scene as Level)?.Session; - WasIdle = false; - WasInteractive = false; - Player = self; - PlayerBody = self; - + ResetState(self, (scene as Level)?.Session); SendState(); SendGraphics(); - foreach (DataPlayerFrame frame in LastFrames.Values.ToArray()) - Handle(null, frame); + scene.OnEndOfFrame += () => { + foreach (DataPlayerFrame frame in LastFrames.Values.ToArray()) + Handle(null, frame); + }; } private PlayerDeadBody OnPlayerDie(On.Celeste.Player.orig_Die orig, Player self, Vector2 direction, bool evenIfInvincible, bool registerDeathInStats) { @@ -918,6 +903,19 @@ private void ILTransitionRoutine(ILContext il) { #endregion + public void ResetState(Player player = null, Session ses = null, bool keepGhosts = false) { + // Clear ghosts if the scene changed, but not on respawns + keepGhosts |= player == null && Player != null && Player.Scene == null; + if (!keepGhosts && player?.Scene != Player?.Scene) + RemoveAllGhosts(); + Player = player; + // when keeping Ghosts we also need to keep PlayerBody apparently, because some Ghost logic uses PlayerBody.Scene as Level + if(!keepGhosts) + PlayerBody = player; + Session = ses; + WasIdle = false; + WasInteractive = false; + } #region Send diff --git a/CelesteNet.Client/Entities/GhostNameTag.cs b/CelesteNet.Client/Entities/GhostNameTag.cs index 857f43ce..25e2dd70 100644 --- a/CelesteNet.Client/Entities/GhostNameTag.cs +++ b/CelesteNet.Client/Entities/GhostNameTag.cs @@ -45,7 +45,7 @@ public override void Render() { float scale = level.GetScreenScale(); - Vector2 pos = Tracking?.Position ?? Position; + Vector2 pos = Tracking?.BottomCenter ?? Position; pos.Y -= 16f; pos = level.WorldToScreen(pos);