Skip to content

Commit

Permalink
Rework debugger (#79)
Browse files Browse the repository at this point in the history
  • Loading branch information
js6pak authored Mar 2, 2024
1 parent d82268b commit 5db20ca
Show file tree
Hide file tree
Showing 27 changed files with 1,068 additions and 180 deletions.
82 changes: 82 additions & 0 deletions Reactor.Debugger/AutoJoin/AutoJoinClientConnection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.IO.Pipes;
using InnerNet;
using Reactor.Debugger.AutoJoin.Messages;
using Reactor.Utilities;

namespace Reactor.Debugger.AutoJoin;

internal sealed class AutoJoinClientConnection : AutoJoinConnection
{
public const string PipeName = "Reactor.Debugger.AutoJoin";

private AutoJoinClientConnection(NamedPipeClientStream pipe) : base(pipe)
{
}

protected override void Handle(BinaryReader reader, MessageType messageType)
{
switch (messageType)
{
case MessageType.JoinGame:
{
Handle(JoinGameMessage.Deserialize(reader));
break;
}

default:
{
throw new ArgumentOutOfRangeException(nameof(messageType), messageType, null);
}
}
}

private static void Handle(in JoinGameMessage message)
{
var (address, port, gameCode) = message;

Dispatcher.Instance.Enqueue(() =>
{
var gameCodeText = gameCode == InnerNetServer.LocalGameId ? "<local>" : GameCode.IntToGameName(gameCode);
Info($"Joining {gameCodeText} on {address}:{port}");
if (gameCode == InnerNetServer.LocalGameId)
{
AmongUsClient.Instance.NetworkMode = NetworkModes.LocalGame;
AmongUsClient.Instance.GameId = gameCode;
AmongUsClient.Instance.StartCoroutine(AmongUsClient.Instance.CoConnectToGameServer(MatchMakerModes.Client, address, port, null));
}
else
{
AmongUsClient.Instance.StartCoroutine(AmongUsClient.Instance.CoJoinOnlineGameDirect(gameCode, address, port, null));
}
});
}

public void SendRequestJoin()
{
Write(default(RequestJoinGameMessage));
}

public static bool TryConnect([NotNullWhen(true)] out AutoJoinClientConnection? client)
{
var pipeClient = new NamedPipeClientStream(".", PipeName, PipeDirection.InOut, PipeOptions.Asynchronous);

try
{
pipeClient.Connect(100);

client = new AutoJoinClientConnection(pipeClient);
return true;
}
catch (TimeoutException)
{
pipeClient.Dispose();

client = null;
return false;
}
}
}
61 changes: 61 additions & 0 deletions Reactor.Debugger/AutoJoin/AutoJoinConnection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System;
using System.IO;
using System.IO.Pipes;
using System.Threading.Tasks;
using Reactor.Debugger.AutoJoin.Messages;

namespace Reactor.Debugger.AutoJoin;

internal abstract class AutoJoinConnection : IDisposable
{
private readonly BinaryWriter _writer;

protected AutoJoinConnection(PipeStream pipe)
{
_writer = new BinaryWriter(pipe);

Task.Run(() =>
{
try
{
var reader = new BinaryReader(pipe);
while (pipe.IsConnected)
{
try
{
var messageType = (MessageType) reader.ReadByte();
Debug($"Received {messageType}");
Handle(reader, messageType);
}
catch (EndOfStreamException)
{
break;
}
}
pipe.Dispose();
Disconnected?.Invoke();
}
catch (Exception e)
{
Error(e);
}
});
}

public event Action? Disconnected;

protected abstract void Handle(BinaryReader reader, MessageType messageType);

public void Write<T>(in T message) where T : IMessage<T>
{
Debug($"Writing {message}");
_writer.Write(message);
}

public void Dispose()
{
_writer.Dispose();
}
}
113 changes: 113 additions & 0 deletions Reactor.Debugger/AutoJoin/AutoJoinConnectionListener.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO.Pipes;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using InnerNet;
using Reactor.Debugger.AutoJoin.Messages;

namespace Reactor.Debugger.AutoJoin;

internal sealed class AutoJoinConnectionListener : IDisposable
{
private readonly CancellationTokenSource _cancellationTokenSource = new();
private readonly Mutex _mutex;

private AutoJoinConnectionListener(Mutex mutex)
{
_mutex = mutex;

Task.Run(async () =>
{
while (!_cancellationTokenSource.IsCancellationRequested)
{
try
{
Info("Listening for connections");
var pipeServer = new NamedPipeServerStream(
AutoJoinClientConnection.PipeName,
PipeDirection.InOut,
NamedPipeServerStream.MaxAllowedServerInstances,
PipeTransmissionMode.Byte,
PipeOptions.Asynchronous
);
try
{
await pipeServer.WaitForConnectionAsync(_cancellationTokenSource.Token);
}
catch (OperationCanceledException)
{
pipeServer.Dispose();
break;
}
Info("Client connected");
var client = new AutoJoinServerConnection(pipeServer);
Clients.Add(client);
client.Disconnected += () =>
{
Clients.Remove(client);
client.Dispose();
};
}
catch (Exception e)
{
Error(e);
}
}
Info("Stopped");
Stopped?.Invoke();
});
}

public event Action? Stopped;

public List<AutoJoinServerConnection> Clients { get; } = new();

public void Dispose()
{
_cancellationTokenSource.Cancel();
_cancellationTokenSource.Dispose();

foreach (var client in Clients)
{
client.Dispose();
}

Clients.Clear();

_mutex.Dispose();
}

public void SendJoinMe(InnerNetClient innerNetClient)
{
var joinGameMessage = JoinGameMessage.From(innerNetClient);

foreach (var client in Clients)
{
client.Write(joinGameMessage);
}
}

public static bool TryStart([NotNullWhen(true)] out AutoJoinConnectionListener? connectionListener)
{
var mutex = new Mutex(false, $@"Global\{AutoJoinClientConnection.PipeName}");

if (mutex.WaitOne(100))
{
connectionListener = new AutoJoinConnectionListener(mutex);
return true;
}

mutex.Dispose();

connectionListener = null;
return false;
}
}
66 changes: 66 additions & 0 deletions Reactor.Debugger/AutoJoin/AutoJoinConnectionManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using HarmonyLib;
using InnerNet;

namespace Reactor.Debugger.AutoJoin;

internal static class AutoJoinConnectionManager
{
public static AutoJoinConnectionListener? Server { get; set; }
public static AutoJoinClientConnection? Client { get; set; }

public static void StartOrConnect()
{
if (AutoJoinConnectionListener.TryStart(out var connectionListener))
{
Info("Started an AutoJoin session");
Server = connectionListener;
Server.Stopped += () =>
{
if (Server == connectionListener)
{
Server = null;
}
};
}
else if (AutoJoinClientConnection.TryConnect(out var client))
{
Info("Connected to an AutoJoin session");
Client = client;
client.Disconnected += () =>
{
if (Client == client)
{
Client = null;
Info("Disconnected from an AutoJoin session, trying to reconnect");
StartOrConnect();
}
};
}
else
{
Error("Failed to start or connect to an AutoJoin session");
}
}

[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.Start))]
public static class ConnectPatch
{
public static void Postfix(InnerNetClient __instance)
{
if (!DebuggerConfig.JoinGameOnStart.Value) return;

if (__instance.TryCast<AmongUsClient>() is { } amongUsClient)
{
if (Server != null)
{
amongUsClient.StartCoroutine(amongUsClient.CoCreateOnlineGame());
}
else if (Client is { } client)
{
client.SendRequestJoin();
}
}
}
}
}
38 changes: 38 additions & 0 deletions Reactor.Debugger/AutoJoin/AutoJoinServerConnection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System;
using System.IO;
using System.IO.Pipes;
using Reactor.Debugger.AutoJoin.Messages;

