diff --git a/AmongUs.props b/AmongUs.props
index 932a068..3908f13 100644
--- a/AmongUs.props
+++ b/AmongUs.props
@@ -1,6 +1,6 @@
- 2021.3.5s
+ 2021.3.31.3s
$(DefineConstants);STEAM
@@ -10,11 +10,11 @@
- NuclearPowered/Mappings:0.2.0
+ NuclearPowered/Mappings:0.3.0
-
+
diff --git a/Reactor.Debugger/DebuggerPlugin.cs b/Reactor.Debugger/DebuggerPlugin.cs
index 9a659d6..36dc0df 100644
--- a/Reactor.Debugger/DebuggerPlugin.cs
+++ b/Reactor.Debugger/DebuggerPlugin.cs
@@ -3,6 +3,7 @@ using System.Linq;
using BepInEx;
using BepInEx.IL2CPP;
using HarmonyLib;
+using InnerNet;
using Reactor.Extensions;
using UnhollowerBaseLib;
using UnhollowerBaseLib.Attributes;
diff --git a/Reactor.Example/CustomRpcs.cs b/Reactor.Example/CustomRpcs.cs
new file mode 100644
index 0000000..5568438
--- /dev/null
+++ b/Reactor.Example/CustomRpcs.cs
@@ -0,0 +1,7 @@
+namespace Reactor.Example
+{
+ public enum CustomRpcCalls : uint
+ {
+ Example
+ }
+}
diff --git a/Reactor.Example/ExamplePlugin.cs b/Reactor.Example/ExamplePlugin.cs
index f64d39e..c729d2c 100644
--- a/Reactor.Example/ExamplePlugin.cs
+++ b/Reactor.Example/ExamplePlugin.cs
@@ -1,7 +1,9 @@
using System;
using BepInEx;
using BepInEx.IL2CPP;
+using InnerNet;
using Reactor.Extensions;
+using Reactor.Networking;
using UnhollowerBaseLib.Attributes;
using UnityEngine;
diff --git a/Reactor.Example/ExampleRpc.cs b/Reactor.Example/ExampleRpc.cs
index f95bd45..f6f5899 100644
--- a/Reactor.Example/ExampleRpc.cs
+++ b/Reactor.Example/ExampleRpc.cs
@@ -1,11 +1,12 @@
using Hazel;
+using Reactor.Networking;
namespace Reactor.Example
{
- [RegisterCustomRpc]
+ [RegisterCustomRpc((uint) CustomRpcCalls.Example)]
public class ExampleRpc : PlayerCustomRpc
{
- public ExampleRpc(ExamplePlugin plugin) : base(plugin)
+ public ExampleRpc(ExamplePlugin plugin, uint id) : base(plugin, id)
{
}
diff --git a/Reactor.Shared/Networking/Mod.cs b/Reactor.Shared/Networking/Mod.cs
new file mode 100644
index 0000000..4f58f79
--- /dev/null
+++ b/Reactor.Shared/Networking/Mod.cs
@@ -0,0 +1,40 @@
+#nullable enable
+
+using System;
+
+namespace Reactor.Networking
+{
+ public class Mod
+ {
+ public Mod(uint netId, string id, string version, PluginSide side)
+ {
+ NetId = netId;
+ Id = id;
+ Version = version;
+ Side = side;
+ }
+
+ public uint NetId { get; }
+ public string Id { get; }
+ public string Version { get; }
+ public PluginSide Side { get; }
+
+ protected bool Equals(Mod other)
+ {
+ return Id == other.Id && Version == other.Version;
+ }
+
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != GetType()) return false;
+ return Equals((Mod) obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return HashCode.Combine(Id, Version);
+ }
+ }
+}
diff --git a/Reactor.Shared/Networking/ReactorMessageFlags.cs b/Reactor.Shared/Networking/ReactorMessageFlags.cs
new file mode 100644
index 0000000..4e7462a
--- /dev/null
+++ b/Reactor.Shared/Networking/ReactorMessageFlags.cs
@@ -0,0 +1,9 @@
+namespace Reactor.Networking
+{
+ public enum ReactorMessageFlags : byte
+ {
+ Handshake,
+ ModDeclaration,
+ PluginDeclaration
+ }
+}
diff --git a/Reactor.Shared/Networking/ReactorProtocolVersion.cs b/Reactor.Shared/Networking/ReactorProtocolVersion.cs
new file mode 100644
index 0000000..dde00fe
--- /dev/null
+++ b/Reactor.Shared/Networking/ReactorProtocolVersion.cs
@@ -0,0 +1,18 @@
+namespace Reactor.Networking
+{
+ ///
+ /// Version of the Reactor.Net protocol format.
+ ///
+ public enum ReactorProtocolVersion : byte
+ {
+ ///
+ /// Initial version.
+ ///
+ Initial = 1,
+
+ ///
+ /// Latest version.
+ ///
+ Latest = Initial,
+ }
+}
diff --git a/Reactor.Shared/PluginSide.cs b/Reactor.Shared/PluginSide.cs
new file mode 100644
index 0000000..f895e30
--- /dev/null
+++ b/Reactor.Shared/PluginSide.cs
@@ -0,0 +1,18 @@
+namespace Reactor
+{
+ ///
+ /// Plugin side used in modded handshake
+ ///
+ public enum PluginSide : byte
+ {
+ ///
+ /// Required by both sides, reject connection if missing on the other side
+ ///
+ Both,
+
+ ///
+ /// Required only by client
+ ///
+ ClientOnly
+ }
+}
diff --git a/Reactor.Shared/Reactor.Shared.projitems b/Reactor.Shared/Reactor.Shared.projitems
new file mode 100644
index 0000000..9c4059e
--- /dev/null
+++ b/Reactor.Shared/Reactor.Shared.projitems
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/Reactor.Shared/Reactor.Shared.shproj b/Reactor.Shared/Reactor.Shared.shproj
new file mode 100644
index 0000000..43dae88
--- /dev/null
+++ b/Reactor.Shared/Reactor.Shared.shproj
@@ -0,0 +1,3 @@
+
+
+
diff --git a/Reactor.sln b/Reactor.sln
index 3cada7a..9dcf9fc 100644
--- a/Reactor.sln
+++ b/Reactor.sln
@@ -9,6 +9,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{E2F4DAFA
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Reactor.Example", "Reactor.Example\Reactor.Example.csproj", "{26A83466-26FE-4383-A35C-B8629902F909}"
EndProject
+Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Reactor.Shared", "Reactor.Shared\Reactor.Shared.shproj", "{8C1EEFE9-EF8A-47C4-810E-6D9BA1E7218B}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
diff --git a/Reactor/Coroutines.cs b/Reactor/Coroutines.cs
index a4cfbee..a2af356 100644
--- a/Reactor/Coroutines.cs
+++ b/Reactor/Coroutines.cs
@@ -343,7 +343,7 @@ namespace Reactor
}
catch (Exception e)
{
- PluginSingleton.Instance.Log.LogError(e.ToString());
+ Logger.Error(e.ToString());
Stop(FindOriginalCoroutine(enumerator)); // We want the entire coroutine hierarchy to stop when an error happen
}
@@ -366,7 +366,7 @@ namespace Reactor
if (nextAsEnumerator != null) // il2cpp IEnumerator also handles CustomYieldInstruction
next = new Il2CppEnumeratorWrapper(nextAsEnumerator);
else
- PluginSingleton.Instance.Log.LogWarning($"Unknown coroutine yield object of type {il2CppObjectBase} for coroutine {enumerator}");
+ Logger.Warning($"Unknown coroutine yield object of type {il2CppObjectBase} for coroutine {enumerator}");
break;
}
diff --git a/Reactor/Extensions/Extensions.cs b/Reactor/Extensions/Extensions.cs
index 124dc80..492a375 100644
--- a/Reactor/Extensions/Extensions.cs
+++ b/Reactor/Extensions/Extensions.cs
@@ -51,7 +51,7 @@ namespace Reactor.Extensions
/// https://docs.unity3d.com/ScriptReference/ColorUtility.ToHtmlStringRGBA.html
public static string ToHtmlStringRGBA(this Color32 color)
{
- return $"{color.r:X2}{color.g:X2}{color.b:X2}{color.a:X2}";
+ return ColorUtility.ToHtmlStringRGBA(color);
}
///
diff --git a/Reactor/Extensions/MessageExtensions.cs b/Reactor/Extensions/ExtraMessageExtensions.cs
similarity index 55%
rename from Reactor/Extensions/MessageExtensions.cs
rename to Reactor/Extensions/ExtraMessageExtensions.cs
index 163ac41..73c587c 100644
--- a/Reactor/Extensions/MessageExtensions.cs
+++ b/Reactor/Extensions/ExtraMessageExtensions.cs
@@ -1,9 +1,11 @@
+using System;
using Hazel;
+using Hazel.Udp;
using UnityEngine;
namespace Reactor.Extensions
{
- public static class MessageExtensions
+ public static class ExtraMessageExtensions
{
private const float MIN = -50f;
private const float MAX = 50f;
@@ -30,15 +32,19 @@ namespace Reactor.Extensions
return new Vector2(Mathf.Lerp(MIN, MAX, x), Mathf.Lerp(MIN, MAX, y));
}
- // public static void Write(MessageWriter writer, InnerNetObject value)
- // {
- // global::MessageExtensions.WriteNetObject(writer, value);
- // }
+ public static void Send(this UdpConnection connection, MessageWriter msg, Action ackCallback)
+ {
+ if (msg.SendOption != SendOption.Reliable)
+ throw new InvalidOperationException("Message SendOption has to be Reliable.");
- // TODO OxygenFilter crashes, Mono.Cecil hates generic arguments, I have no idea why
- // public static T ReadNetObject(MessageReader reader) where T : InnerNetObject
- // {
- // return global::MessageExtensions.ReadNetObject(reader);
- // }
+ var buffer = new byte[msg.Length];
+ Buffer.BlockCopy(msg.Buffer, 0, buffer, 0, msg.Length);
+
+ connection.ResetKeepAliveTimer();
+
+ connection.AttachReliableID(buffer, 1, ackCallback);
+ connection.WriteBytesToConnection(buffer, buffer.Length);
+ connection.Statistics.LogReliableSend(buffer.Length - 3, buffer.Length);
+ }
}
}
diff --git a/Reactor/Logger.cs b/Reactor/Logger.cs
new file mode 100644
index 0000000..8b6a9a8
--- /dev/null
+++ b/Reactor/Logger.cs
@@ -0,0 +1,28 @@
+using BepInEx.IL2CPP;
+using BepInEx.Logging;
+
+namespace Reactor
+{
+ public static class Logger where T : BasePlugin
+ {
+ public static ManualLogSource Instance => PluginSingleton.Instance.Log;
+
+ ///
+ public static void Fatal(object data) => Instance.LogFatal(data);
+
+ ///
+ public static void Error(object data) => Instance.LogError(data);
+
+ ///
+ public static void Warning(object data) => Instance.LogWarning(data);
+
+ ///
+ public static void Message(object data) => Instance.LogMessage(data);
+
+ ///
+ public static void Info(object data) => Instance.LogInfo(data);
+
+ ///
+ public static void Debug(object data) => Instance.LogDebug(data);
+ }
+}
diff --git a/Reactor/Net/Mod.cs b/Reactor/Net/Mod.cs
deleted file mode 100644
index f6870dd..0000000
--- a/Reactor/Net/Mod.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-namespace Reactor.Net
-{
- public readonly struct Mod
- {
- public readonly string Id;
- public readonly string Version;
- public readonly PluginSide Side;
-
- public Mod(string id, string version, PluginSide side)
- {
- Id = id;
- Version = version;
- Side = side;
- }
-
- public override string ToString()
- {
- return $"{Id} ({Version})";
- }
- }
-}
diff --git a/Reactor/Net/ModList.cs b/Reactor/Net/ModList.cs
deleted file mode 100644
index ebcaafd..0000000
--- a/Reactor/Net/ModList.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-using BepInEx.IL2CPP;
-using Hazel;
-
-namespace Reactor.Net
-{
- public static class ModList
- {
- public static void Serialize(MessageWriter writer, ISet mods)
- {
- writer.WritePacked(mods.Count);
-
- foreach (var mod in mods)
- {
- writer.Write(mod.Id);
- writer.Write(mod.Version);
- writer.Write((byte) mod.Side);
- }
- }
-
- public static ISet GetCurrent()
- {
- return IL2CPPChainloader.Instance.Plugins
- .Select(plugin => new Mod(
- plugin.Key,
- plugin.Value.Metadata.Version.ToString(),
- plugin.Value.Instance.GetType().GetCustomAttribute()?.Side ?? PluginSide.Both
- ))
- .ToHashSet();
- }
- }
-}
diff --git a/Reactor/Net/ModdedHandshakeC2S.cs b/Reactor/Net/ModdedHandshakeC2S.cs
deleted file mode 100644
index d071ed3..0000000
--- a/Reactor/Net/ModdedHandshakeC2S.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System.Collections.Generic;
-using Hazel;
-using UnhollowerBaseLib;
-
-namespace Reactor.Net
-{
- public static class ModdedHandshakeC2S
- {
- public static void Serialize(MessageWriter writer, Il2CppStructArray clientVersion, string name, ISet mods = null)
- {
- writer.Write(clientVersion);
- writer.Write(name);
-
- if (mods != null)
- {
- ModList.Serialize(writer, mods);
- }
- }
- }
-}
diff --git a/Reactor/Net/ModdedHandshakeS2C.cs b/Reactor/Net/ModdedHandshakeS2C.cs
deleted file mode 100644
index 63b9d9b..0000000
--- a/Reactor/Net/ModdedHandshakeS2C.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using Hazel;
-
-namespace Reactor.Net
-{
- public static class ModdedHandshakeS2C
- {
- public static void Serialize(MessageWriter writer, string serverBrand)
- {
- writer.StartMessage(byte.MaxValue);
- writer.Write(serverBrand);
- writer.EndMessage();
- }
- }
-}
diff --git a/Reactor/CustomRpc.cs b/Reactor/Networking/CustomRpc.cs
similarity index 76%
rename from Reactor/CustomRpc.cs
rename to Reactor/Networking/CustomRpc.cs
index 13fc10f..2bcf351 100644
--- a/Reactor/CustomRpc.cs
+++ b/Reactor/Networking/CustomRpc.cs
@@ -1,17 +1,14 @@
using System;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
using System.Reflection;
-using System.Text;
using BepInEx;
using BepInEx.IL2CPP;
using HarmonyLib;
using Hazel;
using InnerNet;
-using Reactor.Net;
-namespace Reactor
+namespace Reactor.Networking
{
public enum RpcLocalHandling
{
@@ -22,9 +19,9 @@ namespace Reactor
public abstract class UnsafeCustomRpc
{
- public int? Id { get; internal set; }
-
internal CustomRpcManager Manager { get; set; }
+
+ public uint Id { get; }
public BasePlugin UnsafePlugin { get; }
public string PluginId { get; }
@@ -33,10 +30,11 @@ namespace Reactor
public virtual SendOption SendOption { get; } = SendOption.Reliable;
public abstract RpcLocalHandling LocalHandling { get; }
- protected UnsafeCustomRpc(BasePlugin plugin)
+ protected UnsafeCustomRpc(BasePlugin plugin, uint id)
{
UnsafePlugin = plugin;
PluginId = MetadataHelper.GetMetadata(plugin).GUID;
+ Id = id;
}
public abstract void UnsafeWrite(MessageWriter writer, object data);
@@ -47,7 +45,7 @@ namespace Reactor
{
if (netObject == null) throw new ArgumentNullException(nameof(netObject));
- if (Id == null)
+ if (Manager == null)
{
throw new InvalidOperationException("Can't send unregistered CustomRpc");
}
@@ -63,17 +61,13 @@ namespace Reactor
true => AmongUsClient.Instance.StartRpcImmediately(netObject.NetId, CustomRpcManager.CallId, SendOption, targetClientId)
};
- if (Manager.PluginIdMap.TryGetValue(PluginId, out var pluginId))
- {
- writer.WritePacked(pluginId);
- }
- else
- {
- writer.Write(PluginId);
- }
+ var pluginNetId = ModList.Current.Single(x => x.Id == PluginId).NetId;
+ writer.WritePacked(pluginNetId);
+ writer.WritePacked(Id);
- writer.WritePacked(Id!.Value);
+ writer.StartMessage(0);
UnsafeWrite(writer, data);
+ writer.EndMessage();
if (immediately)
{
@@ -93,7 +87,7 @@ namespace Reactor
public abstract class CustomRpc : UnsafeCustomRpc where TPlugin : BasePlugin where TInnerNetObject : InnerNetObject
{
- protected CustomRpc(TPlugin plugin) : base(plugin)
+ protected CustomRpc(TPlugin plugin, uint id) : base(plugin, id)
{
}
@@ -132,7 +126,7 @@ namespace Reactor
public abstract class PlayerCustomRpc : CustomRpc where TPlugin : BasePlugin
{
- protected PlayerCustomRpc(TPlugin plugin) : base(plugin)
+ protected PlayerCustomRpc(TPlugin plugin, uint id) : base(plugin, id)
{
}
@@ -160,9 +154,6 @@ namespace Reactor
_map.SelectMany(pair => pair.Value, (pair, value) => new { pair.Key, Value = value })
.ToLookup(pair => pair.Key, pair => pair.Value);
- public Dictionary PluginIdMap { get; private set; }
- public Dictionary PluginIdMapReversed { get; private set; }
-
public CustomRpcManager()
{
foreach (var type in HandleRpcPatch.InnerNetObjectTypes)
@@ -171,28 +162,14 @@ namespace Reactor
}
}
- public void ReloadPluginIdMap()
- {
- PluginIdMap = new Dictionary();
- PluginIdMapReversed = new Dictionary();
-
- var i = -1;
-
- foreach (var mod in ModList.GetCurrent().OrderBy(x => x.Id))
- {
- if (mod.Side == PluginSide.Both)
- {
- PluginIdMap[mod.Id] = i;
- PluginIdMapReversed[i] = mod.Id;
- i--;
- }
- }
- }
-
public UnsafeCustomRpc Register(UnsafeCustomRpc customRpc)
{
+ if (_list.Any(x => x.UnsafePlugin == customRpc.UnsafePlugin && x.Id == customRpc.Id))
+ {
+ throw new ArgumentException("Rpc with that id was already registered");
+ }
+
customRpc.Manager = this;
- customRpc.Id = List.Count(x => x.UnsafePlugin == customRpc.UnsafePlugin);
_list.Add(customRpc);
_map[customRpc.InnerNetObjectType].Add(customRpc);
@@ -211,15 +188,6 @@ namespace Reactor
return InnerNetObjectTypes.Select(x => x.GetMethod(nameof(InnerNetObject.HandleRpc), AccessTools.all));
}
- private static string ReadString(MessageReader reader, int len)
- {
- if (reader.BytesRemaining < len) throw new InvalidDataException($"Read length is longer than message length: {len} of {reader.BytesRemaining}");
-
- var output = Encoding.UTF8.GetString(reader.Buffer, reader.readHead, len);
- reader.Position += len;
- return output;
- }
-
public static bool Prefix(InnerNetObject __instance, [HarmonyArgument(0)] byte callId, [HarmonyArgument(1)] MessageReader reader)
{
if (callId == CallId)
@@ -227,17 +195,13 @@ namespace Reactor
var manager = PluginSingleton.Instance.CustomRpcManager;
var customRpcs = manager.Map[__instance.GetType()];
- var lengthOrShortId = reader.ReadPackedInt32();
-
- var pluginId = lengthOrShortId < 0
- ? manager.PluginIdMapReversed[lengthOrShortId]
- : ReadString(reader, lengthOrShortId);
-
- var id = reader.ReadPackedInt32();
+ var pluginNetId = reader.ReadPackedUInt32();
+ var id = reader.ReadPackedUInt32();
+ var pluginId = ModList.Current.Single(x => x.NetId == pluginNetId).Id;
var customRpc = customRpcs.Single(x => x.PluginId == pluginId && x.Id == id);
- customRpc.UnsafeHandle(__instance, customRpc.UnsafeRead(reader));
+ customRpc.UnsafeHandle(__instance, customRpc.UnsafeRead(reader.ReadMessage()));
return false;
}
diff --git a/Reactor/Networking/ModDeclaration.cs b/Reactor/Networking/ModDeclaration.cs
new file mode 100644
index 0000000..51e2acd
--- /dev/null
+++ b/Reactor/Networking/ModDeclaration.cs
@@ -0,0 +1,17 @@
+using Hazel;
+
+namespace Reactor.Networking
+{
+ public static class ModDeclaration
+ {
+ public static void Serialize(MessageWriter writer, Mod mod)
+ {
+ writer.StartMessage(byte.MaxValue);
+ writer.Write((byte) ReactorMessageFlags.ModDeclaration);
+ writer.WritePacked(mod.NetId);
+ writer.Write(mod.Id);
+ writer.Write(mod.Version);
+ writer.Write((byte) mod.Side);
+ }
+ }
+}
diff --git a/Reactor/Networking/ModList.cs b/Reactor/Networking/ModList.cs
new file mode 100644
index 0000000..f8f9f44
--- /dev/null
+++ b/Reactor/Networking/ModList.cs
@@ -0,0 +1,27 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using BepInEx.IL2CPP;
+
+namespace Reactor.Networking
+{
+ public static class ModList
+ {
+ public static ISet Current { get; private set; }
+
+ internal static void Update()
+ {
+ var i = (uint) 0;
+
+ Current = IL2CPPChainloader.Instance.Plugins.Values
+ .OrderByDescending(x => x.Metadata.GUID == ReactorPlugin.Id)
+ .Select(plugin => new Mod(
+ i++,
+ plugin.Metadata.GUID,
+ plugin.Metadata.Version.Clean(),
+ plugin.Instance.GetType().GetCustomAttribute()?.Side ?? PluginSide.Both
+ ))
+ .ToHashSet();
+ }
+ }
+}
diff --git a/Reactor/Networking/ModdedHandshakeC2S.cs b/Reactor/Networking/ModdedHandshakeC2S.cs
new file mode 100644
index 0000000..3b4fb79
--- /dev/null
+++ b/Reactor/Networking/ModdedHandshakeC2S.cs
@@ -0,0 +1,13 @@
+using Hazel;
+
+namespace Reactor.Networking
+{
+ public static class ModdedHandshakeC2S
+ {
+ public static void Serialize(MessageWriter writer, int modCount)
+ {
+ writer.Write((byte) ReactorProtocolVersion.Initial);
+ writer.WritePacked(modCount);
+ }
+ }
+}
diff --git a/Reactor/Networking/ModdedHandshakeS2C.cs b/Reactor/Networking/ModdedHandshakeS2C.cs
new file mode 100644
index 0000000..58f89c2
--- /dev/null
+++ b/Reactor/Networking/ModdedHandshakeS2C.cs
@@ -0,0 +1,24 @@
+using Hazel;
+
+namespace Reactor.Networking
+{
+ public static class ModdedHandshakeS2C
+ {
+ public static void Serialize(MessageWriter writer, string serverName, string serverVersion, int pluginCount)
+ {
+ writer.StartMessage(byte.MaxValue);
+ writer.Write((byte) ReactorMessageFlags.Handshake);
+ writer.Write(serverName);
+ writer.Write(serverVersion);
+ writer.WritePacked(pluginCount);
+ writer.EndMessage();
+ }
+
+ public static void Deserialize(MessageReader reader, out string serverName, out string serverVersion, out int pluginCount)
+ {
+ serverName = reader.ReadString();
+ serverVersion = reader.ReadString();
+ pluginCount = reader.ReadPackedInt32();
+ }
+ }
+}
diff --git a/Reactor/Networking/Patches/ClientPatches.cs b/Reactor/Networking/Patches/ClientPatches.cs
new file mode 100644
index 0000000..1bfce88
--- /dev/null
+++ b/Reactor/Networking/Patches/ClientPatches.cs
@@ -0,0 +1,156 @@
+using System;
+using System.Collections;
+using HarmonyLib;
+using Hazel;
+using Hazel.Udp;
+using InnerNet;
+using Reactor.Extensions;
+using UnhollowerBaseLib;
+using UnityEngine;
+
+namespace Reactor.Networking.Patches
+{
+ internal static class ClientPatches
+ {
+ [HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.HandleMessage))]
+ public static class HandleMessagePatch
+ {
+ public static bool Prefix(InnerNetClient __instance, [HarmonyArgument(0)] MessageReader reader)
+ {
+ if (reader.Tag == byte.MaxValue)
+ {
+ var flag = (ReactorMessageFlags) reader.ReadByte();
+
+ switch (flag)
+ {
+ case ReactorMessageFlags.Handshake:
+ {
+ ModdedHandshakeS2C.Deserialize(reader, out var serverName, out var serverVersion, out var pluginCount);
+
+ Logger.Info($"Connected to a modded server ({serverName} {serverVersion}, {pluginCount} plugins), sending mod declarations");
+
+ var mods = ModList.Current;
+
+ var writer = MessageWriter.Get(SendOption.Reliable);
+
+ var expected = 0;
+ var got = 0;
+
+ void Send()
+ {
+ expected++;
+
+ __instance.connection.Send(writer, () =>
+ {
+ got++;
+
+ if (got >= expected)
+ {
+ Logger.Debug("Received all acks");
+ __instance.connection.State = ConnectionState.Connected;
+ }
+ });
+ writer.Recycle();
+ }
+
+ foreach (var mod in mods)
+ {
+ ModDeclaration.Serialize(writer, mod);
+
+ if (writer.Length >= 500)
+ {
+ writer.CancelMessage();
+
+ Send();
+
+ writer = MessageWriter.Get(SendOption.Reliable);
+ ModDeclaration.Serialize(writer, mod);
+ }
+
+ writer.EndMessage();
+ }
+
+ Send();
+
+ break;
+ }
+
+ case ReactorMessageFlags.PluginDeclaration:
+ {
+ // TODO
+ break;
+ }
+
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ [HarmonyPatch(typeof(UdpConnection), nameof(UdpConnection.HandleSend))]
+ public static class SendHelloPatch
+ {
+ public static void Prefix(UdpConnection __instance, byte sendOption, ref Il2CppSystem.Action ackCallback)
+ {
+ if (sendOption != 8 || AmongUsClient.Instance == null || AmongUsClient.Instance.connection == null || !AmongUsClient.Instance.connection.Equals(__instance))
+ {
+ return;
+ }
+
+ ackCallback = (Action) (() =>
+ {
+ if (__instance.State == ConnectionState.Connected)
+ return;
+
+ Logger.Debug("Hello was acked, waiting for modded handshake response");
+
+ var coroutine = Coroutines.Start(Coroutine(__instance));
+
+ __instance.Disconnected = (Action) ((_, _) =>
+ {
+ Coroutines.Stop(coroutine);
+ });
+ });
+ }
+
+ private static IEnumerator Coroutine(UdpConnection connection)
+ {
+ yield return new WaitForSeconds(3);
+
+ var client = AmongUsClient.Instance;
+ if (client != null && client.connection != null && client.connection.Equals(connection) && client.connection.State == ConnectionState.Connecting)
+ {
+ client.LastDisconnectReason = DisconnectReasons.Custom;
+ client.LastCustomDisconnect = "Server didn't respond to modded handshake";
+ client.HandleDisconnect(client.LastDisconnectReason, client.LastCustomDisconnect);
+ }
+ }
+ }
+
+ [HarmonyPatch(typeof(AmongUsClient), nameof(AmongUsClient.GetConnectionData))]
+ public static class HandshakePatch
+ {
+ public static void Postfix(ref Il2CppStructArray __result)
+ {
+ ModList.Update();
+
+ var handshake = MessageWriter.Get(SendOption.Reliable);
+
+ handshake.Write(__result);
+
+ ModdedHandshakeC2S.Serialize(
+ handshake,
+ ModList.Current.Count
+ );
+
+ __result = handshake.ToByteArray(false);
+ handshake.Recycle();
+ }
+ }
+ }
+}
diff --git a/Reactor/Networking/Patches/ServerPatches.cs b/Reactor/Networking/Patches/ServerPatches.cs
new file mode 100644
index 0000000..e52533c
--- /dev/null
+++ b/Reactor/Networking/Patches/ServerPatches.cs
@@ -0,0 +1,31 @@
+using System.Collections.Generic;
+using System.Reflection;
+using HarmonyLib;
+using Hazel;
+using InnerNet;
+using Reactor.Extensions;
+using UnityEngine;
+
+namespace Reactor.Networking.Patches
+{
+ internal static class ServerPatches
+ {
+ // [HarmonyPatch(typeof(InnerNetServer), nameof(InnerNetServer.OnServerConnect))]
+ [HarmonyPatch]
+ public static class HandshakeResponsePatch
+ {
+ public static IEnumerable TargetMethods()
+ {
+ return typeof(InnerNetServer).GetMethods(typeof(void), typeof(NewConnectionEventArgs));
+ }
+
+ public static void Postfix([HarmonyArgument(0)] NewConnectionEventArgs evt)
+ {
+ var writer = MessageWriter.Get(SendOption.Reliable);
+ ModdedHandshakeS2C.Serialize(writer, "Among Us", Application.version, 0);
+ evt.Connection.Send(writer);
+ writer.Recycle();
+ }
+ }
+ }
+}
diff --git a/Reactor/Patches/KillButtonPatches.cs b/Reactor/Patches/KillButtonPatches.cs
deleted file mode 100644
index adc6f65..0000000
--- a/Reactor/Patches/KillButtonPatches.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-using HarmonyLib;
-
-namespace Reactor.Patches
-{
- ///
- /// Fixes kill cooldown resetting for a few frames if anyone on the map got the kill
- /// Also fixes invisible kill button if KillCooldown is set to 0
- ///
- internal class KillButtonPatches
- {
- [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.SetKillTimer))]
- [HarmonyPriority(Priority.First)]
- public static class SetKillTimerPatch
- {
- public static bool Prefix(PlayerControl __instance, [HarmonyArgument(0)] float time)
- {
- __instance.killTimer = time;
-
- // Patched because of this - game doesn't check if you're the killer
- if (__instance.AmOwner)
- {
- HudManager.Instance.KillButton.SetCoolDown(
- time,
- // if MaxTimer is 0, kill button disappears
- PlayerControl.GameOptions.KillCooldown <= 0 ? 1 : PlayerControl.GameOptions.KillCooldown
- );
- }
-
- return false;
- }
- }
- }
-}
diff --git a/Reactor/Patches/ModdedHandshakePatches.cs b/Reactor/Patches/ModdedHandshakePatches.cs
deleted file mode 100644
index 099add6..0000000
--- a/Reactor/Patches/ModdedHandshakePatches.cs
+++ /dev/null
@@ -1,131 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Reflection;
-using HarmonyLib;
-using Hazel;
-using Hazel.Udp;
-using InnerNet;
-using Reactor.Extensions;
-using Reactor.Net;
-using UnhollowerBaseLib;
-using UnityEngine;
-
-namespace Reactor.Patches
-{
- internal static class ModdedHandshakePatches
- {
- // [HarmonyPatch(typeof(InnerNetServer), nameof(InnerNetServer.OnServerConnect))]
- [HarmonyPatch]
- public static class ServerPatch
- {
- public static IEnumerable TargetMethods()
- {
- return typeof(InnerNetServer).GetMethods(typeof(void), typeof(NewConnectionEventArgs));
- }
-
- private static readonly string ServerBrand = $"Among Us {Application.version}";
-
- public static void Postfix([HarmonyArgument(0)] NewConnectionEventArgs evt)
- {
- var writer = MessageWriter.Get(SendOption.Reliable);
- ModdedHandshakeS2C.Serialize(writer, ServerBrand);
- evt.Connection.Send(writer);
- writer.Recycle();
- }
- }
-
- // [HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.HandleMessage))]
- [HarmonyPatch]
- public static class HandleMessagePatch
- {
- public static IEnumerable TargetMethods()
- {
- return typeof(InnerNetClient).GetMethods(typeof(void), typeof(MessageReader), typeof(SendOption));
- }
-
- public static bool Prefix(InnerNetClient __instance, [HarmonyArgument(0)] MessageReader reader, [HarmonyArgument(1)] SendOption sendOption)
- {
- if (reader.Tag == byte.MaxValue)
- {
- var serverBrand = reader.ReadString();
-
- __instance.connection.State = ConnectionState.Connected;
-
- PluginSingleton.Instance.Log.LogDebug($"Connected to modded server ({serverBrand})");
-
- return false;
- }
-
- return true;
- }
- }
-
- [HarmonyPatch(typeof(UdpConnection), nameof(UdpConnection.HandleSend))]
- public static class SendHelloPatch
- {
- public static void Prefix(UdpConnection __instance, byte sendOption, ref Il2CppSystem.Action ackCallback)
- {
- if (sendOption != 8 || AmongUsClient.Instance == null || AmongUsClient.Instance.connection == null || !AmongUsClient.Instance.connection.Equals(__instance))
- {
- return;
- }
-
- if (!PluginSingleton.Instance.ModdedHandshake.Value)
- {
- return;
- }
-
- ackCallback = (Action) (() =>
- {
- if (__instance.State == ConnectionState.Connected)
- return;
-
- PluginSingleton.Instance.Log.LogDebug("Hello was acked, waiting for modded handshake response");
-
- Coroutines.Start(Coroutine());
-
- __instance.add_Disconnected((Action) ((_, _) =>
- {
- Coroutines.Stop(Coroutine());
- }));
- });
- }
- }
-
- private static IEnumerator Coroutine()
- {
- yield return new WaitForSeconds(3);
-
- var client = AmongUsClient.Instance;
-
- if (client != null && client.connection != null && client.connection.State == ConnectionState.Connecting)
- {
- client.LastDisconnectReason = DisconnectReasons.Custom;
- client.LastCustomDisconnect = "Server didn't respond to modded handshake";
- client.HandleDisconnect(client.LastDisconnectReason, client.LastCustomDisconnect);
- }
- }
-
- [HarmonyPatch(typeof(AmongUsClient), nameof(AmongUsClient.GetConnectionData))]
- public static class HandshakePatch
- {
- public static bool Prefix(out Il2CppStructArray __result)
- {
- var handshake = MessageWriter.Get(SendOption.Reliable);
-
- ModdedHandshakeC2S.Serialize(
- handshake,
- Constants.GetBroadcastVersionBytes(),
- SaveManager.PlayerName,
- ModList.GetCurrent()
- );
-
- __result = handshake.ToByteArray(false);
- handshake.Recycle();
-
- return false;
- }
- }
- }
-}
diff --git a/Reactor/Patches/ReactorVersionShower.cs b/Reactor/Patches/ReactorVersionShower.cs
index ea345fa..3843700 100644
--- a/Reactor/Patches/ReactorVersionShower.cs
+++ b/Reactor/Patches/ReactorVersionShower.cs
@@ -2,6 +2,7 @@ using System;
using System.Reflection;
using BepInEx;
using BepInEx.IL2CPP;
+using HarmonyLib;
using Reactor.Extensions;
using UnityEngine;
using UnityEngine.SceneManagement;
@@ -53,5 +54,15 @@ namespace Reactor.Patches
Text.Text += "\nMods: " + IL2CPPChainloader.Instance.Plugins.Count;
TextUpdated?.Invoke(Text);
}
+
+ [HarmonyPatch(typeof(FreeWeekendShower), nameof(FreeWeekendShower.Start))]
+ private static class FreeWeekendShowerPatch
+ {
+ public static bool Prefix(FreeWeekendShower __instance)
+ {
+ __instance.Output.Destroy();
+ return false;
+ }
+ }
}
}
diff --git a/Reactor/Patches/UiFocusPatch.cs b/Reactor/Patches/UiFocusPatch.cs
deleted file mode 100644
index eae3a22..0000000
--- a/Reactor/Patches/UiFocusPatch.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using System.Collections.Generic;
-using System.Reflection;
-using HarmonyLib;
-using UnityEngine;
-using Reactor.Extensions;
-
-namespace Reactor.Patches
-{
- internal static class UiFocusPatch
- {
- // [HarmonyPatch(typeof(PassiveButtonManager), nameof(PassiveButtonManager.HandleMouseOver))]
- [HarmonyPatch]
- public static class HandleFocusPatch
- {
- public static IEnumerable TargetMethods()
- {
- return typeof(PassiveButtonManager).GetMethods(typeof(void), typeof(PassiveUiElement), typeof(Collider2D));
- }
-
- public static bool Prefix()
- {
- return Application.isFocused;
- }
- }
- }
-}
diff --git a/Reactor/Reactor.csproj b/Reactor/Reactor.csproj
index b898204..14a4cff 100644
--- a/Reactor/Reactor.csproj
+++ b/Reactor/Reactor.csproj
@@ -3,7 +3,7 @@
netstandard2.1
latest
- 0.3.0
+ 0.4.0
dev
Core mod and API for Among Us
NuclearPowered
@@ -14,4 +14,6 @@
+
+
diff --git a/Reactor/ReactorPlugin.cs b/Reactor/ReactorPlugin.cs
index 5c47099..0ecf764 100644
--- a/Reactor/ReactorPlugin.cs
+++ b/Reactor/ReactorPlugin.cs
@@ -1,10 +1,10 @@
using System;
using System.Linq;
using BepInEx;
-using BepInEx.Configuration;
using BepInEx.IL2CPP;
using HarmonyLib;
using Reactor.Extensions;
+using Reactor.Networking;
using Reactor.Patches;
using Reactor.Unstrip;
using UnhollowerBaseLib.Attributes;
@@ -14,7 +14,6 @@ namespace Reactor
{
[BepInPlugin(Id)]
[BepInProcess("Among Us.exe")]
- [ReactorPluginSide(PluginSide.ClientOnly)]
public class ReactorPlugin : BasePlugin
{
public const string Id = "gg.reactor.api";
@@ -22,8 +21,6 @@ namespace Reactor
public Harmony Harmony { get; } = new Harmony(Id);
public CustomRpcManager CustomRpcManager { get; } = new CustomRpcManager();
- public ConfigEntry ModdedHandshake;
-
private GameObject _gameObject;
private RegionInfoWatcher RegionInfoWatcher { get; } = new RegionInfoWatcher();
@@ -31,8 +28,6 @@ namespace Reactor
{
RegisterInIl2CppAttribute.Register();
- ModdedHandshake = Config.Bind("Features", "Modded handshake", true);
-
_gameObject = new GameObject(nameof(ReactorPlugin)).DontDestroy();
_gameObject.AddComponent().Plugin = this;
_gameObject.AddComponent();
@@ -61,11 +56,6 @@ namespace Reactor
{
}
- private void Start()
- {
- Plugin.CustomRpcManager.ReloadPluginIdMap();
- }
-
private void Update()
{
if (Plugin.RegionInfoWatcher.Reload)
diff --git a/Reactor/ReactorPluginSideAttribute.cs b/Reactor/ReactorPluginSideAttribute.cs
index af32b5f..9ec7e2c 100644
--- a/Reactor/ReactorPluginSideAttribute.cs
+++ b/Reactor/ReactorPluginSideAttribute.cs
@@ -12,33 +12,7 @@ namespace Reactor
public ReactorPluginSideAttribute(PluginSide side)
{
- if (side == PluginSide.ServerOnly)
- {
- throw new ArgumentException("BepInEx plugin can't be server only!");
- }
-
Side = side;
}
}
-
- ///
- /// Plugin side used in modded handshake
- ///
- public enum PluginSide : byte
- {
- ///
- /// Required by both sides, reject connection if missing on the other side
- ///
- Both,
-
- ///
- /// Required only by client
- ///
- ClientOnly,
-
- ///
- /// Required only by server
- ///
- ServerOnly
- }
}
diff --git a/Reactor/RegisterCustomRpcAttribute.cs b/Reactor/RegisterCustomRpcAttribute.cs
index cec430e..09f144a 100644
--- a/Reactor/RegisterCustomRpcAttribute.cs
+++ b/Reactor/RegisterCustomRpcAttribute.cs
@@ -2,6 +2,7 @@ using System;
using System.Reflection;
using BepInEx.IL2CPP;
using HarmonyLib;
+using Reactor.Networking;
namespace Reactor
{
@@ -11,6 +12,13 @@ namespace Reactor
[AttributeUsage(AttributeTargets.Class)]
public class RegisterCustomRpcAttribute : Attribute
{
+ public uint Id { get; }
+
+ public RegisterCustomRpcAttribute(uint id)
+ {
+ Id = id;
+ }
+
public static void Register(BasePlugin plugin)
{
Register(Assembly.GetCallingAssembly(), plugin);
@@ -29,7 +37,7 @@ namespace Reactor
throw new InvalidOperationException($"Type {type.FullDescription()} has {nameof(RegisterCustomRpcAttribute)} but doesn't extend {nameof(UnsafeCustomRpc)}.");
}
- var customRpc = (UnsafeCustomRpc) Activator.CreateInstance(type, plugin);
+ var customRpc = (UnsafeCustomRpc) Activator.CreateInstance(type, plugin, attribute.Id);
PluginSingleton.Instance.CustomRpcManager.Register(customRpc);
}
}
diff --git a/Reactor/RegisterInIl2CppAttribute.cs b/Reactor/RegisterInIl2CppAttribute.cs
index 3b046a9..43842ed 100644
--- a/Reactor/RegisterInIl2CppAttribute.cs
+++ b/Reactor/RegisterInIl2CppAttribute.cs
@@ -64,7 +64,7 @@ namespace Reactor
}
catch (Exception e)
{
- PluginSingleton.Instance.Log.LogWarning($"Failed to register {type.FullDescription()}: {e}");
+ Logger.Warning($"Failed to register {type.FullDescription()}: {e}");
}
}