mirror of
https://github.com/NuclearPowered/Reactor.git
synced 2026-10-19 15:51:54 +00:00
Airship support
Logger utility Initial Reactor.Networking protocol rework
This commit is contained in:
1 parent
a2f707fe16
commit
d58afa5907
36 files changed
+473
-395
No files matched your search
+3
-3
@@ -1,6 +1,6 @@
|
||||
<Project>
|
||||
<PropertyGroup Condition="'$(GamePlatform)' == 'Steam'">
|
||||
<GameVersion>2021.3.5s</GameVersion>
|
||||
<GameVersion>2021.3.31.3s</GameVersion>
|
||||
<DefineConstants>$(DefineConstants);STEAM</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Mappings>NuclearPowered/Mappings:0.2.0</Mappings>
|
||||
<Mappings>NuclearPowered/Mappings:0.3.0</Mappings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Reactor.OxygenFilter.MSBuild" Version="0.2.9" />
|
||||
<PackageReference Include="Reactor.OxygenFilter.MSBuild" Version="0.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="Copy" AfterTargets="Reobfuscate">
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Linq;
|
||||
using BepInEx;
|
||||
using BepInEx.IL2CPP;
|
||||
using HarmonyLib;
|
||||
using InnerNet;
|
||||
using Reactor.Extensions;
|
||||
using UnhollowerBaseLib;
|
||||
using UnhollowerBaseLib.Attributes;
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Reactor.Example
|
||||
{
|
||||
public enum CustomRpcCalls : uint
|
||||
{
|
||||
Example
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using BepInEx;
|
||||
using BepInEx.IL2CPP;
|
||||
using InnerNet;
|
||||
using Reactor.Extensions;
|
||||
using Reactor.Networking;
|
||||
using UnhollowerBaseLib.Attributes;
|
||||
using UnityEngine;
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
using Hazel;
|
||||
using Reactor.Networking;
|
||||
|
||||
namespace Reactor.Example
|
||||
{
|
||||
[RegisterCustomRpc]
|
||||
[RegisterCustomRpc((uint) CustomRpcCalls.Example)]
|
||||
public class ExampleRpc : PlayerCustomRpc<ExamplePlugin, ExampleRpc.Data>
|
||||
{
|
||||
public ExampleRpc(ExamplePlugin plugin) : base(plugin)
|
||||
public ExampleRpc(ExamplePlugin plugin, uint id) : base(plugin, id)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Reactor.Networking
|
||||
{
|
||||
public enum ReactorMessageFlags : byte
|
||||
{
|
||||
Handshake,
|
||||
ModDeclaration,
|
||||
PluginDeclaration
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace Reactor.Networking
|
||||
{
|
||||
/// <summary>
|
||||
/// Version of the Reactor.Net protocol format.
|
||||
/// </summary>
|
||||
public enum ReactorProtocolVersion : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Initial version.
|
||||
/// </summary>
|
||||
Initial = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Latest version.
|
||||
/// </summary>
|
||||
Latest = Initial,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace Reactor
|
||||
{
|
||||
/// <summary>
|
||||
/// Plugin side used in modded handshake
|
||||
/// </summary>
|
||||
public enum PluginSide : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Required by both sides, reject connection if missing on the other side
|
||||
/// </summary>
|
||||
Both,
|
||||
|
||||
/// <summary>
|
||||
/// Required only by client
|
||||
/// </summary>
|
||||
ClientOnly
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<Project>
|
||||
<ItemGroup>
|
||||
<Compile Include="$(MSBuildThisFileDirectory)**\*.cs" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,3 @@
|
||||
<Project>
|
||||
<Import Project="Reactor.Shared.projitems" Label="Shared" />
|
||||
</Project>
|
||||
@@ -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
|
||||
|
||||
@@ -343,7 +343,7 @@ namespace Reactor
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
PluginSingleton<ReactorPlugin>.Instance.Log.LogError(e.ToString());
|
||||
Logger<ReactorPlugin>.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<ReactorPlugin>.Instance.Log.LogWarning($"Unknown coroutine yield object of type {il2CppObjectBase} for coroutine {enumerator}");
|
||||
Logger<ReactorPlugin>.Warning($"Unknown coroutine yield object of type {il2CppObjectBase} for coroutine {enumerator}");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace Reactor.Extensions
|
||||
/// <remarks>https://docs.unity3d.com/ScriptReference/ColorUtility.ToHtmlStringRGBA.html</remarks>
|
||||
public static string ToHtmlStringRGBA(this Color32 color)
|
||||
{
|
||||
return $"{color.r:X2}{color.g:X2}{color.b:X2}{color.a:X2}";
|
||||
return ColorUtility.ToHtmlStringRGBA(color);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ToHtmlStringRGBA(UnityEngine.Color32)"/>
|
||||
|
||||
+16
-10
@@ -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<T>(MessageReader reader) where T : InnerNetObject
|
||||
// {
|
||||
// return global::MessageExtensions.ReadNetObject<T>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using BepInEx.IL2CPP;
|
||||
using BepInEx.Logging;
|
||||
|
||||
namespace Reactor
|
||||
{
|
||||
public static class Logger<T> where T : BasePlugin
|
||||
{
|
||||
public static ManualLogSource Instance => PluginSingleton<T>.Instance.Log;
|
||||
|
||||
/// <inheritdoc cref="ManualLogSource.LogFatal"/>
|
||||
public static void Fatal(object data) => Instance.LogFatal(data);
|
||||
|
||||
/// <inheritdoc cref="ManualLogSource.LogError"/>
|
||||
public static void Error(object data) => Instance.LogError(data);
|
||||
|
||||
/// <inheritdoc cref="ManualLogSource.LogWarning"/>
|
||||
public static void Warning(object data) => Instance.LogWarning(data);
|
||||
|
||||
/// <inheritdoc cref="ManualLogSource.LogMessage"/>
|
||||
public static void Message(object data) => Instance.LogMessage(data);
|
||||
|
||||
/// <inheritdoc cref="ManualLogSource.LogInfo"/>
|
||||
public static void Info(object data) => Instance.LogInfo(data);
|
||||
|
||||
/// <inheritdoc cref="ManualLogSource.LogDebug"/>
|
||||
public static void Debug(object data) => Instance.LogDebug(data);
|
||||
}
|
||||
}
|
||||
@@ -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})";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Mod> 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<Mod> GetCurrent()
|
||||
{
|
||||
return IL2CPPChainloader.Instance.Plugins
|
||||
.Select(plugin => new Mod(
|
||||
plugin.Key,
|
||||
plugin.Value.Metadata.Version.ToString(),
|
||||
plugin.Value.Instance.GetType().GetCustomAttribute<ReactorPluginSideAttribute>()?.Side ?? PluginSide.Both
|
||||
))
|
||||
.ToHashSet();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<byte> clientVersion, string name, ISet<Mod> mods = null)
|
||||
{
|
||||
writer.Write(clientVersion);
|
||||
writer.Write(name);
|
||||
|
||||
if (mods != null)
|
||||
{
|
||||
ModList.Serialize(writer, mods);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<TPlugin, TInnerNetObject, TData> : 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<TPlugin, TData> : CustomRpc<TPlugin, PlayerControl, TData> 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<string, int> PluginIdMap { get; private set; }
|
||||
public Dictionary<int, string> PluginIdMapReversed { get; private set; }
|
||||
|
||||
public CustomRpcManager()
|
||||
{
|
||||
foreach (var type in HandleRpcPatch.InnerNetObjectTypes)
|
||||
@@ -171,28 +162,14 @@ namespace Reactor
|
||||
}
|
||||
}
|
||||
|
||||
public void ReloadPluginIdMap()
|
||||
{
|
||||
PluginIdMap = new Dictionary<string, int>();
|
||||
PluginIdMapReversed = new Dictionary<int, string>();
|
||||
|
||||
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<ReactorPlugin>.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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Mod> 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<ReactorPluginSideAttribute>()?.Side ?? PluginSide.Both
|
||||
))
|
||||
.ToHashSet();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<ReactorPlugin>.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<ReactorPlugin>.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<ReactorPlugin>.Debug("Hello was acked, waiting for modded handshake response");
|
||||
|
||||
var coroutine = Coroutines.Start(Coroutine(__instance));
|
||||
|
||||
__instance.Disconnected = (Action<Il2CppSystem.Object, DisconnectedEventArgs>) ((_, _) =>
|
||||
{
|
||||
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<byte> __result)
|
||||
{
|
||||
ModList.Update();
|
||||
|
||||
var handshake = MessageWriter.Get(SendOption.Reliable);
|
||||
|
||||
handshake.Write(__result);
|
||||
|
||||
ModdedHandshakeC2S.Serialize(
|
||||
handshake,
|
||||
ModList.Current.Count
|
||||
);
|
||||
|
||||
__result = handshake.ToByteArray(false);
|
||||
handshake.Recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<MethodBase> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
using HarmonyLib;
|
||||
|
||||
namespace Reactor.Patches
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<MethodBase> 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<MethodBase> 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<ReactorPlugin>.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<ReactorPlugin>.Instance.ModdedHandshake.Value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ackCallback = (Action) (() =>
|
||||
{
|
||||
if (__instance.State == ConnectionState.Connected)
|
||||
return;
|
||||
|
||||
PluginSingleton<ReactorPlugin>.Instance.Log.LogDebug("Hello was acked, waiting for modded handshake response");
|
||||
|
||||
Coroutines.Start(Coroutine());
|
||||
|
||||
__instance.add_Disconnected((Action<Il2CppSystem.Object, DisconnectedEventArgs>) ((_, _) =>
|
||||
{
|
||||
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<byte> __result)
|
||||
{
|
||||
var handshake = MessageWriter.Get(SendOption.Reliable);
|
||||
|
||||
ModdedHandshakeC2S.Serialize(
|
||||
handshake,
|
||||
Constants.GetBroadcastVersionBytes(),
|
||||
SaveManager.PlayerName,
|
||||
ModList.GetCurrent()
|
||||
);
|
||||
|
||||
__result = handshake.ToByteArray(false);
|
||||
handshake.Recycle();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<MethodBase> TargetMethods()
|
||||
{
|
||||
return typeof(PassiveButtonManager).GetMethods(typeof(void), typeof(PassiveUiElement), typeof(Collider2D));
|
||||
}
|
||||
|
||||
public static bool Prefix()
|
||||
{
|
||||
return Application.isFocused;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
|
||||
<VersionPrefix>0.3.0</VersionPrefix>
|
||||
<VersionPrefix>0.4.0</VersionPrefix>
|
||||
<VersionSuffix>dev</VersionSuffix>
|
||||
<Description>Core mod and API for Among Us</Description>
|
||||
<Authors>NuclearPowered</Authors>
|
||||
@@ -14,4 +14,6 @@
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="../AmongUs.props" />
|
||||
|
||||
<Import Project="..\Reactor.Shared\Reactor.Shared.projitems" Label="Shared" />
|
||||
</Project>
|
||||
@@ -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<bool> 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<ReactorComponent>().Plugin = this;
|
||||
_gameObject.AddComponent<Coroutines.Component>();
|
||||
@@ -61,11 +56,6 @@ namespace Reactor
|
||||
{
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
Plugin.CustomRpcManager.ReloadPluginIdMap();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (Plugin.RegionInfoWatcher.Reload)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plugin side used in modded handshake
|
||||
/// </summary>
|
||||
public enum PluginSide : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Required by both sides, reject connection if missing on the other side
|
||||
/// </summary>
|
||||
Both,
|
||||
|
||||
/// <summary>
|
||||
/// Required only by client
|
||||
/// </summary>
|
||||
ClientOnly,
|
||||
|
||||
/// <summary>
|
||||
/// Required only by server
|
||||
/// </summary>
|
||||
ServerOnly
|
||||
}
|
||||
}
|
||||
@@ -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<ReactorPlugin>.Instance.CustomRpcManager.Register(customRpc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace Reactor
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
PluginSingleton<ReactorPlugin>.Instance.Log.LogWarning($"Failed to register {type.FullDescription()}: {e}");
|
||||
Logger<ReactorPlugin>.Warning($"Failed to register {type.FullDescription()}: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user