Skip to content

Commit

Permalink
refactoring, added connection retry
Browse files Browse the repository at this point in the history
  • Loading branch information
Jenna1337 committed Dec 4, 2024
1 parent c6b87e6 commit f0b3eb0
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 45 deletions.
38 changes: 37 additions & 1 deletion FezMultiplayerMod/MultiplayerMod/FezMultiplayerMod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@ public FezMultiplayerMod(Game game)
mp = new MultiplayerClient(settings);
IniTools.WriteSettingsFile(SettingsFilePath, settings);

mp.ConnectToServerAsync(settings.MainEndpoint);

//TODO add a in-game menu to let players easily choose what server and name they want to use

drawer = new SpriteBatch(GraphicsDevice);
mesh.AddFace(new Vector3(1f), new Vector3(0f, 0.25f, 0f), FaceOrientation.Front, centeredOnOrigin: true, doublesided: true);
}
Expand Down Expand Up @@ -145,7 +149,39 @@ public override void Update(GameTime gameTime)
{
ShowDebugInfo = !ShowDebugInfo;
}
mp.Update();
try
{
mp.Update();
}
catch (VersionMismatchException e)
{
//Server replied with data that is for a different network version of FezMultiplayerMod
//TODO tell the user the desired FezMultiplayerServer is using a network protocol that is incompatible with their version.
throw e;
}
catch (System.IO.InvalidDataException e)
{
//Server replied with data that is not related to FezMultiplayerMod or data is malformed
//TODO tell the user the IP endpoint provided is not a FezMultiplayerServer.
throw e;
}
catch (System.IO.IOException e)
{
//Connection failed, data read error, connection timeout, connection terminated by server, etc.
//TODO
throw e;
}
catch (Exception e)
{
#if DEBUG
if (!System.Diagnostics.Debugger.IsAttached)
{
System.Diagnostics.Debugger.Launch();
}
System.Diagnostics.Debugger.Break();
#endif
throw e;//This should never happen
}
}

