From fc4fcd88b57d2fa80902db070119b6fb4bf86614 Mon Sep 17 00:00:00 2001 From: Jenna Sloan Date: Sun, 24 Nov 2024 23:50:15 -0600 Subject: [PATCH] bug fixes also added a network latency number --- .../FezCompatibilityTypes.cs | 160 ++++++++++++++++++ .../FezDedicatedServer.cs | 2 +- .../FezMultiplayerDedicatedServer.csproj | 1 + .../MultiplayerServer.cs | 22 ++- .../MultiplayerMod/FezMultiplayerMod.cs | 55 +++--- .../MultiplayerClientNetcode.cs | 5 +- Shared/Shared.cs | 46 +++-- 7 files changed, 245 insertions(+), 46 deletions(-) create mode 100644 FezMultiplayerDedicatedServer/FezCompatibilityTypes.cs diff --git a/FezMultiplayerDedicatedServer/FezCompatibilityTypes.cs b/FezMultiplayerDedicatedServer/FezCompatibilityTypes.cs new file mode 100644 index 0000000..8cb0d5e --- /dev/null +++ b/FezMultiplayerDedicatedServer/FezCompatibilityTypes.cs @@ -0,0 +1,160 @@ +using System; + +namespace FezMultiplayerDedicatedServer +{ + public struct Vector3 + { + public float X, Y, Z; + public Vector3(float x, float y, float z) + { + X = x; Y = y; Z = z; + } + public override string ToString() + { + string separator = System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator; + return $"<{X}{separator} {Y}{separator} {this.Z}>"; + } + public Vector3 Round(int d) + { + return new Vector3((float)Math.Round(X, d), (float)Math.Round(Y, d), (float)Math.Round(Z, d)); + } + } + public enum HorizontalDirection + { + None, + Left, + Right + } + public enum Viewpoint + { + None, + Front, + Right, + Back, + Left, + Up, + Down, + Perspective + } + public enum ActionType + { + None, + Idle, + LookingLeft, + LookingRight, + LookingUp, + LookingDown, + Walking, + Running, + Jumping, + FrontClimbingLadder, + BackClimbingLadder, + SideClimbingLadder, + CarryIdle, + CarryWalk, + CarryJump, + CarrySlide, + CarryEnter, + CarryHeavyIdle, + CarryHeavyWalk, + CarryHeavyJump, + CarryHeavySlide, + CarryHeavyEnter, + DropTrile, + DropHeavyTrile, + Throwing, + ThrowingHeavy, + Lifting, + LiftingHeavy, + Dying, + Suffering, + Falling, + Bouncing, + Flying, + Dropping, + Sliding, + Landing, + ReadingSign, + FreeFalling, + CollectingFez, + Victory, + EnteringDoor, + Grabbing, + Pushing, + SuckedIn, + FrontClimbingVine, + FrontClimbingVineSideways, + SideClimbingVine, + BackClimbingVine, + BackClimbingVineSideways, + WakingUp, + OpeningTreasure, + OpeningDoor, + WalkingTo, + Treading, + Swimming, + Sinking, + Teetering, + HurtSwim, + EnteringTunnel, + PushingPivot, + EnterDoorSpin, + EnterDoorSpinCarry, + EnterDoorSpinCarryHeavy, + EnterTunnelCarry, + EnterTunnelCarryHeavy, + RunTurnAround, + FindingTreasure, + IdlePlay, + IdleSleep, + IdleLookAround, + PullUpCornerLedge, + LowerToCornerLedge, + GrabCornerLedge, + GrabLedgeFront, + GrabLedgeBack, + PullUpFront, + PullUpBack, + LowerToLedge, + ShimmyFront, + ShimmyBack, + ToCornerFront, + ToCornerBack, + FromCornerBack, + IdleToClimb, + IdleToFrontClimb, + IdleToSideClimb, + JumpToClimb, + JumpToSideClimb, + ClimbOverLadder, + GrabTombstone, + PivotTombstone, + LetGoOfTombstone, + EnteringPipe, + ExitDoor, + ExitDoorCarry, + ExitDoorCarryHeavy, + LesserWarp, + GateWarp, + SleepWake, + ReadTurnAround, + EndReadTurnAround, + TurnToBell, + HitBell, + TurnAwayFromBell, + CrushHorizontal, + CrushVertical, + DrumsIdle, + DrumsCrash, + DrumsTom, + DrumsTom2, + DrumsToss, + DrumsTwirl, + DrumsHiHat, + VictoryForever, + Floating, + Standing, + StandWinking, + IdleYawn + } +} \ No newline at end of file diff --git a/FezMultiplayerDedicatedServer/FezDedicatedServer.cs b/FezMultiplayerDedicatedServer/FezDedicatedServer.cs index 69fe36e..d97d5fc 100644 --- a/FezMultiplayerDedicatedServer/FezDedicatedServer.cs +++ b/FezMultiplayerDedicatedServer/FezDedicatedServer.cs @@ -77,7 +77,7 @@ static void Main(string[] args) + $"{p.TimeSinceJoin}, " + $"{((p.CurrentLevelName == null || p.CurrentLevelName.Length == 0) ? "???" : p.CurrentLevelName)}, " + $"{p.Action}, {p.CameraViewpoint}, " - + $"{p.Position/*.Round(3)*/}, {(DateTime.UtcNow.Ticks - p.LastUpdateTimestamp) / (double)TimeSpan.TicksPerSecond}\n"; + + $"{p.Position.Round(3)}, {p.NetworkSpeedUp}, {(DateTime.UtcNow.Ticks - p.LastUpdateTimestamp) / (double)TimeSpan.TicksPerSecond}\n"; } s += $"{count} players online"; Console.WriteLine(s); diff --git a/FezMultiplayerDedicatedServer/FezMultiplayerDedicatedServer.csproj b/FezMultiplayerDedicatedServer/FezMultiplayerDedicatedServer.csproj index 0063187..3c7646e 100644 --- a/FezMultiplayerDedicatedServer/FezMultiplayerDedicatedServer.csproj +++ b/FezMultiplayerDedicatedServer/FezMultiplayerDedicatedServer.csproj @@ -46,6 +46,7 @@ + diff --git a/FezMultiplayerDedicatedServer/MultiplayerServer.cs b/FezMultiplayerDedicatedServer/MultiplayerServer.cs index aaba58e..8b4e8bd 100644 --- a/FezMultiplayerDedicatedServer/MultiplayerServer.cs +++ b/FezMultiplayerDedicatedServer/MultiplayerServer.cs @@ -9,10 +9,6 @@ using System.IO; using System.Collections.Concurrent; using FezSharedTools; - -using ActionType = System.Int32; -using HorizontalDirection = System.Int32; -using Viewpoint = System.Int32; using System.Threading.Tasks; using static FezMultiplayerDedicatedServer.MultiplayerServer; @@ -31,6 +27,7 @@ public class ServerPlayerMetadata : PlayerMetadata public TcpClient tcpClient; public readonly DateTime joinTime = DateTime.UtcNow; public TimeSpan TimeSinceJoin => DateTime.UtcNow - joinTime; + public long NetworkSpeedUp = 0; public ServerPlayerMetadata(TcpClient tcpClient, Guid Uuid, string CurrentLevelName, Vector3 Position, Viewpoint CameraViewpoint, ActionType Action, int AnimFrame, HorizontalDirection LookingDirection, long LastUpdateTimestamp) : base(Uuid, CurrentLevelName, Position, CameraViewpoint, Action, AnimFrame, LookingDirection, LastUpdateTimestamp) @@ -258,8 +255,12 @@ private void OnNewClientConnect(TcpClient tcpClient) { try { + Queue SpeedUp = new Queue(100); + long SpeedDown = 0; //send them our data and get player appearance from client - WriteServerGameTickPacket(writer, Players.Values.Cast().ToList(), null, GetActiveLevelStates(), DisconnectedPlayers.Keys, PlayerAppearances, uuid, false, sharedSaveData); + SpeedUp.Enqueue(WriteServerGameTickPacket(writer, Players.Values.Cast().ToList(), + null, GetActiveLevelStates(), DisconnectedPlayers.Keys, + PlayerAppearances, uuid, false, sharedSaveData)); MiscClientData clientData = new MiscClientData(null, false, new HashSet(MiscClientData.MaxRequestedAppearancesSize)); ReadClientGameTickPacket(reader, ref clientData, uuid); bool Disconnecting = clientData.Disconnecting; @@ -298,11 +299,18 @@ bool PlayerAppearancesFilter(KeyValuePair p) { break; } + if(Players.TryGetValue(uuid, out ServerPlayerMetadata serverPlayerMetadata)){ + serverPlayerMetadata.NetworkSpeedUp = (long)Math.Round(SpeedUp.Average());//Note: does not produce a meaningful number for connections to loopback addresses + } //if UnknownPlayerAppearanceGuids contains uuid, ask client to retransmit their PlayerAppearance bool requestAppearance = UnknownPlayerAppearanceGuids.ContainsKey(uuid); //repeat until the client disconnects or times out - WriteServerGameTickPacket(writer, Players.Values.Cast().ToList(), GetSaveDataUpdate(), GetActiveLevelStates(), DisconnectedPlayers.Keys, - GetPlayerAppearances(PlayerAppearancesFilter), null, requestAppearance, null); + if(SpeedUp.Count>=100){ + _ = SpeedUp.Dequeue(); + } + SpeedUp.Enqueue(WriteServerGameTickPacket(writer, Players.Values.Cast().ToList(), + GetSaveDataUpdate(), GetActiveLevelStates(), DisconnectedPlayers.Keys, + GetPlayerAppearances(PlayerAppearancesFilter), null, requestAppearance, null)); ReadClientGameTickPacket(reader, ref clientData, uuid); Disconnecting = clientData.Disconnecting; playerMetadata = clientData.Metadata; diff --git a/FezMultiplayerMod/MultiplayerMod/FezMultiplayerMod.cs b/FezMultiplayerMod/MultiplayerMod/FezMultiplayerMod.cs index 4120637..593a8ce 100644 --- a/FezMultiplayerMod/MultiplayerMod/FezMultiplayerMod.cs +++ b/FezMultiplayerMod/MultiplayerMod/FezMultiplayerMod.cs @@ -202,10 +202,10 @@ public override void Draw(GameTime gameTime) if (p != null) { s += "(you): "; - s += $"{mp.MyAppearance.PlayerName}, {mp.MyUuid}, " + s += $"{mp.MyAppearance.PlayerName}, "//{mp.MyUuid}, " + $"{((p.CurrentLevelName == null || p.CurrentLevelName.Length == 0) ? "???" : p.CurrentLevelName)}, " + $"{p.Action}, {p.CameraViewpoint}, " - + $"{p.Position.Round(3)}\n"; + + $"{p.Position.Round(3)}, {mp.ConnectionLatencyUp}\n"; } } if (mp.ErrorMessage != null) @@ -246,7 +246,12 @@ public override void Draw(GameTime gameTime) { DrawPlayer(p, playerName, gameTime); } - catch { } + catch(Exception e) + { +#if DEBUG + System.Diagnostics.Debugger.Launch(); + #endif + } } } } @@ -273,23 +278,30 @@ public override void Draw(GameTime gameTime) SamplerState = SamplerState.PointClamp }; private TimeSpan sinceBackgroundChanged = TimeSpan.Zero; + const bool HideInFirstPerson = false; internal void DrawPlayer(PlayerMetadata p, string playerName, GameTime gameTime, bool doDraw = true) { + ActionType pAction = p.Action; #region adapted from GomezHost.Update if (GameState.Loading || GameState.InMap || GameState.Paused - || (FezMath.AlmostEqual(PlayerManager.GomezOpacity, 0f) && CameraManager.Viewpoint != Viewpoint.Perspective) - || p.Action == ActionType.None) + || (HideInFirstPerson + && ((FezMath.AlmostEqual(PlayerManager.GomezOpacity, 0f) && CameraManager.Viewpoint != Viewpoint.Perspective) + || pAction == ActionType.None) + ) + ) { return; } //TODO fix the problem with climbing in different viewpoints; see p.CameraViewpoint - if (CameraManager.Viewpoint.GetOpposite() == p.CameraViewpoint) + HorizontalDirection LookingDir = p.LookingDirection; + Viewpoint cameraViewpoint = CameraManager.Viewpoint.IsOrthographic() ? CameraManager.Viewpoint : CameraManager.LastViewpoint; + if (cameraViewpoint.GetOpposite() == p.CameraViewpoint) { - p.LookingDirection = p.LookingDirection.GetOpposite(); + LookingDir = LookingDir.GetOpposite(); } - AnimatedTexture animation = PlayerManager.GetAnimation(p.Action); + AnimatedTexture animation = PlayerManager.GetAnimation(pAction); if (animation.Offsets.Length < 0) { return; @@ -310,7 +322,7 @@ internal void DrawPlayer(PlayerMetadata p, string playerName, GameTime gameTime, mesh.Texture = animation.Texture; mesh.FirstGroup.TextureMatrix.Set(new Matrix((float)rectangle.Width / (float)width, 0f, 0f, 0f, 0f, (float)rectangle.Height / (float)height, 0f, 0f, (float)rectangle.X / (float)width, (float)rectangle.Y / (float)height, 1f, 0f, 0f, 0f, 0f, 0f)); bool playerinbackground = false;// PlayerManager.Background; - /*if (lastBackground != playerinbackground && !p.Action.NoBackgroundDarkening()) + /*if (lastBackground != playerinbackground && !pAction.NoBackgroundDarkening()) { sinceBackgroundChanged = TimeSpan.Zero; lastBackground = playerinbackground; @@ -319,9 +331,10 @@ internal void DrawPlayer(PlayerMetadata p, string playerName, GameTime gameTime, { sinceBackgroundChanged += gameTime.ElapsedGameTime; } - effect.Background = p.Action.NoBackgroundDarkening() ? 0f : FezMath.Saturate(playerinbackground ? ((float)sinceBackgroundChanged.TotalSeconds * 2f) : (1f - (float)sinceBackgroundChanged.TotalSeconds * 2f)); + effect.Background = pAction.NoBackgroundDarkening() ? 0f : FezMath.Saturate(playerinbackground ? ((float)sinceBackgroundChanged.TotalSeconds * 2f) : (1f - (float)sinceBackgroundChanged.TotalSeconds * 2f)); mesh.Scale = new Vector3(animation.FrameWidth / 16f, animation.FrameHeight / 16f, 1f); - mesh.Position = p.Position + GetPositionOffset(p, ref animation); + + mesh.Position = p.Position + GetPositionOffset(pAction, ref animation, LookingDir, cameraViewpoint); #endregion #region adapted from GomezHost.DoDraw_Internal //if (GameState.StereoMode || LevelManager.Quantum) @@ -334,13 +347,13 @@ internal void DrawPlayer(PlayerMetadata p, string playerName, GameTime gameTime, { mesh.Rotation = CameraManager.Rotation;//always point the mesh at the camera so first person mode looks good } - if (p.LookingDirection == HorizontalDirection.Left) + if (LookingDir == HorizontalDirection.Left) { mesh.Rotation *= FezMath.QuaternionFromPhi((float)Math.PI); } //} //blinking - /*if (p.Action == ActionType.Suffering || p.Action == ActionType.Sinking) + /*if (pAction == ActionType.Suffering || pAction == ActionType.Sinking) { mesh.Material.Opacity = (float)FezMath.Saturate((Math.Sin(PlayerManager.BlinkSpeed * ((float)Math.PI * 2f) * 5f) + 0.5 - (double)(PlayerManager.BlinkSpeed * 1.25f)) * 2.0); } @@ -348,9 +361,10 @@ internal void DrawPlayer(PlayerMetadata p, string playerName, GameTime gameTime, { mesh.Material.Opacity = PlayerManager.GomezOpacity; }*/ + mesh.Material.Opacity = 1; GraphicsDevice graphicsDevice = base.GraphicsDevice; //silhouette - if (!p.Action.SkipSilhouette()) + if (!pAction.SkipSilhouette()) { graphicsDevice.PrepareStencilRead(CompareFunction.Greater, StencilMask.NoSilhouette); mesh.DepthWrites = false; @@ -368,7 +382,7 @@ internal void DrawPlayer(PlayerMetadata p, string playerName, GameTime gameTime, } //finally draw the mesh graphicsDevice.PrepareStencilWrite(StencilMask.Gomez); - mesh.AlwaysOnTop = p.Action.NeedsAlwaysOnTop(); + mesh.AlwaysOnTop = pAction.NeedsAlwaysOnTop(); mesh.DepthWrites = !GameState.InFpsMode; effect.Silhouette = false; if (doDraw) mesh.Draw(); @@ -382,15 +396,14 @@ internal void DrawPlayer(PlayerMetadata p, string playerName, GameTime gameTime, #endregion } //Adapted from GomezHost.GetPositionOffset - private Vector3 GetPositionOffset(PlayerMetadata p, ref AnimatedTexture anim) + private Vector3 GetPositionOffset(ActionType pAction, ref AnimatedTexture anim, HorizontalDirection LookingDir, Viewpoint view) { - float playerSizeY = p.Action.IsCarry() ? (Enum.GetName(typeof(ActionType), p.Action).Contains("Heavy") ? 1.75f : 1.9375f) : 0.9375f;//numbers from PlayerManager.SyncCollisionSize - float num = playerSizeY + ((p.Action.IsCarry() || p.Action == ActionType.ThrowingHeavy) ? (-2) : 0); + float playerSizeY = pAction.IsCarry() ? (Enum.GetName(typeof(ActionType), pAction).Contains("Heavy") ? 1.75f : 1.9375f) : 0.9375f;//numbers from PlayerManager.SyncCollisionSize + float num = playerSizeY + ((pAction.IsCarry() || pAction == ActionType.ThrowingHeavy) ? (-2) : 0); Vector3 vector = (1f - num) / 2f * Vector3.UnitY; - Vector2 vector2 = p.Action.GetOffset() / 16f; + Vector2 vector2 = pAction.GetOffset() / 16f; vector2.Y -= anim.PotOffset.Y / 64f; - Viewpoint view = ((CameraManager.Viewpoint.IsOrthographic() || !CameraManager.ActionRunning) ? CameraManager.Viewpoint : CameraManager.LastViewpoint); - return vector + (vector2.X * view.RightVector() * p.LookingDirection.Sign() + vector2.Y * Vector3.UnitY); + return vector + (vector2.X * view.RightVector() * LookingDir.Sign() + vector2.Y * Vector3.UnitY); } #endregion } diff --git a/FezMultiplayerMod/MultiplayerMod/MultiplayerClientNetcode.cs b/FezMultiplayerMod/MultiplayerMod/MultiplayerClientNetcode.cs index 10ec575..fe0df4d 100644 --- a/FezMultiplayerMod/MultiplayerMod/MultiplayerClientNetcode.cs +++ b/FezMultiplayerMod/MultiplayerMod/MultiplayerClientNetcode.cs @@ -31,6 +31,7 @@ public abstract class MultiplayerClientNetcode : SharedNetcode, public volatile bool Listening; public PlayerAppearance MyAppearance; private volatile bool MyAppearanceChanged = false; + public volatile uint ConnectionLatencyUp = 0; public string MyPlayerName { get => MyAppearance.PlayerName; @@ -71,7 +72,7 @@ internal MultiplayerClientNetcode(MultiplayerClientSettings settings) { bool retransmitAppearanceRequested = false; ReadServerGameTickPacket(reader, ref retransmitAppearanceRequested); - WriteClientGameTickPacket(writer, MyPlayerMetadata, null, null, MyAppearance, UnknownPlayerAppearanceGuids.Keys, false); + ConnectionLatencyUp = (uint)WriteClientGameTickPacket(writer, MyPlayerMetadata, null, null, MyAppearance, UnknownPlayerAppearanceGuids.Keys, false); WasSucessfullyConnected = true; while (true) { @@ -95,7 +96,7 @@ internal MultiplayerClientNetcode(MultiplayerClientSettings settings) { appearance = MyAppearance; } - WriteClientGameTickPacket(writer, MyPlayerMetadata, GetSaveDataUpdate(), activeLevelState, appearance, UnknownPlayerAppearanceGuids.Keys, false); + ConnectionLatencyUp = (uint)WriteClientGameTickPacket(writer, MyPlayerMetadata, GetSaveDataUpdate(), activeLevelState, appearance, UnknownPlayerAppearanceGuids.Keys, false); } else { diff --git a/Shared/Shared.cs b/Shared/Shared.cs index dec2e27..c44c33e 100644 --- a/Shared/Shared.cs +++ b/Shared/Shared.cs @@ -7,6 +7,7 @@ using System.Net; using System.Reflection; using System.Text; +using System.Diagnostics; #if FEZCLIENT using ActionType = FezGame.Structure.ActionType; @@ -14,17 +15,10 @@ using Viewpoint = FezEngine.Viewpoint; using Vector3 = Microsoft.Xna.Framework.Vector3; #else -using ActionType = System.Int32; -using HorizontalDirection = System.Int32; -using Viewpoint = System.Int32; -public struct Vector3 -{ - public float X, Y, Z; - public Vector3(float x, float y, float z) - { - X = x; Y = y; Z = z; - } -} +using ActionType = FezMultiplayerDedicatedServer.ActionType; +using HorizontalDirection = FezMultiplayerDedicatedServer.HorizontalDirection; +using Viewpoint = FezMultiplayerDedicatedServer.Viewpoint; +using Vector3 = FezMultiplayerDedicatedServer.Vector3; #endif namespace FezSharedTools { @@ -362,9 +356,14 @@ protected void ReadClientGameTickPacket(BinaryNetworkReader reader, ref MiscClie retval.Metadata = playerMetadata; retval.Disconnecting = Disconnecting; } - protected void WriteClientGameTickPacket(BinaryNetworkWriter writer0, PlayerMetadata playerMetadata, SaveDataUpdate? saveDataUpdate, ActiveLevelState? levelState, + /// + /// + /// the amount of time, in ticks, it took to write the data to the network + protected long WriteClientGameTickPacket(BinaryNetworkWriter writer0, PlayerMetadata playerMetadata, SaveDataUpdate? saveDataUpdate, ActiveLevelState? levelState, PlayerAppearance? appearance, ICollection requestPlayerAppearance, bool Disconnecting) { + Stopwatch sw = new Stopwatch(); + int datalength; //optimize network writing so it doesn't send a bazillion packets for a single tick using (MemoryStream ms = new MemoryStream()) { @@ -397,8 +396,14 @@ protected void WriteClientGameTickPacket(BinaryNetworkWriter writer0, PlayerMeta writer.Write(Disconnecting); writer.Flush(); } - writer0.Write(ms.ToArray()); + byte[] data = ms.ToArray(); + datalength = data.Length; + sw.Start(); + writer0.Write(data); + writer0.Flush(); + sw.Stop(); } + return sw.ElapsedTicks / datalength; } protected void ReadServerGameTickPacket(BinaryNetworkReader reader, ref bool RetransmitAppearance) { @@ -448,10 +453,15 @@ protected void ReadServerGameTickPacket(BinaryNetworkReader reader, ref bool Ret } RetransmitAppearance = reader.ReadBoolean(); } - protected void WriteServerGameTickPacket(BinaryNetworkWriter writer0, List playerMetadatas, SaveDataUpdate? saveDataUpdate, ICollection levelStates, + /// + /// + /// the amount of time, in ticks, it took to write the data to the network + protected long WriteServerGameTickPacket(BinaryNetworkWriter writer0, List playerMetadatas, SaveDataUpdate? saveDataUpdate, ICollection levelStates, ICollection disconnectedPlayers, IDictionary appearances, Guid? NewClientGuid, bool RequestAppearance, SharedSaveData sharedSaveData) { + Stopwatch sw = new Stopwatch(); + int datalength; //optimize network writing so it doesn't send a bazillion packets for a single tick using (MemoryStream ms = new MemoryStream()) { @@ -494,8 +504,14 @@ protected void WriteServerGameTickPacket(BinaryNetworkWriter writer0, List