namespace Reactor.Debugger.AutoJoin;

internal sealed class AutoJoinServerConnection : AutoJoinConnection
{
public AutoJoinServerConnection(NamedPipeServerStream pipe) : base(pipe)
{
}

protected override void Handle(BinaryReader reader, MessageType messageType)
{
switch (messageType)
{
case MessageType.RequestJoinGame:
{
Handle(RequestJoinGameMessage.Deserialize(reader));
break;
}

default:
{
throw new ArgumentOutOfRangeException(nameof(messageType), messageType, null);
}
}
}

private void Handle(in RequestJoinGameMessage message)
{
if (AmongUsClient.Instance.AmConnected)
{
Write(JoinGameMessage.From(AmongUsClient.Instance));
}
}
}
12 changes: 12 additions & 0 deletions Reactor.Debugger/AutoJoin/Messages/Extensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.IO;

namespace Reactor.Debugger.AutoJoin.Messages;

internal static class Extensions
{
public static void Write<T>(this BinaryWriter writer, in T message) where T : IMessage<T>
{
writer.Write((byte) T.Type);
message.Serialize(writer);
}
}
15 changes: 15 additions & 0 deletions Reactor.Debugger/AutoJoin/Messages/IMessage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#pragma warning disable CA2252 // TODO Remove after BepInEx updates the .NET version
using System.IO;

namespace Reactor.Debugger.AutoJoin.Messages;

internal interface IMessage<TSelf> where TSelf : IMessage<TSelf>
{
static abstract MessageType Type { get; }

void Serialize(BinaryWriter writer);

static abstract TSelf Deserialize(BinaryReader reader);

string ToString();
}
Loading

0 comments on commit 5db20ca

Please sign in to comment.