private void PreDraw(GameTime gameTime)
Expand Down
9 changes: 3 additions & 6 deletions FezMultiplayerMod/MultiplayerMod/MultiplayerClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
namespace FezGame.MultiplayerMod
{
/// <summary>
/// The class that contains all the networking stuff
/// The GameComponent-like class that handles updating data from the Game and updating data in the Game
/// </summary>
public class MultiplayerClient : MultiplayerClientNetcode, IDisposable
{
Expand All @@ -35,20 +35,17 @@ public class MultiplayerClient : MultiplayerClientNetcode, IDisposable
[ServiceDependency]
public IGameCameraManager CameraManager { private get; set; }

public bool SyncWorldState;

/// <summary>
/// Creates a new instance of this class with the provided parameters.
/// For any errors that get encountered see <see cref="ErrorMessage"/> an <see cref="FatalException"/>
/// For any errors that get encountered see <see cref="ErrorMessage"/> and <see cref="FatalException"/>
/// Use <c>ConnectToServerAsync(IPEndPoint)</c> to connect to a server.
/// </summary>
/// <param name="settings">The <see cref="MultiplayerClientSettings"/> to use to create this instance.</param>
internal MultiplayerClient(MultiplayerClientSettings settings) : base(settings)
{
_ = Waiters.Wait(() => ServiceHelper.FirstLoadDone, () => ServiceHelper.InjectServices(this));

OnUpdate += UpdateMyPlayer;

SyncWorldState = settings.SyncWorldState;
}

public void UpdateMyPlayer()
Expand Down
72 changes: 36 additions & 36 deletions FezMultiplayerMod/MultiplayerMod/MultiplayerClientNetcode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,16 @@

namespace FezGame.MultiplayerMod
{
/// <summary>
/// The class that contains all the networking stuff
///
/// Note: This class should only contain System usings
/// </summary>
public abstract class MultiplayerClientNetcode : SharedNetcode<PlayerMetadata>, IDisposable
{

private readonly Thread listenerThread;
private Thread listenerThread;

public Guid MyUuid { get; private set; }
public volatile PlayerMetadata MyPlayerMetadata = null;
public override ConcurrentDictionary<Guid, PlayerMetadata> Players { get; } = new ConcurrentDictionary<Guid, PlayerMetadata>();
public volatile bool Listening;
private volatile bool listening;
public bool Listening { get => listening; }
public PlayerAppearance MyAppearance;
private volatile bool MyAppearanceChanged = false;
public volatile uint ConnectionLatencyUp = 0;
Expand All @@ -41,6 +37,9 @@ public string MyPlayerName
}
}


public volatile bool SyncWorldState;

public event Action OnUpdate = () => { };
public event Action OnDispose = () => { };

Expand All @@ -51,17 +50,25 @@ public string MyPlayerName
/// <param name="settings">The <see cref="MultiplayerClientSettings"/> to use to create this instance.</param>
internal MultiplayerClientNetcode(MultiplayerClientSettings settings)
{
Listening = false;
listening = false;
SyncWorldState = settings.SyncWorldState;
MyAppearance = new PlayerAppearance(settings.MyPlayerName, settings.Appearance);
}

public void ConnectToServerAsync(IPEndPoint endpoint)
{
if (listenerThread != null && listenerThread.IsAlive)
{
//TODO
}
listenerThread = new Thread(() =>
{
bool WasSucessfullyConnected = false;
try
void ConnectToServerInternal(out bool ConnectionSucessful)
{
ConnectionSucessful = false;
TcpClient tcpClient = new TcpClient(AddressFamily.InterNetwork);
tcpClient.Connect(settings.MainEndpoint);
Listening = true;
tcpClient.Connect(endpoint);
listening = true;
while (MyPlayerMetadata == null)
{
Thread.Sleep(100);
Expand All @@ -73,26 +80,26 @@ internal MultiplayerClientNetcode(MultiplayerClientSettings settings)
bool retransmitAppearanceRequested = false;
ReadServerGameTickPacket(reader, ref retransmitAppearanceRequested);
ConnectionLatencyUp = (uint)WriteClientGameTickPacket(writer, MyPlayerMetadata, null, null, MyAppearance, UnknownPlayerAppearanceGuids.Keys, false);
WasSucessfullyConnected = true;
ConnectionSucessful = true;
while (true)
{
ReadServerGameTickPacket(reader, ref retransmitAppearanceRequested);
if (!disposing)
{
ActiveLevelState? activeLevelState = null;
if (settings.SyncWorldState)
if (SyncWorldState)
{
activeLevelState = GetCurrentLevelState();
}

SaveDataUpdate? saveDataUpdate = null;
if (settings.SyncWorldState)
if (SyncWorldState)
{
saveDataUpdate = GetSaveDataUpdate();
}
//TODO transmit MyAppearance whenever its value changes
PlayerAppearance? appearance = null;
if(retransmitAppearanceRequested || MyAppearanceChanged)
if (retransmitAppearanceRequested || MyAppearanceChanged)
{
appearance = MyAppearance;
}
Expand All @@ -110,36 +117,36 @@ internal MultiplayerClientNetcode(MultiplayerClientSettings settings)
tcpClient.Close();
}
}
catch (VersionMismatchException e)
{
FatalException = e;
}
catch (InvalidDataException e)
bool WasSucessfullyConnected = false;
try
{
FatalException = e;
ConnectToServerInternal(out WasSucessfullyConnected);
}
//catch (EndOfStreamException e)
//{
// FatalException = e;
//}
catch (IOException e)
catch (IOException e)//Connection failed, data read error, connection terminated by server, etc.
{
if(WasSucessfullyConnected){
//TODO retry connection?
if (WasSucessfullyConnected)
{
//retry connection
ConnectToServerInternal(out WasSucessfullyConnected);
//TODO retry connection multiple times?
FatalException = e;
}
else
{
FatalException = e;
}
}
catch (Exception e)
catch (Exception e)// Note: VersionMismatchException and InvalidDataException also get caught here
{
FatalException = e;
}
finally
{
Listening = false;
listening = false;
}
});
listenerThread.Start();
Expand Down Expand Up @@ -191,17 +198,10 @@ public void Update()
{
if (FatalException != null)
{
#if DEBUG
if (!System.Diagnostics.Debugger.IsAttached)
{
System.Diagnostics.Debugger.Launch();
}
System.Diagnostics.Debugger.Break();
#endif
throw FatalException;//This should never happen
throw FatalException;
}

if (!Listening)
if (!listening)
{
return;
}
Expand Down
4 changes: 2 additions & 2 deletions Shared/Shared.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ public static class FezMultiplayerBinaryIOExtensions
/// <param name="reader">The <see cref="BinaryNetworkReader"/> from which to read the string.</param>
/// <param name="maxLength">The maximum allowable length for the string. Any length greater than this will result in an exception.</param>
/// <returns>A string read from the binary stream, decoded using UTF-8.</returns>
/// <exception cref="ArgumentOutOfRangeException">
/// <exception cref="InvalidDataException">
/// Thrown when the length of the string read (specified by the first 4 bytes) is outside the allowed range of 0 to <paramref name="maxLength"/>.
/// This exception is raised to prevent the application from processing excessively long data, which could lead to denial of service or allocate undue resources.
/// </exception>
Expand All @@ -149,7 +149,7 @@ public static string ReadStringAsByteArrayWithLength(this BinaryNetworkReader re
int length = reader.ReadInt32();
if (length > maxLength || length < 0)
{
throw new ArgumentOutOfRangeException($"The length {length} is outside the allowed range of {minLength} to {maxLength}.");
throw new InvalidDataException($"The length {length} is outside the allowed range of {minLength} to {maxLength}.");
}
else
{
Expand Down

0 comments on commit f0b3eb0

Please sign in to comment.