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

Add a Dashes field to Ghost, Sync the dash particle color of players #158

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
67 changes: 63 additions & 4 deletions CelesteNet.Client/Components/CelesteNetMainComponent.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
Expand Down Expand Up @@ -34,13 +34,20 @@ public class CelesteNetMainComponent : CelesteNetGameComponent {
private bool WasInteractive;
private int SentHairLength = 0;

private int? SentDashes;
private Color SentP_DashColor;
private Color SentP_DashColor2;
private static Color LastP_DashColor = Player.P_DashA.Color;
private static Color LastP_DashColor2 = Player.P_DashA.Color2;

public HashSet<string> ForceIdle = new();
public bool StateUpdated;

public GhostNameTag PlayerNameTag;
public GhostEmote PlayerIdleTag;
public ConcurrentDictionary<uint, Ghost> Ghosts = new();
public ConcurrentDictionary<uint, DataPlayerFrame> LastFrames = new();
public ConcurrentDictionary<uint, DataPlayerDashExt> LastDashExt = new();

public ConcurrentDictionary<string, int> SpriteAnimationIDs = new(StringComparer.OrdinalIgnoreCase);
public HashSet<PlayerSpriteMode> UnsupportedSpriteModes = new();
Expand Down Expand Up @@ -86,6 +93,7 @@ public override void Initialize() {
On.Celeste.PlayerHair.GetHairScale += OnGetHairScale;
On.Celeste.PlayerHair.GetHairTexture += OnGetHairTexture;
On.Celeste.TrailManager.Add_Vector2_Image_PlayerHair_Vector2_Color_int_float_bool_bool += OnDashTrailAdd;
IL.Celeste.Player.DashUpdate += IlDashUpdate;

MethodInfo transitionRoutine =
typeof(Level).GetNestedType("<TransitionRoutine>d__24", BindingFlags.NonPublic)
Expand Down Expand Up @@ -113,6 +121,7 @@ protected override void Dispose(bool disposing) {
On.Celeste.PlayerHair.GetHairScale -= OnGetHairScale;
On.Celeste.PlayerHair.GetHairTexture -= OnGetHairTexture;
On.Celeste.TrailManager.Add_Vector2_Image_PlayerHair_Vector2_Color_int_float_bool_bool -= OnDashTrailAdd;
IL.Celeste.Player.DashUpdate -= IlDashUpdate;

ILHookTransitionRoutine?.Dispose();
ILHookTransitionRoutine = null;
Expand All @@ -133,6 +142,7 @@ public void Cleanup() {
Session = null;
WasIdle = false;
WasInteractive = false;
SentDashes = null;

foreach (Ghost ghost in Ghosts.Values)
ghost?.RemoveSelf();
Expand Down Expand Up @@ -175,6 +185,7 @@ public void Handle(CelesteNetConnection con, DataPlayerInfo player) {
ghost.RunOnUpdate(ghost => ghost.NameTag.Name = "");
Ghosts.TryRemove(player.ID, out _);
LastFrames.TryRemove(player.ID, out _);
LastDashExt.TryRemove(player.ID, out _);
Client.Data.FreeOrder<DataPlayerFrame>(player.ID);
return;
}
Expand Down Expand Up @@ -298,7 +309,7 @@ public void Handle(CelesteNetConnection con, DataPlayerFrame frame) {
ghost.UpdateGeneric(frame.Position, frame.Scale, frame.Color, frame.Facing, frame.Speed);
ghost.UpdateAnimation(frame.CurrentAnimationID, frame.CurrentAnimationFrame);
ghost.UpdateHair(frame.Facing, frame.HairColors, frame.HairTexture0, frame.HairSimulateMotion && !state.Idle);
ghost.UpdateDash(frame.DashWasB, frame.DashDir); // TODO: Get rid of this, sync particles separately!
ghost.UpdateDash(frame.DashWasB, frame.DashDir);
ghost.UpdateDead(frame.Dead && state.Level == session?.Level);
ghost.UpdateFollowers((Settings.InGame.Entities & CelesteNetClientSettings.SyncMode.Receive) == 0 ? Dummy<DataPlayerFrame.Entity>.EmptyArray : frame.Followers);
ghost.UpdateHolding((Settings.InGame.Entities & CelesteNetClientSettings.SyncMode.Receive) == 0 ? null : frame.Holding);
Expand Down Expand Up @@ -546,6 +557,26 @@ public void Handle(CelesteNetConnection con, DataPlayerGrabPlayer grab) {

Release:
SendReleaseMe();
}
public void Handle(CelesteNetConnection con, DataPlayerDashExt dashExt) {
if (Client?.Data == null)
return;
LastDashExt[dashExt.Player.ID] = dashExt;

Session session = Session;
Level level = PlayerBody?.Scene as Level;
bool outside = IsGhostOutside(session, level, dashExt.Player, out DataPlayerState state);
if (!Ghosts.TryGetValue(dashExt.Player.ID, out Ghost ghost) || ghost == null || (ghost.Active && ghost.Scene != level) || outside) {
RemoveGhost(dashExt.Player);
return;
}
if (level == null || outside)
return;
ghost.RunOnUpdate(ghost => {
if (string.IsNullOrEmpty(ghost.NameTag.Name))
return;
ghost.UpdateDashExt(dashExt.Dashes, dashExt.P_DashColor, dashExt.P_DashColor2);
});
}

#endregion
Expand Down Expand Up @@ -622,6 +653,10 @@ protected Ghost CreateGhost(Level level, DataPlayerInfo player, DataPlayerGraphi
ghost.UpdateGraphics(graphics);
});
ghost.UpdateGraphics(graphics);
if (LastDashExt.TryGetValue(player.ID, out var lastDashExt)) {
ghost.UpdateDashExt(lastDashExt.Dashes, lastDashExt.P_DashColor, lastDashExt.P_DashColor2);
}
SentDashes = null; // There is a new ghost!... Re-send DataPlayerDashExt to let it sync with we
}
return ghost;
}
Expand Down Expand Up @@ -936,6 +971,18 @@ private void ILTransitionRoutine(ILContext il) {
c.Emit(OpCodes.Pop);
c.Emit(OpCodes.Ldc_I4_0);
}
}
private void IlDashUpdate(ILContext il) {
ILCursor c = new(il);
if (c.TryGotoNext(MoveType.After, i => i.MatchCallOrCallvirt<ParticleSystem>("Emit"))) {
c.Emit(OpCodes.Ldloc_S, (byte)7);
c.Emit(OpCodes.Ldfld, typeof(ParticleType).GetField("Color", BindingFlags.Public | BindingFlags.Instance));
c.Emit(OpCodes.Stsfld, typeof(CelesteNetMainComponent).GetField("LastP_DashColor", BindingFlags.NonPublic | BindingFlags.Static));

c.Emit(OpCodes.Ldloc_S, (byte)7);
c.Emit(OpCodes.Ldfld, typeof(ParticleType).GetField("Color2", BindingFlags.Public | BindingFlags.Instance));
c.Emit(OpCodes.Stsfld, typeof(CelesteNetMainComponent).GetField("LastP_DashColor2", BindingFlags.NonPublic | BindingFlags.Static));
}
}

#endregion
Expand Down Expand Up @@ -1093,10 +1140,22 @@ public void SendFrame() {

// TODO: Get rid of this, sync particles separately!
DashWasB = player.StateMachine.State == Player.StDash ? player.GetWasDashB() : null,
DashDir = player.StateMachine.State == Player.StDash ? player.DashDir : null,
DashDir = player.StateMachine.State == Player.StDash ? player.DashDir : null,

Dead = player.Dead
});
});
if (SentDashes != player.Dashes || LastP_DashColor != SentP_DashColor || LastP_DashColor2 != SentP_DashColor2) {
SentDashes = player.Dashes;
SentP_DashColor = LastP_DashColor;
SentP_DashColor2 = LastP_DashColor2;
Client?.Send(new DataPlayerDashExt {
Player = Client.PlayerInfo,

Dashes = player.Dashes,
P_DashColor = SentP_DashColor,
P_DashColor2 = SentP_DashColor,
});
}
} catch (Exception e) {
Logger.Log(LogLevel.INF, "client-main", $"Error in SendFrame:\n{e}");
Context.DisposeSafe();
Expand Down
29 changes: 25 additions & 4 deletions CelesteNet.Client/Entities/Ghost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ public class Ghost : Actor, ITickReceiver {
public GhostNameTag NameTag;
public GhostEmote IdleTag;

public Color[] HairColors = new[] { Color.White };
public Color[] HairColors = new[] { Color.White };

public int Dashes = 1;
public Color? P_DashColor;
public Color? P_DashColor2;

public bool? DashWasB;
public Vector2? DashDir;
Expand Down Expand Up @@ -229,9 +233,18 @@ public override void Update() {
if (Holding != null && Holding.Scene != level)
level.Add(Holding);

// TODO: Get rid of this, sync particles separately!
if (CelesteNetClientModule.Settings.InGame.OtherPlayerOpacity != 0 && DashWasB != null && DashDir != null && level != null && Speed != Vector2.Zero && level.OnRawInterval(0.02f))
level.ParticlesFG.Emit(DashWasB.Value ? Player.P_DashB : Player.P_DashA, Center + Calc.Random.Range(Vector2.One * -2f, Vector2.One * 2f), DashDir.Value.Angle());
if (CelesteNetClientModule.Settings.InGame.OtherPlayerOpacity != 0 && DashWasB != null && DashDir != null && level != null && Speed != Vector2.Zero && level.OnRawInterval(0.02f)) {
ParticleType particle;
if (P_DashColor == null || P_DashColor2 == null) {
particle = DashWasB.Value ? Player.P_DashB : Player.P_DashA;
} else {
particle = new(Player.P_DashA) {
Color = P_DashColor.Value,
Color2 = P_DashColor2.Value
};
}
level.ParticlesFG.Emit(particle, Center + Calc.Random.Range(Vector2.One * -2f, Vector2.One * 2f), DashDir.Value.Angle());
}
}

private void OpacityAdjustAlpha() {
Expand Down Expand Up @@ -356,6 +369,8 @@ public void UpdateHair(Facings facing, Color[] colors, string texture0, bool sim

if (colors.Length <= 0)
colors = new[] { Color.White };
else
Hair.Color = colors[0];
if (PlayerGraphics.HairCount < colors.Length)
Array.Resize(ref colors, PlayerGraphics.HairCount);

Expand All @@ -369,6 +384,12 @@ public void UpdateHair(Facings facing, Color[] colors, string texture0, bool sim
public void UpdateDash(bool? wasB, Vector2? dir) {
DashWasB = wasB;
DashDir = dir;
}

public void UpdateDashExt(int dashes, Color p_color, Color p_color2) {
Dashes = dashes;
P_DashColor = p_color;
P_DashColor2 = p_color2;
}

public void UpdateDead(bool dead) {
Expand Down
2 changes: 1 addition & 1 deletion CelesteNet.Shared/CelesteNetClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ public class CelesteNetClientOptions {
[Flags]
public enum CelesteNetSupportedClientFeatures : ulong {
None = 0,
LocateCommand = 1 << 0
LocateCommand = 1 << 0,
}
}
52 changes: 52 additions & 0 deletions CelesteNet.Shared/DataTypes/DataPlayerDashExt.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using Microsoft.Xna.Framework;
using Monocle;
using System;

namespace Celeste.Mod.CelesteNet.DataTypes {
public class DataPlayerDashExt : DataType<DataPlayerDashExt> {
public DataPlayerInfo? Player;
public int Dashes;
public Color P_DashColor;
public Color P_DashColor2;

static DataPlayerDashExt() {
DataID = "playerDashExt";
}

public override bool FilterHandle(DataContext ctx)
=> Player != null;

public override MetaType[] GenerateMeta(DataContext ctx)
=> new MetaType[] {
new MetaPlayerUpdate(Player),
new MetaOrderedUpdate(Player?.ID ?? uint.MaxValue)
};

public override void FixupMeta(DataContext ctx) {
MetaPlayerUpdate playerUpd = Get<MetaPlayerUpdate>(ctx);
MetaOrderedUpdate order = Get<MetaOrderedUpdate>(ctx);

order.ID = playerUpd;
Player = playerUpd;
}

public override void ReadAll(CelesteNetBinaryReader reader) {
Player = reader.ReadRef<DataPlayerInfo>();

Dashes = reader.Read7BitEncodedInt();
P_DashColor = reader.ReadColor();
P_DashColor2 = reader.ReadColor();

Meta = GenerateMeta(reader.Data);
}

public override void WriteAll(CelesteNetBinaryWriter writer) {
FixupMeta(writer.Data);
writer.WriteRef(Player);

writer.Write7BitEncodedInt(Dashes);
writer.Write(P_DashColor);
writer.Write(P_DashColor2);
}
}
}
5 changes: 2 additions & 3 deletions CelesteNet.Shared/DataTypes/DataPlayerFrame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ static DataPlayerFrame() {

public Entity? Holding;

// TODO: Get rid of this, sync particles separately!
public bool? DashWasB;
public Vector2? DashDir;

Expand Down Expand Up @@ -75,7 +74,7 @@ public override void ReadAll(CelesteNetBinaryReader reader) {

CurrentAnimationID = reader.Read7BitEncodedInt();
CurrentAnimationFrame = reader.Read7BitEncodedInt();

HairColors = new Color[reader.ReadByte()];
Color lastHairCol = Color.White;
for (int i = 0; i < HairColors.Length;) {
Expand Down Expand Up @@ -250,7 +249,7 @@ private enum Flags {
Dashing = 0b00000100,
DashB = 0b00001000,
Holding = 0b00010000,
Dead = 0b00100000
Dead = 0b00100000,
}

public class Entity {
Expand Down