mirror of
https://github.com/NuclearPowered/Reactor.git
synced 2026-10-19 15:51:54 +00:00
Add xmldocs
This commit is contained in:
56 files changed
+1036
-148
No files matched your search
+1
-1
@@ -9,7 +9,7 @@
|
||||
<PackageReference Include="BepInEx.Unity.IL2CPP" Version="6.0.0-be.660" Private="false" ExcludeAssets="runtime;native" />
|
||||
<PackageReference Include="AmongUs.GameLibs.$(GamePlatform)" Version="$(GameVersion)" PrivateAssets="all" />
|
||||
|
||||
<PackageReference Include="BepInEx.AutoPlugin" Version="1.0.1" PrivateAssets="all" />
|
||||
<PackageReference Include="BepInEx.AutoPlugin" Version="1.1.0" PrivateAssets="all" />
|
||||
<PackageReference Include="BepInEx.IL2CPP.MSBuild" Version="2.0.1" PrivateAssets="all" ExcludeAssets="runtime" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma warning disable SA1611, SA1615
|
||||
|
||||
using BenchmarkDotNet.Engines;
|
||||
using BenchmarkDotNet.Loggers;
|
||||
using BenchmarkDotNet.Toolchains;
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
},
|
||||
"BepInEx.AutoPlugin": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.0.1, )",
|
||||
"resolved": "1.0.1",
|
||||
"contentHash": "G4YpTHG9rtkDJof+ld7Er4E+sCOlgsjG4bJ1itx1ub7dgff6qqHLy9ksX1ehgO0AdBOPVyVl/GKJg9o7iNl8HA=="
|
||||
"requested": "[1.1.0, )",
|
||||
"resolved": "1.1.0",
|
||||
"contentHash": "/7Nmc1EA9lYlGBiyz6qPAaHvlcJHEjFZ1RFpGX+8EBj3GrgoX5kbpnZusuSHoOlLx0l+CnzKuQNE54B8UaGPyw=="
|
||||
},
|
||||
"BepInEx.IL2CPP.MSBuild": {
|
||||
"type": "Direct",
|
||||
@@ -57,6 +57,15 @@
|
||||
"Samboy063.Cpp2IL.Core": "2022.0.7.2"
|
||||
}
|
||||
},
|
||||
"StyleCop.Analyzers": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.2.0-beta.435, )",
|
||||
"resolved": "1.2.0-beta.435",
|
||||
"contentHash": "TADk7vdGXtfTnYCV7GyleaaRTQjfoSfZXprQrVMm7cSJtJbFc1QIbWPyLvrgrfGdfHbGmUPvaN4ODKNxg2jgPQ==",
|
||||
"dependencies": {
|
||||
"StyleCop.Analyzers.Unstable": "1.2.0.435"
|
||||
}
|
||||
},
|
||||
"AssetRipper.VersionUtilities": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.2.1",
|
||||
@@ -463,6 +472,11 @@
|
||||
"resolved": "2.0.2",
|
||||
"contentHash": "4EQgYdNZ92SyaO7YFk6olVnebF5V+jrHyMUjvPq89tLeMo8NSfgDF+6Zwq/lgh9j/0yfQp9Lkm0ZA0rUATCZFA=="
|
||||
},
|
||||
"StyleCop.Analyzers.Unstable": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.2.0.435",
|
||||
"contentHash": "ouwPWZxbOV3SmCZxIRqHvljkSzkCyi1tDoMzQtDb/bRP8ctASV/iRJr+A2Gdj0QLaLmWnqTWDrH82/iP+X80Lg=="
|
||||
},
|
||||
"System.CodeDom": {
|
||||
"type": "Transitive",
|
||||
"resolved": "6.0.0",
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
},
|
||||
"BepInEx.AutoPlugin": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.0.1, )",
|
||||
"resolved": "1.0.1",
|
||||
"contentHash": "G4YpTHG9rtkDJof+ld7Er4E+sCOlgsjG4bJ1itx1ub7dgff6qqHLy9ksX1ehgO0AdBOPVyVl/GKJg9o7iNl8HA=="
|
||||
"requested": "[1.1.0, )",
|
||||
"resolved": "1.1.0",
|
||||
"contentHash": "/7Nmc1EA9lYlGBiyz6qPAaHvlcJHEjFZ1RFpGX+8EBj3GrgoX5kbpnZusuSHoOlLx0l+CnzKuQNE54B8UaGPyw=="
|
||||
},
|
||||
"BepInEx.IL2CPP.MSBuild": {
|
||||
"type": "Direct",
|
||||
@@ -37,6 +37,15 @@
|
||||
"Samboy063.Cpp2IL.Core": "2022.0.7.2"
|
||||
}
|
||||
},
|
||||
"StyleCop.Analyzers": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.2.0-beta.435, )",
|
||||
"resolved": "1.2.0-beta.435",
|
||||
"contentHash": "TADk7vdGXtfTnYCV7GyleaaRTQjfoSfZXprQrVMm7cSJtJbFc1QIbWPyLvrgrfGdfHbGmUPvaN4ODKNxg2jgPQ==",
|
||||
"dependencies": {
|
||||
"StyleCop.Analyzers.Unstable": "1.2.0.435"
|
||||
}
|
||||
},
|
||||
"AssetRipper.VersionUtilities": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.2.1",
|
||||
@@ -303,6 +312,11 @@
|
||||
"resolved": "2.0.2",
|
||||
"contentHash": "4EQgYdNZ92SyaO7YFk6olVnebF5V+jrHyMUjvPq89tLeMo8NSfgDF+6Zwq/lgh9j/0yfQp9Lkm0ZA0rUATCZFA=="
|
||||
},
|
||||
"StyleCop.Analyzers.Unstable": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.2.0.435",
|
||||
"contentHash": "ouwPWZxbOV3SmCZxIRqHvljkSzkCyi1tDoMzQtDb/bRP8ctASV/iRJr+A2Gdj0QLaLmWnqTWDrH82/iP+X80Lg=="
|
||||
},
|
||||
"System.Collections": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
},
|
||||
"BepInEx.AutoPlugin": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.0.1, )",
|
||||
"resolved": "1.0.1",
|
||||
"contentHash": "G4YpTHG9rtkDJof+ld7Er4E+sCOlgsjG4bJ1itx1ub7dgff6qqHLy9ksX1ehgO0AdBOPVyVl/GKJg9o7iNl8HA=="
|
||||
"requested": "[1.1.0, )",
|
||||
"resolved": "1.1.0",
|
||||
"contentHash": "/7Nmc1EA9lYlGBiyz6qPAaHvlcJHEjFZ1RFpGX+8EBj3GrgoX5kbpnZusuSHoOlLx0l+CnzKuQNE54B8UaGPyw=="
|
||||
},
|
||||
"BepInEx.IL2CPP.MSBuild": {
|
||||
"type": "Direct",
|
||||
@@ -37,6 +37,15 @@
|
||||
"Samboy063.Cpp2IL.Core": "2022.0.7.2"
|
||||
}
|
||||
},
|
||||
"StyleCop.Analyzers": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.2.0-beta.435, )",
|
||||
"resolved": "1.2.0-beta.435",
|
||||
"contentHash": "TADk7vdGXtfTnYCV7GyleaaRTQjfoSfZXprQrVMm7cSJtJbFc1QIbWPyLvrgrfGdfHbGmUPvaN4ODKNxg2jgPQ==",
|
||||
"dependencies": {
|
||||
"StyleCop.Analyzers.Unstable": "1.2.0.435"
|
||||
}
|
||||
},
|
||||
"AssetRipper.VersionUtilities": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.2.1",
|
||||
@@ -303,6 +312,11 @@
|
||||
"resolved": "2.0.2",
|
||||
"contentHash": "4EQgYdNZ92SyaO7YFk6olVnebF5V+jrHyMUjvPq89tLeMo8NSfgDF+6Zwq/lgh9j/0yfQp9Lkm0ZA0rUATCZFA=="
|
||||
},
|
||||
"StyleCop.Analyzers.Unstable": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.2.0.435",
|
||||
"contentHash": "ouwPWZxbOV3SmCZxIRqHvljkSzkCyi1tDoMzQtDb/bRP8ctASV/iRJr+A2Gdj0QLaLmWnqTWDrH82/iP+X80Lg=="
|
||||
},
|
||||
"System.Collections": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
|
||||
@@ -8,8 +8,18 @@ using System.Text;
|
||||
|
||||
namespace Reactor.Networking;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Reactor.Protocol mod.
|
||||
/// </summary>
|
||||
public readonly struct Mod : IEquatable<Mod>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Mod"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the mod.</param>
|
||||
/// <param name="version">The version of the mod.</param>
|
||||
/// <param name="flags">The flags of the mod.</param>
|
||||
/// <param name="name">The name of the mod.</param>
|
||||
public Mod(string id, string version, ModFlags flags, string? name = null)
|
||||
{
|
||||
Id = id;
|
||||
@@ -18,38 +28,62 @@ public readonly struct Mod : IEquatable<Mod>
|
||||
Name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the id of the mod.
|
||||
/// </summary>
|
||||
public string Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the version of the mod.
|
||||
/// </summary>
|
||||
public string Version { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the flags of the mod.
|
||||
/// </summary>
|
||||
public ModFlags Flags { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the mod.
|
||||
/// </summary>
|
||||
public string? Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the mod is required on all clients.
|
||||
/// </summary>
|
||||
public bool IsRequiredOnAllClients => (Flags & ModFlags.RequireOnAllClients) != 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Equals(Mod other)
|
||||
{
|
||||
return Id == other.Id && Version == other.Version;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is Mod other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public static bool operator ==(Mod left, Mod right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public static bool operator !=(Mod left, Mod right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(Id, Version);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Name ?? Id} ({Version})";
|
||||
|
||||
@@ -2,9 +2,15 @@ using System;
|
||||
|
||||
namespace Reactor.Networking;
|
||||
|
||||
/// <summary>
|
||||
/// Represents flags of the mod.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum ModFlags : ushort
|
||||
{
|
||||
/// <summary>
|
||||
/// No flags.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<NoBuild>true</NoBuild>
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -22,6 +23,10 @@
|
||||
<Pack>true</Pack>
|
||||
<PackagePath>$(ContentTargetFolders)\cs\any\$(PackageId)\%(RecursiveDir)\</PackagePath>
|
||||
</Compile>
|
||||
<Compile Update="Mod.cs">
|
||||
<Pack>true</Pack>
|
||||
<PackagePath>contentFiles\cs\any\Reactor.Networking.Shared\\</PackagePath>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
namespace Reactor.Networking;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a GameData flag for Reactor messages.
|
||||
/// </summary>
|
||||
public enum ReactorGameDataFlag : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// KickWithReason GameData message.
|
||||
/// </summary>
|
||||
KickWithReason,
|
||||
}
|
||||
@@ -1,6 +1,12 @@
|
||||
namespace Reactor.Networking;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a message flag for Reactor messages.
|
||||
/// </summary>
|
||||
public enum ReactorMessageFlags : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Handshake message.
|
||||
/// </summary>
|
||||
Handshake,
|
||||
}
|
||||
@@ -6,14 +6,25 @@ using Il2CppInterop.Runtime.InteropTypes.Arrays;
|
||||
|
||||
namespace Reactor.Localization;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a way to use custom static StringNames.
|
||||
/// </summary>
|
||||
public class CustomStringName
|
||||
{
|
||||
private static int _lastId = -1;
|
||||
|
||||
private static readonly List<CustomStringName> _list = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of all <see cref="CustomStringName"/>s.
|
||||
/// </summary>
|
||||
public static IReadOnlyList<CustomStringName> List => _list.AsReadOnly();
|
||||
|
||||
/// <summary>
|
||||
/// Registers a <see cref="CustomStringName"/> with specified <paramref name="value"/>.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <returns>A <see cref="CustomStringName"/>.</returns>
|
||||
public static CustomStringName Register(string value)
|
||||
{
|
||||
var customStringName = new CustomStringName(_lastId--, value);
|
||||
@@ -22,8 +33,14 @@ public class CustomStringName
|
||||
return customStringName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the id.
|
||||
/// </summary>
|
||||
public int Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value.
|
||||
/// </summary>
|
||||
public string Value { get; }
|
||||
|
||||
private CustomStringName(int id, string value)
|
||||
@@ -32,7 +49,18 @@ public class CustomStringName
|
||||
Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines an implicit conversion of a <see cref="CustomStringName"/> to a <see cref="StringNames"/>.
|
||||
/// </summary>
|
||||
/// <param name="name">The <see cref="CustomStringName"/>.</param>
|
||||
/// <returns>A <see cref="StringNames"/>.</returns>
|
||||
public static implicit operator StringNames(CustomStringName name) => (StringNames) name.Id;
|
||||
|
||||
/// <summary>
|
||||
/// Defines an explicit conversion of a <see cref="StringNames"/> to a <see cref="CustomStringName"/>.
|
||||
/// </summary>
|
||||
/// <param name="name">The <see cref="StringNames"/>.</param>
|
||||
/// <returns>A <see cref="CustomStringName"/>.</returns>
|
||||
public static explicit operator CustomStringName?(StringNames name) => List.SingleOrDefault(x => x.Id == (int) name);
|
||||
|
||||
[HarmonyPatch(typeof(TranslationController), nameof(TranslationController.GetString), typeof(StringNames), typeof(Il2CppReferenceArray<Il2CppSystem.Object>))]
|
||||
|
||||
@@ -7,11 +7,19 @@ using Reactor.Networking.Serialization;
|
||||
|
||||
namespace Reactor.Networking.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// Automatically registers a <see cref="MessageConverter{T}"/>.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public sealed class MessageConverterAttribute : Attribute
|
||||
{
|
||||
private static readonly HashSet<Assembly> _registeredAssemblies = new();
|
||||
|
||||
/// <summary>
|
||||
/// Registers all <see cref="MessageConverter{T}"/>s annotated with <see cref="MessageConverterAttribute"/> in the specified <paramref name="assembly"/>.
|
||||
/// </summary>
|
||||
/// <remarks>This is called automatically on plugin assemblies so you probably don't need to call this.</remarks>
|
||||
/// <param name="assembly">The assembly to search.</param>
|
||||
public static void Register(Assembly assembly)
|
||||
{
|
||||
if (_registeredAssemblies.Contains(assembly)) return;
|
||||
|
||||
@@ -10,21 +10,49 @@ using Reactor.Utilities;
|
||||
|
||||
namespace Reactor.Networking.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// Automatically registers a method rpc.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public sealed class MethodRpcAttribute : Attribute
|
||||
{
|
||||
private static readonly HashSet<Assembly> _registeredAssemblies = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the id of the rpc.
|
||||
/// </summary>
|
||||
public uint Id { get; }
|
||||
public SendOption Option { get; set; } = SendOption.Reliable;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the send option of the rpc.
|
||||
/// </summary>
|
||||
public SendOption SendOption { get; set; } = SendOption.Reliable;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the local handling of the rpc.
|
||||
/// </summary>
|
||||
public RpcLocalHandling LocalHandling { get; set; } = RpcLocalHandling.Before;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the rpc should be sent immediately.
|
||||
/// </summary>
|
||||
public bool SendImmediately { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MethodRpcAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the rpc.</param>
|
||||
public MethodRpcAttribute(uint id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers all method rpc's annotated with <see cref="MethodRpcAttribute"/> in the specified <paramref name="assembly"/>.
|
||||
/// </summary>
|
||||
/// <remarks>This is called automatically on plugin assemblies so you probably don't need to call this.</remarks>
|
||||
/// <param name="assembly">The assembly to search.</param>
|
||||
/// <param name="plugin">The plugin to register the rpc to.</param>
|
||||
public static void Register(Assembly assembly, BasePlugin plugin)
|
||||
{
|
||||
if (_registeredAssemblies.Contains(assembly)) return;
|
||||
@@ -43,7 +71,7 @@ public sealed class MethodRpcAttribute : Attribute
|
||||
|
||||
try
|
||||
{
|
||||
var customRpc = new MethodRpc(plugin, method, attribute.Id, attribute.Option, attribute.LocalHandling, attribute.SendImmediately);
|
||||
var customRpc = new MethodRpc(plugin, method, attribute.Id, attribute.SendOption, attribute.LocalHandling, attribute.SendImmediately);
|
||||
PluginSingleton<ReactorPlugin>.Instance.CustomRpcManager.Register(customRpc);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@@ -4,17 +4,27 @@ using System.Reflection;
|
||||
|
||||
namespace Reactor.Networking.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// Describes the <see cref="ModFlags"/> of the annotated plugin class.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public sealed class ReactorModFlagsAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets flags of the mod.
|
||||
/// </summary>
|
||||
public ModFlags Flags { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ReactorModFlagsAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="flags">Flags of the mod.</param>
|
||||
public ReactorModFlagsAttribute(ModFlags flags)
|
||||
{
|
||||
Flags = flags;
|
||||
}
|
||||
|
||||
public static ModFlags GetModFlags(Type type)
|
||||
internal static ModFlags GetModFlags(Type type)
|
||||
{
|
||||
var attribute = type.GetCustomAttribute<ReactorModFlagsAttribute>();
|
||||
if (attribute != null)
|
||||
|
||||
@@ -9,25 +9,33 @@ using Reactor.Utilities;
|
||||
namespace Reactor.Networking.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// Utility attribute for automatically registering CustomRpc
|
||||
/// Automatically registers a rpc.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public sealed class RegisterCustomRpcAttribute : Attribute
|
||||
{
|
||||
private static readonly HashSet<Assembly> _registeredAssemblies = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the id of the rpc.
|
||||
/// </summary>
|
||||
public uint Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RegisterCustomRpcAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the rpc.</param>
|
||||
public RegisterCustomRpcAttribute(uint id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers all types marked with <see cref="RegisterCustomRpcAttribute"/> from the specified assembly as custom rpcs
|
||||
/// Registers all rpc's annotated with <see cref="RegisterCustomRpcAttribute"/> in the specified <paramref name="assembly"/>.
|
||||
/// </summary>
|
||||
/// <param name="assembly"></param>
|
||||
/// <param name="plugin"></param>
|
||||
/// <remarks>This is called automatically on plugin assemblies so you probably don't need to call this.</remarks>
|
||||
/// <param name="assembly">The assembly to search.</param>
|
||||
/// <param name="plugin">The plugin to register the rpc to.</param>
|
||||
public static void Register(Assembly assembly, BasePlugin plugin)
|
||||
{
|
||||
if (_registeredAssemblies.Contains(assembly)) return;
|
||||
|
||||
@@ -3,8 +3,17 @@ using InnerNet;
|
||||
|
||||
namespace Reactor.Networking.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="InnerNetClient"/>.
|
||||
/// </summary>
|
||||
public static class ClientExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Kicks a client with id equal to <paramref name="targetClientId"/> with a <paramref name="reason"/>.
|
||||
/// </summary>
|
||||
/// <param name="innerNetClient">The <see cref="InnerNetClient"/> to send the message from.</param>
|
||||
/// <param name="targetClientId">The target client's id.</param>
|
||||
/// <param name="reason">The kick reason.</param>
|
||||
public static void KickWithReason(this InnerNetClient innerNetClient, int targetClientId, string reason)
|
||||
{
|
||||
var writer = MessageWriter.Get(SendOption.Reliable);
|
||||
@@ -22,6 +31,11 @@ public static class ClientExtensions
|
||||
writer.Recycle();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnects a local <see cref="InnerNetClient"/> with a custom reason.
|
||||
/// </summary>
|
||||
/// <param name="innerNetClient">The <see cref="InnerNetClient"/> to disconnect.</param>
|
||||
/// <param name="reason">The custom reason to disconnect with.</param>
|
||||
public static void DisconnectWithReason(this InnerNetClient innerNetClient, string reason)
|
||||
{
|
||||
innerNetClient.LastCustomDisconnect = reason;
|
||||
|
||||
@@ -5,6 +5,9 @@ using UnityEngine;
|
||||
|
||||
namespace Reactor.Networking.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="MessageWriter"/> and <see cref="MessageReader"/>.
|
||||
/// </summary>
|
||||
public static class ExtraMessageExtensions
|
||||
{
|
||||
private const float MIN = -50f;
|
||||
@@ -15,6 +18,11 @@ public static class ExtraMessageExtensions
|
||||
return Mathf.Clamp((t - MIN) / (MAX - MIN), 0f, 1f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a <see cref="Vector2"/> to the <paramref name="writer"/>.
|
||||
/// </summary>
|
||||
/// <param name="writer">The <see cref="MessageWriter"/> to write to.</param>
|
||||
/// <param name="value">The <see cref="Vector2"/> to write.</param>
|
||||
public static void Write(this MessageWriter writer, Vector2 value)
|
||||
{
|
||||
var x = (ushort) (ReverseLerp(value.x) * ushort.MaxValue);
|
||||
@@ -24,6 +32,11 @@ public static class ExtraMessageExtensions
|
||||
writer.Write(y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a <see cref="Vector2"/> from the <paramref name="reader"/>.
|
||||
/// </summary>
|
||||
/// <param name="reader">The <see cref="MessageReader"/> to read from.</param>
|
||||
/// <returns>A <see cref="Vector2"/> from the <paramref name="reader"/>.</returns>
|
||||
public static Vector2 ReadVector2(this MessageReader reader)
|
||||
{
|
||||
var x = reader.ReadUInt16() / (float) ushort.MaxValue;
|
||||
@@ -32,6 +45,12 @@ public static class ExtraMessageExtensions
|
||||
return new Vector2(Mathf.Lerp(MIN, MAX, x), Mathf.Lerp(MIN, MAX, y));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends a message on the <paramref name="connection"/> with an <paramref name="ackCallback"/>.
|
||||
/// </summary>
|
||||
/// <param name="connection">The connection to send the message on.</param>
|
||||
/// <param name="msg">The message to send.</param>
|
||||
/// <param name="ackCallback">The callback to invoke when this packet is acknowledged.</param>
|
||||
public static void Send(this UdpConnection connection, MessageWriter msg, Action ackCallback)
|
||||
{
|
||||
if (msg.SendOption != SendOption.Reliable)
|
||||
|
||||
@@ -11,13 +11,24 @@ using Reactor.Networking.Patches;
|
||||
|
||||
namespace Reactor.Networking;
|
||||
|
||||
/// <summary>
|
||||
/// Represents currently loaded mods.
|
||||
/// </summary>
|
||||
public static class ModList
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets current mods.
|
||||
/// </summary>
|
||||
public static IReadOnlyCollection<Mod> Current { get; private set; } = null!;
|
||||
|
||||
private static readonly Dictionary<string, Mod> _modById = new();
|
||||
private static readonly Dictionary<Type, Mod> _modByPluginType = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a mod by it's id.
|
||||
/// </summary>
|
||||
/// <param name="id">The id of the mod.</param>
|
||||
/// <returns>The mod with the specified <paramref name="id"/>.</returns>
|
||||
public static Mod GetById(string id)
|
||||
{
|
||||
return _modById[id];
|
||||
@@ -42,6 +53,12 @@ public static class ModList
|
||||
return _netIdByMod[mod];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a <see cref="Mod"/> reference from the <paramref name="reader"/>.
|
||||
/// </summary>
|
||||
/// <param name="reader">The <see cref="MessageReader"/> to read from.</param>
|
||||
/// <returns>A <see cref="Mod"/> from the <paramref name="reader"/>.</returns>
|
||||
/// <remarks>This will try to read by net id and fallback to a regular id otherwise.</remarks>
|
||||
public static Mod ReadMod(this MessageReader reader)
|
||||
{
|
||||
var netId = reader.ReadPackedUInt32();
|
||||
@@ -54,16 +71,22 @@ public static class ModList
|
||||
return GetById(id);
|
||||
}
|
||||
|
||||
public static void Write(this MessageWriter writer, Mod mod)
|
||||
/// <summary>
|
||||
/// Writes a <see cref="Mod"/> reference to the <paramref name="writer"/>.
|
||||
/// </summary>
|
||||
/// <param name="writer">The <see cref="MessageWriter"/> to write to.</param>
|
||||
/// <param name="value">The <see cref="Mod"/> to write.</param>
|
||||
/// <remarks>This will write the net id if <see cref="Mod.IsRequiredOnAllClients"/> is true, regular id otherwise.</remarks>
|
||||
public static void Write(this MessageWriter writer, Mod value)
|
||||
{
|
||||
if (mod.IsRequiredOnAllClients)
|
||||
if (value.IsRequiredOnAllClients)
|
||||
{
|
||||
writer.WritePacked(mod.GetNetId());
|
||||
writer.WritePacked(value.GetNetId());
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.WritePacked(0);
|
||||
writer.Write(mod.Id);
|
||||
writer.Write(value.Id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,10 +3,19 @@ using InnerNet;
|
||||
|
||||
namespace Reactor.Networking.Patches;
|
||||
|
||||
/// <summary>
|
||||
/// Provides information about the reactor protocol state of the current connection.
|
||||
/// </summary>
|
||||
public class ReactorConnection
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the syncer.
|
||||
/// </summary>
|
||||
public Syncer? Syncer { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current instance of <see cref="ReactorConnection"/>.
|
||||
/// </summary>
|
||||
public static ReactorConnection? Instance { get; private set; }
|
||||
|
||||
[HarmonyPatch]
|
||||
@@ -30,8 +39,18 @@ public class ReactorConnection
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies who syncs the mod list and handles compatibility.
|
||||
/// </summary>
|
||||
public enum Syncer
|
||||
{
|
||||
/// <summary>
|
||||
/// A custom region server.
|
||||
/// </summary>
|
||||
Server,
|
||||
|
||||
/// <summary>
|
||||
/// The host of the game.
|
||||
/// </summary>
|
||||
Host,
|
||||
}
|
||||
@@ -5,77 +5,137 @@ using InnerNet;
|
||||
|
||||
namespace Reactor.Networking.Rpc;
|
||||
|
||||
/// <summary>
|
||||
/// Base type for custom rpc's but typed with generics.
|
||||
/// </summary>
|
||||
/// <typeparam name="TPlugin">The type of the plugin the the rpc is attached to.</typeparam>
|
||||
/// <typeparam name="TInnerNetObject">The type of the <see cref="InnerNetObject"/>.</typeparam>
|
||||
/// <typeparam name="TData">The type of the rpc data.</typeparam>
|
||||
public abstract class CustomRpc<TPlugin, TInnerNetObject, TData> : UnsafeCustomRpc where TPlugin : BasePlugin where TInnerNetObject : InnerNetObject
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected CustomRpc(TPlugin plugin, uint id) : base(plugin, id)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="UnsafeCustomRpc.UnsafePlugin"/>
|
||||
public TPlugin Plugin => (TPlugin) UnsafePlugin;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type InnerNetObjectType => typeof(TInnerNetObject);
|
||||
|
||||
/// <inheritdoc cref="UnsafeCustomRpc.UnsafeWrite" />
|
||||
public abstract void Write(MessageWriter writer, TData? data);
|
||||
|
||||
/// <inheritdoc cref="UnsafeCustomRpc.UnsafeRead" />
|
||||
public abstract TData Read(MessageReader reader);
|
||||
|
||||
/// <inheritdoc cref="UnsafeCustomRpc.UnsafeHandle" />
|
||||
public abstract void Handle(TInnerNetObject innerNetObject, TData? data);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void UnsafeWrite(MessageWriter writer, object? data)
|
||||
{
|
||||
Write(writer, (TData?) data);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object? UnsafeRead(MessageReader reader)
|
||||
{
|
||||
return Read(reader);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void UnsafeHandle(InnerNetObject innerNetObject, object? data)
|
||||
{
|
||||
Handle((TInnerNetObject) innerNetObject, (TData?) data);
|
||||
}
|
||||
|
||||
public void Send(InnerNetObject netObject, TData data, bool immediately = false, Action? ackCallback = null)
|
||||
/// <summary>
|
||||
/// Sends this custom rpc on the specified <paramref name="innerNetObject"/> with the specified <paramref name="data"/>.
|
||||
/// </summary>
|
||||
/// <param name="innerNetObject">The <see cref="InnerNetObject"/> to send the rpc on.</param>
|
||||
/// <param name="data">The data to send.</param>
|
||||
/// <param name="immediately">Whether to send it immediately.</param>
|
||||
/// <param name="ackCallback">The callback to invoke when this packet is acknowledged.</param>
|
||||
public void Send(InnerNetObject innerNetObject, TData data, bool immediately = false, Action? ackCallback = null)
|
||||
{
|
||||
UnsafeSend(netObject, data, immediately, ackCallback: ackCallback);
|
||||
UnsafeSend(innerNetObject, data, immediately, ackCallback: ackCallback);
|
||||
}
|
||||
|
||||
public void SendTo(InnerNetObject netObject, int targetId, TData data, Action? ackCallback = null)
|
||||
/// <summary>
|
||||
/// Sends this custom rpc on the specified <paramref name="innerNetObject"/> with the specified <paramref name="data"/> to the specified target.
|
||||
/// </summary>
|
||||
/// <param name="innerNetObject">The <see cref="InnerNetObject"/> to send the rpc on.</param>
|
||||
/// <param name="targetClientId">Target client id, defaults to broadcast.</param>
|
||||
/// <param name="data">The data to send.</param>
|
||||
/// <param name="ackCallback">The callback to invoke when this packet is acknowledged.</param>
|
||||
public void SendTo(InnerNetObject innerNetObject, int targetClientId, TData data, Action? ackCallback = null)
|
||||
{
|
||||
UnsafeSend(netObject, data, true, targetId, ackCallback);
|
||||
UnsafeSend(innerNetObject, data, true, targetClientId, ackCallback);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base type for custom rpc's but typed with generics and without any data.
|
||||
/// </summary>
|
||||
/// <typeparam name="TPlugin">The type of the plugin the the rpc is attached to.</typeparam>
|
||||
/// <typeparam name="TInnerNetObject">The type of the <see cref="InnerNetObject"/>.</typeparam>
|
||||
public abstract class CustomRpc<TPlugin, TInnerNetObject> : UnsafeCustomRpc where TPlugin : BasePlugin where TInnerNetObject : InnerNetObject
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected CustomRpc(TPlugin plugin, uint id) : base(plugin, id)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="UnsafeCustomRpc.UnsafePlugin"/>
|
||||
public TPlugin Plugin => (TPlugin) UnsafePlugin;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type InnerNetObjectType => typeof(TInnerNetObject);
|
||||
|
||||
/// <summary>
|
||||
/// Handles the rpc.
|
||||
/// </summary>
|
||||
/// <param name="innerNetObject">The <see cref="InnerNetObject"/> the rpc is being handled on.</param>
|
||||
public abstract void Handle(TInnerNetObject innerNetObject);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void UnsafeWrite(MessageWriter writer, object? data)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object? UnsafeRead(MessageReader reader)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void UnsafeHandle(InnerNetObject innerNetObject, object? data)
|
||||
{
|
||||
Handle((TInnerNetObject) innerNetObject);
|
||||
}
|
||||
|
||||
public void Send(InnerNetObject netObject, bool immediately = false, Action? ackCallback = null)
|
||||
/// <summary>
|
||||
/// Sends this custom rpc on the specified <paramref name="innerNetObject"/>.
|
||||
/// </summary>
|
||||
/// <param name="innerNetObject">The <see cref="InnerNetObject"/> to send the rpc on.</param>
|
||||
/// <param name="immediately">Whether to send it immediately.</param>
|
||||
/// <param name="ackCallback">The callback to invoke when this packet is acknowledged.</param>
|
||||
public void Send(InnerNetObject innerNetObject, bool immediately = false, Action? ackCallback = null)
|
||||
{
|
||||
UnsafeSend(netObject, immediately, ackCallback: ackCallback);
|
||||
UnsafeSend(innerNetObject, immediately, ackCallback: ackCallback);
|
||||
}
|
||||
|
||||
public void SendTo(InnerNetObject netObject, int targetId, Action? ackCallback = null)
|
||||
/// <summary>
|
||||
/// Sends this custom rpc on the specified <paramref name="innerNetObject"/> to the specified target.
|
||||
/// </summary>
|
||||
/// <param name="innerNetObject">The <see cref="InnerNetObject"/> to send the rpc on.</param>
|
||||
/// <param name="targetClientId">Target client id, defaults to broadcast.</param>
|
||||
/// <param name="ackCallback">The callback to invoke when this packet is acknowledged.</param>
|
||||
public void SendTo(InnerNetObject innerNetObject, int targetClientId, Action? ackCallback = null)
|
||||
{
|
||||
UnsafeSend(netObject, null, true, targetId, ackCallback);
|
||||
UnsafeSend(innerNetObject, null, true, targetClientId, ackCallback);
|
||||
}
|
||||
}
|
||||
@@ -10,16 +10,29 @@ using Reactor.Utilities;
|
||||
|
||||
namespace Reactor.Networking.Rpc;
|
||||
|
||||
/// <summary>
|
||||
/// Manages custom rpc's.
|
||||
/// </summary>
|
||||
public class CustomRpcManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Rpc call id for the reactor's custom rpc wrapper.
|
||||
/// </summary>
|
||||
public const byte CallId = byte.MaxValue;
|
||||
|
||||
private readonly List<UnsafeCustomRpc> _list = new();
|
||||
private readonly Dictionary<Type, Dictionary<Mod, Dictionary<uint, UnsafeCustomRpc>>> _map = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of all custom rpc's.
|
||||
/// </summary>
|
||||
public IReadOnlyList<UnsafeCustomRpc> List => _list.AsReadOnly();
|
||||
|
||||
public UnsafeCustomRpc Register(UnsafeCustomRpc customRpc)
|
||||
/// <summary>
|
||||
/// Registers a custom rpc.
|
||||
/// </summary>
|
||||
/// <param name="customRpc">The custom rpc to register.</param>
|
||||
public void Register(UnsafeCustomRpc customRpc)
|
||||
{
|
||||
customRpc.Manager = this;
|
||||
_list.Add(customRpc);
|
||||
@@ -31,10 +44,17 @@ public class CustomRpcManager
|
||||
{
|
||||
typeof(Rpc<>).MakeGenericType(customRpc.GetType()).GetProperty("Instance")!.SetValue(null, customRpc);
|
||||
}
|
||||
|
||||
return customRpc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of <see cref="InnerNetObject.HandleRpc"/> for reading and handling reactor custom rpc's.
|
||||
/// You can use this in your custom <see cref="InnerNetObject"/> to use reactor custom rpc's on it.
|
||||
/// </summary>
|
||||
/// <param name="innerNetObject">The <see cref="InnerNetObject"/> the rpc is handled on.</param>
|
||||
/// <param name="callId">The call id of the rpc.</param>
|
||||
/// <param name="reader">The <see cref="MessageReader"/> with the data of the rpc.</param>
|
||||
/// <returns>A value indicating whether the rpc was handled.</returns>
|
||||
/// <remarks>Reactor patches base game <see cref="InnerNetObject"/>s to use this.</remarks>
|
||||
public static bool HandleRpc(InnerNetObject innerNetObject, byte callId, MessageReader reader)
|
||||
{
|
||||
if (callId == CallId)
|
||||
|
||||
@@ -15,10 +15,24 @@ using Reactor.Utilities;
|
||||
|
||||
namespace Reactor.Networking.Rpc;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a custom rpc for method rpc.
|
||||
/// </summary>
|
||||
public class MethodRpc : UnsafeCustomRpc
|
||||
{
|
||||
public delegate object HandleDelegate(InnerNetObject innerNetObject, object[] args);
|
||||
private delegate object HandleDelegate(InnerNetObject innerNetObject, object[] args);
|
||||
|
||||
private readonly HandleDelegate _handle;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MethodRpc"/> class.
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin that the rpc is attached to.</param>
|
||||
/// <param name="method">The method of the method rpc.</param>
|
||||
/// <param name="id">The id of the rpc.</param>
|
||||
/// <param name="option">The send option of the rpc.</param>
|
||||
/// <param name="localHandling">The local handling method of the rpc.</param>
|
||||
/// <param name="sendImmediately">The value indicating whether the rpc should be sent immediately.</param>
|
||||
public MethodRpc(BasePlugin plugin, MethodInfo method, uint id, SendOption option, RpcLocalHandling localHandling, bool sendImmediately) : base(plugin, id)
|
||||
{
|
||||
Method = method;
|
||||
@@ -54,35 +68,58 @@ public class MethodRpc : UnsafeCustomRpc
|
||||
InnerNetObjectType = method.DeclaringType;
|
||||
}
|
||||
|
||||
Handle = Hook(method, parameters, method.IsStatic);
|
||||
_handle = Hook(method, parameters, method.IsStatic);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the method of the method rpc.
|
||||
/// </summary>
|
||||
public MethodInfo Method { get; }
|
||||
public HandleDelegate Handle { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
protected internal override bool IsSingleton => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type InnerNetObjectType { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override RpcLocalHandling LocalHandling { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override SendOption SendOption { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the method rpc should be sent immediately.
|
||||
/// </summary>
|
||||
public bool SendImmediately { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void UnsafeWrite(MessageWriter writer, object? data)
|
||||
{
|
||||
var args = (object[]) data!;
|
||||
MessageSerializer.Serialize(writer, args);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object UnsafeRead(MessageReader reader)
|
||||
{
|
||||
var args = MessageSerializer.Deserialize(reader, this);
|
||||
var parameters = Method.GetParameters();
|
||||
var args = new object[parameters.Length - 1];
|
||||
|
||||
for (var i = 1; i < parameters.Length; i++)
|
||||
{
|
||||
var parameter = parameters[i];
|
||||
args[i - 1] = reader.Deserialize(parameter.ParameterType);
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void UnsafeHandle(InnerNetObject innerNetObject, object? data)
|
||||
{
|
||||
var args = (object[]) data!;
|
||||
var result = Handle(innerNetObject, args);
|
||||
var result = _handle(innerNetObject, args);
|
||||
|
||||
if (result is IEnumerator enumerator)
|
||||
{
|
||||
@@ -90,6 +127,11 @@ public class MethodRpc : UnsafeCustomRpc
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends this method rpc on the specified <paramref name="innerNetObject"/> with the specified <paramref name="args"/>.
|
||||
/// </summary>
|
||||
/// <param name="innerNetObject">The <see cref="InnerNetObject"/> to send the rpc on.</param>
|
||||
/// <param name="args">The arguments to serialize and send.</param>
|
||||
public void Send(InnerNetObject innerNetObject, object[] args)
|
||||
{
|
||||
UnsafeSend(innerNetObject, args, SendImmediately);
|
||||
@@ -98,7 +140,7 @@ public class MethodRpc : UnsafeCustomRpc
|
||||
private static readonly MethodInfo _sendMethod = AccessTools.Method(typeof(MethodRpc), nameof(Send));
|
||||
|
||||
/// <summary>
|
||||
/// Hooks the <paramref name="method"/> rpc with a dynamic method that sends it
|
||||
/// Hooks the <paramref name="method"/> rpc with a dynamic method that sends it.
|
||||
/// </summary>
|
||||
private HandleDelegate Hook(MethodInfo method, ParameterInfo[] parameters, bool isStatic)
|
||||
{
|
||||
|
||||
@@ -3,36 +3,69 @@ using BepInEx.Unity.IL2CPP;
|
||||
|
||||
namespace Reactor.Networking.Rpc;
|
||||
|
||||
/// <summary>
|
||||
/// Shorthand for <see cref="CustomRpc{TPlugin,TInnerNetObject,TData}"/> with <see cref="PlayerControl"/> as the TInnerNetObject.
|
||||
/// </summary>
|
||||
/// <typeparam name="TPlugin">The type of the plugin the the rpc is attached to.</typeparam>
|
||||
/// <typeparam name="TData">The type of the rpc data.</typeparam>
|
||||
public abstract class PlayerCustomRpc<TPlugin, TData> : CustomRpc<TPlugin, PlayerControl, TData> where TPlugin : BasePlugin
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected PlayerCustomRpc(TPlugin plugin, uint id) : base(plugin, id)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends this custom rpc on the local player with the specified <paramref name="data"/>.
|
||||
/// </summary>
|
||||
/// <param name="data">The data to send.</param>
|
||||
/// <param name="immediately">Whether to send it immediately.</param>
|
||||
/// <param name="ackCallback">The callback to invoke when this packet is acknowledged.</param>
|
||||
public void Send(TData data, bool immediately = false, Action? ackCallback = null)
|
||||
{
|
||||
Send(PlayerControl.LocalPlayer, data, immediately, ackCallback);
|
||||
}
|
||||
|
||||
public void SendTo(int targetId, TData data, Action? ackCallback = null)
|
||||
/// <summary>
|
||||
/// Sends this custom rpc on the local player with the specified <paramref name="data"/> to the specified target.
|
||||
/// </summary>
|
||||
/// <param name="targetClientId">Target client id, defaults to broadcast.</param>
|
||||
/// <param name="data">The data to send.</param>
|
||||
/// <param name="ackCallback">The callback to invoke when this packet is acknowledged.</param>
|
||||
public void SendTo(int targetClientId, TData data, Action? ackCallback = null)
|
||||
{
|
||||
SendTo(PlayerControl.LocalPlayer, targetId, data, ackCallback);
|
||||
SendTo(PlayerControl.LocalPlayer, targetClientId, data, ackCallback);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shorthand for <see cref="CustomRpc{TPlugin,TInnerNetObject}"/> with <see cref="PlayerControl"/> as the TInnerNetObject.
|
||||
/// </summary>
|
||||
/// <typeparam name="TPlugin">The type of the plugin the the rpc is attached to.</typeparam>
|
||||
public abstract class PlayerCustomRpc<TPlugin> : CustomRpc<TPlugin, PlayerControl> where TPlugin : BasePlugin
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected PlayerCustomRpc(TPlugin plugin, uint id) : base(plugin, id)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends this custom rpc on the local player.
|
||||
/// </summary>
|
||||
/// <param name="immediately">Whether to send it immediately.</param>
|
||||
/// <param name="ackCallback">The callback to invoke when this packet is acknowledged.</param>
|
||||
public void Send(bool immediately = false, Action? ackCallback = null)
|
||||
{
|
||||
Send(PlayerControl.LocalPlayer, immediately, ackCallback);
|
||||
}
|
||||
|
||||
public void SendTo(int targetId, Action? ackCallback = null)
|
||||
/// <summary>
|
||||
/// Sends this custom rpc on the local player to the specified target.
|
||||
/// </summary>
|
||||
/// <param name="targetClientId">Target client id, defaults to broadcast.</param>
|
||||
/// <param name="ackCallback">The callback to invoke when this packet is acknowledged.</param>
|
||||
public void SendTo(int targetClientId, Action? ackCallback = null)
|
||||
{
|
||||
SendTo(PlayerControl.LocalPlayer, targetId, ackCallback);
|
||||
SendTo(PlayerControl.LocalPlayer, targetClientId, ackCallback);
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,17 @@ using System;
|
||||
|
||||
namespace Reactor.Networking.Rpc;
|
||||
|
||||
/// <summary>
|
||||
/// Provides access to singleton custom rpc's.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the custom rpc.</typeparam>
|
||||
public static class Rpc<T> where T : UnsafeCustomRpc
|
||||
{
|
||||
private static T? _instance;
|
||||
|
||||
/// <summary>
|
||||
/// Gets an instance of <typeparamref name="T"/> rpc.
|
||||
/// </summary>
|
||||
public static T Instance
|
||||
{
|
||||
get
|
||||
|
||||
@@ -1,8 +1,22 @@
|
||||
namespace Reactor.Networking.Rpc;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies how the rpc should be handled locally.
|
||||
/// </summary>
|
||||
public enum RpcLocalHandling
|
||||
{
|
||||
/// <summary>
|
||||
/// Don't do anything.
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Invoke Handle before sending.
|
||||
/// </summary>
|
||||
Before,
|
||||
|
||||
/// <summary>
|
||||
/// Invoke Handle after sending.
|
||||
/// </summary>
|
||||
After,
|
||||
}
|
||||
@@ -11,20 +11,53 @@ using Buffer = Il2CppSystem.Buffer;
|
||||
|
||||
namespace Reactor.Networking.Rpc;
|
||||
|
||||
/// <summary>
|
||||
/// Base type for custom rpc's.
|
||||
/// </summary>
|
||||
public abstract class UnsafeCustomRpc
|
||||
{
|
||||
internal CustomRpcManager? Manager { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the rpc is a singleton and can be used with <see cref="Rpc{T}"/>.
|
||||
/// </summary>
|
||||
protected internal virtual bool IsSingleton => true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the id of the rpc.
|
||||
/// </summary>
|
||||
public uint Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin of the rpc.
|
||||
/// </summary>
|
||||
public BasePlugin UnsafePlugin { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mod of the rpc.
|
||||
/// </summary>
|
||||
public Mod Mod { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the InnerNetObject type the rpc targets.
|
||||
/// </summary>
|
||||
public abstract Type InnerNetObjectType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the send option of the rpc.
|
||||
/// </summary>
|
||||
public virtual SendOption SendOption => SendOption.Reliable;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the local handling method of the rpc.
|
||||
/// </summary>
|
||||
public abstract RpcLocalHandling LocalHandling { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UnsafeCustomRpc"/> class.
|
||||
/// </summary>
|
||||
/// <param name="plugin">The plugin that the rpc is attached to.</param>
|
||||
/// <param name="id">The id of the rpc.</param>
|
||||
protected UnsafeCustomRpc(BasePlugin plugin, uint id)
|
||||
{
|
||||
UnsafePlugin = plugin;
|
||||
@@ -32,10 +65,35 @@ public abstract class UnsafeCustomRpc
|
||||
Id = id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes <paramref name="data"/> to the <paramref name="writer"/>.
|
||||
/// </summary>
|
||||
/// <param name="writer">The writer to write to.</param>
|
||||
/// <param name="data">The data to be written.</param>
|
||||
public abstract void UnsafeWrite(MessageWriter writer, object? data);
|
||||
|
||||
/// <summary>
|
||||
/// Reads rpc data from the <paramref name="reader"/>.
|
||||
/// </summary>
|
||||
/// <param name="reader">The reader to read from.</param>
|
||||
/// <returns>The rpc data from the <paramref name="reader"/>.</returns>
|
||||
public abstract object? UnsafeRead(MessageReader reader);
|
||||
|
||||
/// <summary>
|
||||
/// Handles rpc data.
|
||||
/// </summary>
|
||||
/// <param name="innerNetObject">The <see cref="InnerNetObject"/> the rpc is being handled on.</param>
|
||||
/// <param name="data">The data associated with the rpc.</param>
|
||||
public abstract void UnsafeHandle(InnerNetObject innerNetObject, object? data);
|
||||
|
||||
/// <summary>
|
||||
/// Sends this custom rpc on the specified <paramref name="innerNetObject"/> with the specified <paramref name="data"/>.
|
||||
/// </summary>
|
||||
/// <param name="innerNetObject">The <see cref="InnerNetObject"/> to send the rpc on.</param>
|
||||
/// <param name="data">The data to send.</param>
|
||||
/// <param name="immediately">Whether to send it immediately.</param>
|
||||
/// <param name="targetClientId">Target client id, defaults to broadcast.</param>
|
||||
/// <param name="ackCallback">The callback to invoke when this packet is acknowledged.</param>
|
||||
public void UnsafeSend(InnerNetObject innerNetObject, object? data, bool immediately = false, int targetClientId = -1, Action? ackCallback = null)
|
||||
{
|
||||
if (innerNetObject == null) throw new ArgumentNullException(nameof(innerNetObject));
|
||||
|
||||
@@ -8,30 +8,41 @@ using Reactor.Networking.Attributes;
|
||||
|
||||
namespace Reactor.Networking.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a MessageConverter for <see cref="InnerNetObject"/>.
|
||||
/// </summary>
|
||||
[MessageConverter]
|
||||
public class InnerNetObjectConverter : MessageConverter<InnerNetObject>
|
||||
public class InnerNetObjectConverter : MessageConverter<InnerNetObject?>
|
||||
{
|
||||
private static readonly MethodInfo _readNetObject = AccessTools.Method(typeof(MessageExtensions), nameof(MessageExtensions.ReadNetObject));
|
||||
private static readonly Dictionary<Type, Func<MessageReader, InnerNetObject>> _readNetObjectMap = new();
|
||||
private static readonly Dictionary<Type, Func<MessageReader, InnerNetObject?>> _readNetObjectMap = new();
|
||||
|
||||
public static InnerNetObject ReadNetObject(MessageReader reader, Type t)
|
||||
/// <summary>
|
||||
/// <see cref="MessageExtensions.ReadNetObject{T}"/> but without generics.
|
||||
/// </summary>
|
||||
/// <param name="reader">The <see cref="MessageReader"/> to read from.</param>
|
||||
/// <param name="innerNetObjectType">The type of the <see cref="InnerNetObject"/>.</param>
|
||||
/// <returns>The <see cref="InnerNetObject"/> read from the <paramref name="reader"/>.</returns>
|
||||
public static InnerNetObject? ReadNetObject(MessageReader reader, Type innerNetObjectType)
|
||||
{
|
||||
if (_readNetObjectMap.TryGetValue(t, out var value))
|
||||
if (_readNetObjectMap.TryGetValue(innerNetObjectType, out var value))
|
||||
{
|
||||
return value(reader);
|
||||
}
|
||||
|
||||
var @delegate = _readNetObject.MakeGenericMethod(t).CreateDelegate<Func<MessageReader, InnerNetObject>>();
|
||||
_readNetObjectMap[t] = @delegate;
|
||||
var @delegate = _readNetObject.MakeGenericMethod(innerNetObjectType).CreateDelegate<Func<MessageReader, InnerNetObject>>();
|
||||
_readNetObjectMap[innerNetObjectType] = @delegate;
|
||||
return @delegate(reader);
|
||||
}
|
||||
|
||||
public override void Write(MessageWriter writer, InnerNetObject value)
|
||||
/// <inheritdoc />
|
||||
public override void Write(MessageWriter writer, InnerNetObject? value)
|
||||
{
|
||||
MessageExtensions.WriteNetObject(writer, value);
|
||||
writer.WriteNetObject(value);
|
||||
}
|
||||
|
||||
public override InnerNetObject Read(MessageReader reader, Type objectType)
|
||||
/// <inheritdoc />
|
||||
public override InnerNetObject? Read(MessageReader reader, Type objectType)
|
||||
{
|
||||
return ReadNetObject(reader, objectType);
|
||||
}
|
||||
|
||||
@@ -3,26 +3,60 @@ using Hazel;
|
||||
|
||||
namespace Reactor.Networking.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Base type for MessageConverter's.
|
||||
/// </summary>
|
||||
public abstract class UnsafeMessageConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Writes the <paramref name="value"/> to the <paramref name="writer"/>.
|
||||
/// </summary>
|
||||
/// <param name="writer">The <see cref="MessageWriter"/> to write to.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
public abstract void UnsafeWrite(MessageWriter writer, object value);
|
||||
|
||||
/// <summary>
|
||||
/// Reads an object of <paramref name="objectType"/> from the <paramref name="reader"/>.
|
||||
/// </summary>
|
||||
/// <param name="reader">The <see cref="MessageReader"/> to read from.</param>
|
||||
/// <param name="objectType">The type of the object being read.</param>
|
||||
/// <returns>The object read from the <paramref name="reader"/>.</returns>
|
||||
public abstract object UnsafeRead(MessageReader reader, Type objectType);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether this MessageConverter can convert the specified type.
|
||||
/// </summary>
|
||||
/// <param name="objectType">The type to check.</param>
|
||||
/// <returns>A value indicating whether this MessageConverter can convert the specified type.</returns>
|
||||
public abstract bool CanConvert(Type objectType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Base type for MessageConverter's but typed with generics.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the value being converted.</typeparam>
|
||||
public abstract class MessageConverter<T> : UnsafeMessageConverter
|
||||
{
|
||||
/// <inheritdoc cref="UnsafeMessageConverter.UnsafeWrite"/>
|
||||
public abstract void Write(MessageWriter writer, T value);
|
||||
|
||||
/// <summary>
|
||||
/// Reads a <typeparamref name="T"/> from the <paramref name="reader"/>.
|
||||
/// </summary>
|
||||
/// <param name="reader">The <see cref="MessageReader"/> to read from.</param>
|
||||
/// <param name="objectType">The type of the object being read.</param>
|
||||
/// <returns>The object read from the <paramref name="reader"/>.</returns>
|
||||
public abstract T Read(MessageReader reader, Type objectType);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return typeof(T).IsAssignableFrom(objectType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void UnsafeWrite(MessageWriter writer, object value) => Write(writer, (T) value);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override object UnsafeRead(MessageReader reader, Type objectType) => Read(reader, objectType)!;
|
||||
}
|
||||
@@ -3,23 +3,34 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Hazel;
|
||||
using Reactor.Networking.Extensions;
|
||||
using Reactor.Networking.Rpc;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Reactor.Networking.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Provides de/serializing of objects from/into <see cref="MessageReader"/>/<see cref="MessageWriter"/>.
|
||||
/// </summary>
|
||||
public static class MessageSerializer
|
||||
{
|
||||
private static List<UnsafeMessageConverter> MessageConverters { get; } = new();
|
||||
|
||||
private static Dictionary<Type, UnsafeMessageConverter?> MessageConverterMap { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Registers a MessageConverter.
|
||||
/// </summary>
|
||||
/// <param name="messageConverter">The MessageConverter to be registered.</param>
|
||||
public static void Register(UnsafeMessageConverter messageConverter)
|
||||
{
|
||||
MessageConverters.Add(messageConverter);
|
||||
MessageConverterMap.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a MessageConverter for the specified <paramref name="type"/>.
|
||||
/// </summary>
|
||||
/// <param name="type">The type of an object.</param>
|
||||
/// <returns>A MessageConverted that can convert the specified <see cref="Type"/>.</returns>
|
||||
public static UnsafeMessageConverter? FindConverter(Type type)
|
||||
{
|
||||
if (MessageConverterMap.TryGetValue(type, out var value))
|
||||
@@ -33,6 +44,11 @@ public static class MessageSerializer
|
||||
return converter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializes <paramref name="args"/> to the <paramref name="writer"/>.
|
||||
/// </summary>
|
||||
/// <param name="writer">The <see cref="MessageWriter"/> to write to.</param>
|
||||
/// <param name="args">The args to be written.</param>
|
||||
public static void Serialize(MessageWriter writer, object[] args)
|
||||
{
|
||||
foreach (var arg in args)
|
||||
@@ -41,9 +57,14 @@ public static class MessageSerializer
|
||||
}
|
||||
}
|
||||
|
||||
public static void Serialize(this MessageWriter writer, object o)
|
||||
/// <summary>
|
||||
/// Serializes an <paramref name="object"/> to the <paramref name="writer"/>.
|
||||
/// </summary>
|
||||
/// <param name="writer">The <see cref="MessageWriter"/> to write to.</param>
|
||||
/// <param name="object">The <see cref="object"/> to be written.</param>
|
||||
public static void Serialize(this MessageWriter writer, object @object)
|
||||
{
|
||||
switch (o)
|
||||
switch (@object)
|
||||
{
|
||||
case int i:
|
||||
writer.WritePacked(i);
|
||||
@@ -73,84 +94,76 @@ public static class MessageSerializer
|
||||
writer.Write(i);
|
||||
break;
|
||||
default:
|
||||
var converter = FindConverter(o.GetType());
|
||||
var converter = FindConverter(@object.GetType());
|
||||
if (converter != null)
|
||||
{
|
||||
converter.UnsafeWrite(writer, o);
|
||||
converter.UnsafeWrite(writer, @object);
|
||||
break;
|
||||
}
|
||||
|
||||
throw new NotSupportedException("Couldn't serialize " + o.GetType());
|
||||
throw new NotSupportedException("Couldn't serialize " + @object.GetType());
|
||||
}
|
||||
}
|
||||
|
||||
public static object[] Deserialize(MessageReader reader, MethodRpc methodRpc)
|
||||
/// <summary>
|
||||
/// Deserializes an <see cref="object"/> of <paramref name="objectType"/> from the <paramref name="reader"/>.
|
||||
/// </summary>
|
||||
/// <param name="reader">The <see cref="MessageReader"/> to read from.</param>
|
||||
/// <param name="objectType">The <see cref="Type"/> of the object.</param>
|
||||
/// <returns>An <see cref="object"/> from the <paramref name="reader"/>.</returns>
|
||||
public static object Deserialize(this MessageReader reader, Type objectType)
|
||||
{
|
||||
var parameters = methodRpc.Method.GetParameters();
|
||||
var args = new object[parameters.Length - 1];
|
||||
|
||||
for (var i = 1; i < parameters.Length; i++)
|
||||
{
|
||||
var parameter = parameters[i];
|
||||
args[i - 1] = reader.Deserialize(parameter.ParameterType);
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
public static object Deserialize(this MessageReader reader, Type t)
|
||||
{
|
||||
if (t == typeof(int))
|
||||
if (objectType == typeof(int))
|
||||
{
|
||||
return reader.ReadPackedInt32();
|
||||
}
|
||||
|
||||
if (t == typeof(uint))
|
||||
if (objectType == typeof(uint))
|
||||
{
|
||||
return reader.ReadPackedUInt32();
|
||||
}
|
||||
|
||||
if (t == typeof(byte))
|
||||
if (objectType == typeof(byte))
|
||||
{
|
||||
return reader.ReadByte();
|
||||
}
|
||||
|
||||
if (t == typeof(float))
|
||||
if (objectType == typeof(float))
|
||||
{
|
||||
return reader.ReadSingle();
|
||||
}
|
||||
|
||||
if (t == typeof(sbyte))
|
||||
if (objectType == typeof(sbyte))
|
||||
{
|
||||
return reader.ReadSByte();
|
||||
}
|
||||
|
||||
if (t == typeof(ushort))
|
||||
if (objectType == typeof(ushort))
|
||||
{
|
||||
return reader.ReadUInt16();
|
||||
}
|
||||
|
||||
if (t == typeof(bool))
|
||||
if (objectType == typeof(bool))
|
||||
{
|
||||
return reader.ReadBoolean();
|
||||
}
|
||||
|
||||
if (t == typeof(Vector2))
|
||||
if (objectType == typeof(Vector2))
|
||||
{
|
||||
return reader.ReadVector2();
|
||||
}
|
||||
|
||||
if (t == typeof(string))
|
||||
if (objectType == typeof(string))
|
||||
{
|
||||
return reader.ReadString();
|
||||
}
|
||||
|
||||
var converter = FindConverter(t);
|
||||
var converter = FindConverter(objectType);
|
||||
if (converter != null)
|
||||
{
|
||||
return converter.UnsafeRead(reader, t);
|
||||
return converter.UnsafeRead(reader, objectType);
|
||||
}
|
||||
|
||||
throw new NotSupportedException("Couldn't deserialize " + t);
|
||||
throw new NotSupportedException("Couldn't deserialize " + objectType);
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ using UnityEngine;
|
||||
namespace Reactor.Patches.Fixes;
|
||||
|
||||
/// <summary>
|
||||
/// "Fixes" an issue where empty TextBoxes have wrong cursor positions
|
||||
/// "Fixes" an issue where empty TextBoxes have wrong cursor positions.
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(TextMeshProExtensions), nameof(TextMeshProExtensions.CursorPos))]
|
||||
internal static class CursorPosPatch
|
||||
|
||||
@@ -17,9 +17,7 @@ internal static class ServerInfoFixesPatch
|
||||
Info($"Joining {__instance.networkAddress}:{__instance.networkPort}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fixes hardcoded port and filters out IPv6 in DnsRegionInfo
|
||||
/// </summary>
|
||||
// Fixes hardcoded port and filters out IPv6 in DnsRegionInfo.
|
||||
[HarmonyPatch(typeof(DnsRegionInfo), nameof(DnsRegionInfo.PopulateServers))]
|
||||
[HarmonyPrefix]
|
||||
public static bool FixPopulateServers(DnsRegionInfo __instance)
|
||||
|
||||
@@ -6,7 +6,7 @@ using HarmonyLib;
|
||||
namespace Reactor.Patches.Miscellaneous;
|
||||
|
||||
/// <summary>
|
||||
/// Allows launching steam builds outside of steam
|
||||
/// Allows launching steam builds outside of steam.
|
||||
/// </summary>
|
||||
[HarmonyPatch]
|
||||
internal static class StandaloneSteamPatch
|
||||
|
||||
@@ -10,13 +10,20 @@ using Version = SemanticVersioning.Version;
|
||||
|
||||
namespace Reactor.Patches;
|
||||
|
||||
/// <summary>
|
||||
/// Shows the reactor version on the main menu.
|
||||
/// </summary>
|
||||
public static class ReactorVersionShower
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the <see cref="TextMeshPro"/> text.
|
||||
/// </summary>
|
||||
public static TextMeshPro? Text { get; private set; }
|
||||
|
||||
public static event TextUpdatedHandler? TextUpdated;
|
||||
|
||||
public delegate void TextUpdatedHandler(TextMeshPro text);
|
||||
/// <summary>
|
||||
/// Occurs when <see cref="Text"/> is updated.
|
||||
/// </summary>
|
||||
public static event Action<TextMeshPro>? TextUpdated;
|
||||
|
||||
internal static void Initialize()
|
||||
{
|
||||
@@ -59,6 +66,9 @@ public static class ReactorVersionShower
|
||||
return $"{version.Major}.{version.Minor}.{version.Patch}{(version.PreRelease == null ? string.Empty : $"-{version.PreRelease}")}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates <see cref="Text"/> with reactor version and fires <see cref="TextUpdated"/>.
|
||||
/// </summary>
|
||||
public static void UpdateText()
|
||||
{
|
||||
if (Text == null) return;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<Description>Core mod and API for Among Us</Description>
|
||||
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
|
||||
<!-- SourceLink -->
|
||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||
|
||||
@@ -18,17 +18,28 @@ using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Reactor;
|
||||
|
||||
/// <summary>
|
||||
/// Reactor's main class.
|
||||
/// </summary>
|
||||
[BepInAutoPlugin("gg.reactor.api")]
|
||||
[BepInProcess("Among Us.exe")]
|
||||
public partial class ReactorPlugin : BasePlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets harmony instance.
|
||||
/// </summary>
|
||||
public Harmony Harmony { get; } = new(Id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets custom rpc manager.
|
||||
/// </summary>
|
||||
public CustomRpcManager CustomRpcManager { get; } = new();
|
||||
|
||||
public ConfigEntry<bool>? AllowVanillaServers { get; private set; }
|
||||
internal ConfigEntry<bool>? AllowVanillaServers { get; private set; }
|
||||
|
||||
internal RegionInfoWatcher RegionInfoWatcher { get; } = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public ReactorPlugin()
|
||||
{
|
||||
PluginSingleton<ReactorPlugin>.Instance = this;
|
||||
@@ -40,6 +51,7 @@ public partial class ReactorPlugin : BasePlugin
|
||||
MethodRpcAttribute.Initialize();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Load()
|
||||
{
|
||||
AllowVanillaServers = Config.Bind("Features", "Allow vanilla servers", false, "Whether reactor should ignore servers not responding to modded handshake. This config is ignored if any plugin uses custom rpcs!");
|
||||
@@ -63,6 +75,7 @@ public partial class ReactorPlugin : BasePlugin
|
||||
}));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Unload()
|
||||
{
|
||||
Harmony.UnpatchSelf();
|
||||
|
||||
@@ -10,10 +10,19 @@ using UnityEngine;
|
||||
|
||||
namespace Reactor.Utilities;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a standard way of loading asset bundles from a file or embedded resource.
|
||||
/// </summary>
|
||||
public static class AssetBundleManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension of a asset bundle.
|
||||
/// </summary>
|
||||
public const string BundleExtension = ".bundle";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target name of the current system.
|
||||
/// </summary>
|
||||
public static string TargetName { get; }
|
||||
|
||||
static AssetBundleManager()
|
||||
@@ -48,6 +57,11 @@ public static class AssetBundleManager
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file name for an asset bundle named <paramref name="assetBundleName"/>.
|
||||
/// </summary>
|
||||
/// <param name="assetBundleName">The name of the asset bundle.</param>
|
||||
/// <returns>File name of the asset bundle.</returns>
|
||||
public static string GetFileName(string assetBundleName) => $"{assetBundleName}-{TargetName}{BundleExtension}";
|
||||
|
||||
private static bool TryFindFile(Assembly assembly, string fileName, [NotNullWhen(true)] out string? path)
|
||||
@@ -85,11 +99,24 @@ public static class AssetBundleManager
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Synchronously loads an <see cref="AssetBundle"/> named <paramref name="name"/> from the calling assembly.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the asset bundle.</param>
|
||||
/// <returns>The loaded assetbundle.</returns>
|
||||
/// <exception cref="AssetBundleNotFoundException">Couldn't find an assetbundle named <paramref name="name"/>.</exception>
|
||||
public static AssetBundle Load(string name)
|
||||
{
|
||||
return Load(Assembly.GetCallingAssembly(), name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Synchronously loads an <see cref="AssetBundle"/> named <paramref name="name"/> from the specified <paramref name="assembly"/>.
|
||||
/// </summary>
|
||||
/// <param name="assembly">The specified assembly.</param>
|
||||
/// <param name="name">The name of the asset bundle.</param>
|
||||
/// <returns>The loaded assetbundle.</returns>
|
||||
/// <exception cref="AssetBundleNotFoundException">Couldn't find an assetbundle named <paramref name="name"/>.</exception>
|
||||
public static AssetBundle Load(Assembly assembly, string name)
|
||||
{
|
||||
var fileName = GetFileName(name);
|
||||
@@ -107,11 +134,24 @@ public static class AssetBundleManager
|
||||
throw new AssetBundleNotFoundException(name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously loads an <see cref="AssetBundle"/> named <paramref name="name"/> from the calling assembly.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the asset bundle.</param>
|
||||
/// <returns>The loaded assetbundle.</returns>
|
||||
/// <exception cref="AssetBundleNotFoundException">Couldn't find an assetbundle named <paramref name="name"/>.</exception>
|
||||
public static AssetBundleCreateRequest LoadAsync(string name)
|
||||
{
|
||||
return LoadAsync(Assembly.GetCallingAssembly(), name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchronously loads an <see cref="AssetBundle"/> named <paramref name="name"/> from the specified <paramref name="assembly"/>.
|
||||
/// </summary>
|
||||
/// <param name="assembly">The specified assembly.</param>
|
||||
/// <param name="name">The name of the asset bundle.</param>
|
||||
/// <returns>The loaded assetbundle.</returns>
|
||||
/// <exception cref="AssetBundleNotFoundException">Couldn't find an assetbundle named <paramref name="name"/>.</exception>
|
||||
public static AssetBundleCreateRequest LoadAsync(Assembly assembly, string name)
|
||||
{
|
||||
var fileName = GetFileName(name);
|
||||
@@ -130,6 +170,9 @@ public static class AssetBundleManager
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The exception that is thrown when an assetbundle is not found.
|
||||
/// </summary>
|
||||
public class AssetBundleNotFoundException : IOException
|
||||
{
|
||||
internal AssetBundleNotFoundException(string name) : base("Couldn't find an assetbundle named " + name)
|
||||
|
||||
@@ -8,20 +8,30 @@ using Il2CppInterop.Runtime.Injection;
|
||||
namespace Reactor.Utilities.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// Utility attribute for automatically calling <see cref="ClassInjector.RegisterTypeInIl2Cpp{T}()"/>
|
||||
/// Automatically registers an il2cpp type using <see cref="ClassInjector.RegisterTypeInIl2Cpp{T}()"/>.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class)]
|
||||
public sealed class RegisterInIl2CppAttribute : Attribute
|
||||
{
|
||||
private static readonly HashSet<Assembly> _registeredAssemblies = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets il2cpp interfaces to be injected with this type.
|
||||
/// </summary>
|
||||
public Type[] Interfaces { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RegisterInIl2CppAttribute"/> class without any interfaces.
|
||||
/// </summary>
|
||||
public RegisterInIl2CppAttribute()
|
||||
{
|
||||
Interfaces = Type.EmptyTypes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RegisterInIl2CppAttribute"/> class with interfaces.
|
||||
/// </summary>
|
||||
/// <param name="interfaces">Il2Cpp interfaces to be injected with this type.</param>
|
||||
public RegisterInIl2CppAttribute(params Type[] interfaces)
|
||||
{
|
||||
Interfaces = interfaces;
|
||||
@@ -51,9 +61,10 @@ public sealed class RegisterInIl2CppAttribute : Attribute
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers in Il2Cpp all types marked with <see cref="RegisterInIl2CppAttribute"/> from the specified assembly.<br/>
|
||||
/// Registers all Il2Cpp types annotated with <see cref="RegisterInIl2CppAttribute"/> in the specified <paramref name="assembly"/>.
|
||||
/// </summary>
|
||||
/// <param name="assembly"></param>
|
||||
/// <remarks>This is called automatically on plugin assemblies so you probably don't need to call this.</remarks>
|
||||
/// <param name="assembly">The assembly to search.</param>
|
||||
public static void Register(Assembly assembly)
|
||||
{
|
||||
if (_registeredAssemblies.Contains(assembly)) return;
|
||||
|
||||
@@ -5,8 +5,15 @@ using UnityEngine.UI;
|
||||
|
||||
namespace Reactor.Utilities;
|
||||
|
||||
/// <summary>
|
||||
/// Provides utilities for working with unity canvas ui.
|
||||
/// </summary>
|
||||
public static class CanvasUtilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a canvas.
|
||||
/// </summary>
|
||||
/// <returns>A canvas <see cref="GameObject"/>.</returns>
|
||||
public static GameObject CreateCanvas()
|
||||
{
|
||||
var gameObject = new GameObject("Canvas");
|
||||
@@ -23,6 +30,10 @@ public static class CanvasUtilities
|
||||
return gameObject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an EventSystem.
|
||||
/// </summary>
|
||||
/// <returns>An EventSystem <see cref="GameObject"/>.</returns>
|
||||
public static GameObject CreateEventSystem()
|
||||
{
|
||||
var gameObject = new GameObject("EventSystem");
|
||||
@@ -34,8 +45,11 @@ public static class CanvasUtilities
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shortcut for empty texture
|
||||
/// Creates an empty texture of specified <paramref name="width"/> and <paramref name="height"/>.
|
||||
/// </summary>
|
||||
/// <param name="width">The width of the texture.</param>
|
||||
/// <param name="height">The height of the texture.</param>
|
||||
/// <returns>An empty texture of specified size.</returns>
|
||||
public static Texture2D CreateEmptyTexture(int width = 0, int height = 0)
|
||||
{
|
||||
return new Texture2D(width, height, TextureFormat.RGBA32, Texture.GenerateAllMips, false, IntPtr.Zero);
|
||||
|
||||
@@ -8,6 +8,9 @@ using UnityEngine;
|
||||
|
||||
namespace Reactor.Utilities;
|
||||
|
||||
/// <summary>
|
||||
/// Provides utilities for starting coroutines.
|
||||
/// </summary>
|
||||
public static class Coroutines
|
||||
{
|
||||
[RegisterInIl2Cpp]
|
||||
@@ -33,20 +36,29 @@ public static class Coroutines
|
||||
|
||||
private static readonly ConditionalWeakTable<IEnumerator, Coroutine> _ourCoroutineStore = new();
|
||||
|
||||
[return: NotNullIfNotNull("routine")]
|
||||
public static IEnumerator? Start(IEnumerator? routine)
|
||||
/// <summary>
|
||||
/// Starts a coroutine.
|
||||
/// </summary>
|
||||
/// <param name="coroutine">The coroutine to start.</param>
|
||||
/// <returns>Specified <paramref name="coroutine"/>.</returns>
|
||||
[return: NotNullIfNotNull("coroutine")]
|
||||
public static IEnumerator? Start(IEnumerator? coroutine)
|
||||
{
|
||||
if (routine != null)
|
||||
if (coroutine != null)
|
||||
{
|
||||
_ourCoroutineStore.AddOrUpdate(routine, Component.Instance!.StartCoroutine(routine));
|
||||
_ourCoroutineStore.AddOrUpdate(coroutine, Component.Instance!.StartCoroutine(coroutine));
|
||||
}
|
||||
|
||||
return routine;
|
||||
return coroutine;
|
||||
}
|
||||
|
||||
public static void Stop(IEnumerator? enumerator)
|
||||
/// <summary>
|
||||
/// Stops a coroutine.
|
||||
/// </summary>
|
||||
/// <param name="coroutine">The coroutine to stop.</param>
|
||||
public static void Stop(IEnumerator? coroutine)
|
||||
{
|
||||
if (enumerator != null && _ourCoroutineStore.TryGetValue(enumerator, out var routine))
|
||||
if (coroutine != null && _ourCoroutineStore.TryGetValue(coroutine, out var routine))
|
||||
{
|
||||
Component.Instance!.StopCoroutine(routine);
|
||||
}
|
||||
|
||||
@@ -4,8 +4,14 @@ using UnityEngine.UI;
|
||||
|
||||
namespace Reactor.Utilities;
|
||||
|
||||
/// <summary>
|
||||
/// Provides unity things that were stripped.
|
||||
/// </summary>
|
||||
public static class DefaultBundle
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets standard resources used for canvas ui.
|
||||
/// </summary>
|
||||
public static DefaultControls.Resources StandardResources { get; internal set; } = null!;
|
||||
|
||||
internal static void Load()
|
||||
|
||||
@@ -6,9 +6,15 @@ using UnityEngine;
|
||||
|
||||
namespace Reactor.Utilities;
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches actions on Unity main thread.
|
||||
/// </summary>
|
||||
[RegisterInIl2Cpp]
|
||||
public sealed class Dispatcher : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the instance.
|
||||
/// </summary>
|
||||
public static Dispatcher Instance { get; private set; } = null!;
|
||||
|
||||
private readonly Queue<Action> _queue = new();
|
||||
@@ -29,6 +35,10 @@ public sealed class Dispatcher : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueues an <paramref name="action"/>.
|
||||
/// </summary>
|
||||
/// <param name="action">The action to enqueue.</param>
|
||||
[HideFromIl2Cpp]
|
||||
public void Enqueue(Action action)
|
||||
{
|
||||
|
||||
@@ -3,10 +3,20 @@ using UnityEngine;
|
||||
|
||||
namespace Reactor.Utilities.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="AssetBundle"/>.
|
||||
/// </summary>
|
||||
public static class AssetBundleExtensions
|
||||
{
|
||||
public static T? LoadAsset<T>(this AssetBundle assetBundle, string name) where T : Object
|
||||
/// <summary>
|
||||
/// Loads an asset with <paramref name="name"/> from the <paramref name="bundle"/> with the specified <typeparamref name="T"/> type.
|
||||
/// </summary>
|
||||
/// <param name="bundle">The <see cref="AssetBundle"/> to load the asset from.</param>
|
||||
/// <param name="name">The name of the asset.</param>
|
||||
/// <typeparam name="T">The type of the asset.</typeparam>
|
||||
/// <returns>The loaded asset or null if it wasn't found.</returns>
|
||||
public static T? LoadAsset<T>(this AssetBundle bundle, string name) where T : Object
|
||||
{
|
||||
return assetBundle.LoadAsset(name, Il2CppType.Of<T>())?.Cast<T>();
|
||||
return bundle.LoadAsset(name, Il2CppType.Of<T>())?.Cast<T>();
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,15 @@ using UnityEngine;
|
||||
namespace Reactor.Utilities.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// GUI utilities
|
||||
/// Provides extension methods for unity imgui.
|
||||
/// </summary>
|
||||
public static class GUIExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Clamp Rect to screen size
|
||||
/// Clamps <paramref name="rect"/> to the screen size.
|
||||
/// </summary>
|
||||
/// <param name="rect">The <see cref="Rect"/> to clamp.</param>
|
||||
/// <returns>A clamped rect.</returns>
|
||||
public static Rect ClampScreen(this Rect rect)
|
||||
{
|
||||
rect.x = Mathf.Clamp(rect.x, 0, Screen.width - rect.width);
|
||||
@@ -19,8 +21,10 @@ public static class GUIExtensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset Rect size
|
||||
/// Set <paramref name="rect"/>'s <see cref="Rect.width"/> and <see cref="Rect.height"/> to 0.
|
||||
/// </summary>
|
||||
/// <param name="rect">The <see cref="Rect"/> to reset.</param>
|
||||
/// <returns>A rect with reset size.</returns>
|
||||
public static Rect ResetSize(this Rect rect)
|
||||
{
|
||||
rect.width = rect.height = 0;
|
||||
@@ -29,13 +33,23 @@ public static class GUIExtensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create <see cref="Sprite"/> from <paramref name="tex"/>
|
||||
/// Creates a <see cref="Sprite"/> from the <paramref name="texture"/>.
|
||||
/// </summary>
|
||||
public static Sprite CreateSprite(this Texture2D tex)
|
||||
/// <param name="texture">Texture from which to obtain the sprite graphic.</param>
|
||||
/// <param name="pivot">Sprite's pivot point relative to its graphic rectangle.</param>
|
||||
/// <param name="pixelsPerUnit">The number of pixels in the sprite that correspond to one unit in world space.</param>
|
||||
/// <returns>A sprite.</returns>
|
||||
public static Sprite CreateSprite(this Texture2D texture, Vector2? pivot = null, float pixelsPerUnit = 100f)
|
||||
{
|
||||
return Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f), 100f);
|
||||
return Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), pivot ?? new Vector2(0.5f, 0.5f), pixelsPerUnit);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets <paramref name="rectTransform"/>'s width and height.
|
||||
/// </summary>
|
||||
/// <param name="rectTransform">The <see cref="RectTransform"/> to set the size of.</param>
|
||||
/// <param name="width">The width.</param>
|
||||
/// <param name="height">The height.</param>
|
||||
public static void SetSize(this RectTransform rectTransform, float width, float height)
|
||||
{
|
||||
rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, width);
|
||||
|
||||
@@ -3,8 +3,17 @@ using Il2CppInterop.Runtime.InteropTypes.Arrays;
|
||||
|
||||
namespace Reactor.Utilities.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for Il2CppInterop.
|
||||
/// </summary>
|
||||
public static class Il2CppInteropExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a span over a <see cref="Il2CppStructArray{T}"/>.
|
||||
/// </summary>
|
||||
/// <param name="array">The array to create a span over.</param>
|
||||
/// <typeparam name="T">The type of items in the <see cref="Il2CppStructArray{T}"/>.</typeparam>
|
||||
/// <returns>A span.</returns>
|
||||
public static unsafe Span<T> ToSpan<T>(this Il2CppStructArray<T> array) where T : unmanaged
|
||||
{
|
||||
return new Span<T>(IntPtr.Add(array.Pointer, IntPtr.Size * 4).ToPointer(), array.Length);
|
||||
|
||||
@@ -4,29 +4,45 @@ using System.Linq;
|
||||
|
||||
namespace Reactor.Utilities.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides Random related extension methods.
|
||||
/// </summary>
|
||||
public static class RandomExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns random <typeparamref name="T"/> from <paramref name="input"/> using <paramref name="random"/>
|
||||
/// Returns a random double that is within a specified range.
|
||||
/// </summary>
|
||||
public static T? Random<T>(this IEnumerable<T> input, Random random)
|
||||
{
|
||||
var list = input as IList<T> ?? input.ToList();
|
||||
return list.Count == 0 ? default : list[random.Next(0, list.Count)];
|
||||
}
|
||||
|
||||
/// <summary>Returns a random double that is within a specified range.</summary>
|
||||
/// <param name="random">The <see cref="System.Random"/> to get the random number from.</param>
|
||||
/// <param name="minValue">The minimum value of the range.</param>
|
||||
/// <param name="maxValue">The maximum value of the range.</param>
|
||||
/// <returns>A random double that is within a specified range.</returns>
|
||||
public static double NextDouble(this Random random, double minValue, double maxValue)
|
||||
{
|
||||
return random.NextDouble() * (maxValue - minValue) + minValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns random <typeparamref name="T"/> from <paramref name="input"/>
|
||||
/// Returns a random <typeparamref name="T"/> from the <paramref name="input"/>.
|
||||
/// </summary>
|
||||
/// <param name="input">The input enumerable to take the item from.</param>
|
||||
/// <typeparam name="T">The type of <paramref name="input"/> items.</typeparam>
|
||||
/// <returns>A random item from the <paramref name="input"/> enumerable.</returns>
|
||||
public static T? Random<T>(this IEnumerable<T> input)
|
||||
{
|
||||
var list = input as IList<T> ?? input.ToList();
|
||||
return list.Count == 0 ? default : list[UnityEngine.Random.Range(0, list.Count)];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a random <typeparamref name="T"/> from the <paramref name="input"/> using <paramref name="random"/>.
|
||||
/// </summary>
|
||||
/// <param name="input">The input enumerable to take the item from.</param>
|
||||
/// <param name="random">The <see cref="System.Random"/> to get the random number from.</param>
|
||||
/// <typeparam name="T">The type of <paramref name="input"/> items.</typeparam>
|
||||
/// <returns>A random item from the <paramref name="input"/> enumerable.</returns>
|
||||
public static T? Random<T>(this IEnumerable<T> input, Random random)
|
||||
{
|
||||
var list = input as IList<T> ?? input.ToList();
|
||||
return list.Count == 0 ? default : list[random.Next(0, list.Count)];
|
||||
}
|
||||
}
|
||||
@@ -14,17 +14,30 @@ using MethodInfo = System.Reflection.MethodInfo;
|
||||
|
||||
namespace Reactor.Utilities.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for reflection.
|
||||
/// </summary>
|
||||
public static class ReflectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a <see cref="Il2CppMethodInfo"/> for the specified <see cref="MethodInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="methodInfo">The <see cref="MethodInfo"/>.</param>
|
||||
/// <returns>A <see cref="Il2CppMethodInfo"/>.</returns>
|
||||
public static Il2CppMethodInfo ToIl2CppMethodInfo(this MethodInfo methodInfo)
|
||||
{
|
||||
var il2CppMethodField = Il2CppInteropUtils.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(methodInfo);
|
||||
if (il2CppMethodField == null) throw new ArgumentException($"'{methodInfo.Name}' is not an il2cpp method", nameof(methodInfo));
|
||||
var il2CppMethod = (IntPtr) il2CppMethodField.GetValue(null)!;
|
||||
|
||||
return new Il2CppSystem.Reflection.MethodInfo(IL2CPP.il2cpp_method_get_object(il2CppMethod, IntPtr.Zero));
|
||||
return new Il2CppMethodInfo(IL2CPP.il2cpp_method_get_object(il2CppMethod, IntPtr.Zero));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets enumerator's MoveNext type for the specified <see cref="Il2CppMethodInfo"/>.
|
||||
/// </summary>
|
||||
/// <param name="methodInfo">The enumerator <see cref="Il2CppMethodInfo"/>.</param>
|
||||
/// <returns>A <see cref="Il2CppSystemType"/> for enumerator's MoveNext type.</returns>
|
||||
public static Il2CppSystemType GetEnumeratorMoveNextType(this Il2CppMethodInfo methodInfo)
|
||||
{
|
||||
var customAttribute = Il2CppCustomAttributeExtensions.GetCustomAttribute(methodInfo, Il2CppType.Of<IteratorStateMachineAttribute>()).TryCast<IteratorStateMachineAttribute>();
|
||||
@@ -33,6 +46,11 @@ public static class ReflectionExtensions
|
||||
return customAttribute._StateMachineType_k__BackingField;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="Type"/> for the specified <see cref="Il2CppSystemType"/>.
|
||||
/// </summary>
|
||||
/// <param name="type">The <see cref="Il2CppSystemType"/>.</param>
|
||||
/// <returns>A <see cref="Type"/>.</returns>
|
||||
public static Type ToSystemType(this Il2CppSystemType type)
|
||||
{
|
||||
var result = Type.GetType(type.AssemblyQualifiedName);
|
||||
@@ -51,16 +69,37 @@ public static class ReflectionExtensions
|
||||
throw new TypeLoadException("Failed to find a system type for " + type.AssemblyQualifiedName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets MoveNext <see cref="MethodInfo"/> for specified enumerator method.
|
||||
/// </summary>
|
||||
/// <param name="type">The enclosing type of the method.</param>
|
||||
/// <param name="methodName">The name of the method.</param>
|
||||
/// <returns>A <see cref="MethodInfo"/>.</returns>
|
||||
public static MethodInfo EnumeratorMoveNext(Type type, string methodName)
|
||||
{
|
||||
return AccessTools.Method(AccessTools.Method(type, methodName).ToIl2CppMethodInfo().GetEnumeratorMoveNextType().ToSystemType(), "MoveNext");
|
||||
}
|
||||
|
||||
public static IEnumerable<MethodBase> GetMethods(this Type type, BindingFlags bindingAttr, Type returnType, params Type[] parameterTypes)
|
||||
/// <summary>
|
||||
/// Gets all method from the <paramref name="type"/> with the specified <paramref name="bindingFlags"/>, <paramref name="returnType"/> and <paramref name="parameterTypes"/>.
|
||||
/// </summary>
|
||||
/// <param name="type">The type to search the methods in.</param>
|
||||
/// <param name="bindingFlags">The <see cref="BindingFlags"/>.</param>
|
||||
/// <param name="returnType">The return type.</param>
|
||||
/// <param name="parameterTypes">The parameter types.</param>
|
||||
/// <returns>An array of <see cref="MethodInfo"/> objects representing all methods defined for the current <see cref="Type"/> that match the specified binding constraints.</returns>
|
||||
public static IEnumerable<MethodBase> GetMethods(this Type type, BindingFlags bindingFlags, Type returnType, params Type[] parameterTypes)
|
||||
{
|
||||
return type.GetMethods(bindingAttr).Where(x => x.ReturnType == returnType && x.GetParameters().Select(x => x.ParameterType).SequenceEqual(parameterTypes));
|
||||
return type.GetMethods(bindingFlags).Where(x => x.ReturnType == returnType && x.GetParameters().Select(x => x.ParameterType).SequenceEqual(parameterTypes));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all method from the <paramref name="type"/> with the specified <paramref name="returnType"/> and <paramref name="parameterTypes"/>.
|
||||
/// </summary>
|
||||
/// <param name="type">The type to search the methods in.</param>
|
||||
/// <param name="returnType">The return type.</param>
|
||||
/// <param name="parameterTypes">The parameter types.</param>
|
||||
/// <returns>An array of <see cref="MethodInfo"/> objects representing all methods defined for the current <see cref="Type"/> that match the specified binding constraints.</returns>
|
||||
public static IEnumerable<MethodBase> GetMethods(this Type type, Type returnType, params Type[] parameterTypes)
|
||||
{
|
||||
return type.GetMethods(AccessTools.all, returnType, parameterTypes);
|
||||
|
||||
+6
-2
@@ -3,13 +3,17 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Reactor.Utilities;
|
||||
namespace Reactor.Utilities.Extensions;
|
||||
|
||||
public static class SceneManagerUtilities
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="SceneManager"/>.
|
||||
/// </summary>
|
||||
public static class SceneManagerExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds a delegate to get one notification when a scene has loaded.
|
||||
/// </summary>
|
||||
/// <param name="value">The action.</param>
|
||||
[SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "This supplements existing methods like add_sceneLoaded and remove_sceneLoaded")]
|
||||
public static void once_sceneLoaded(Action<Scene, LoadSceneMode> value)
|
||||
{
|
||||
@@ -8,13 +8,20 @@ using Reactor.Utilities.Attributes;
|
||||
|
||||
namespace Reactor.Utilities.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="Stream"/>.
|
||||
/// </summary>
|
||||
public static class StreamExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a <see cref="Il2CppSystem.IO.Stream"/> which uses <see cref="System.IO.Stream"/> under the hood.
|
||||
/// </summary>
|
||||
[RegisterInIl2Cpp]
|
||||
public class StreamWrapper : Il2CppSystem.IO.Stream
|
||||
{
|
||||
private readonly Stream _stream;
|
||||
|
||||
/// <inheritdoc />
|
||||
public StreamWrapper(Stream stream) : base(ClassInjector.DerivedConstructorPointer<StreamWrapper>())
|
||||
{
|
||||
ClassInjector.DerivedConstructorBody(this);
|
||||
@@ -29,31 +36,37 @@ public static class StreamExtensions
|
||||
return new Span<byte>(rawBuffer + offset, count);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int Read(Il2CppStructArray<byte> buffer, int offset, int count)
|
||||
{
|
||||
return _stream.Read(GetSpan(buffer, offset, count));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Write(Il2CppStructArray<byte> buffer, int offset, int count)
|
||||
{
|
||||
_stream.Write(GetSpan(buffer, offset, count));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Flush()
|
||||
{
|
||||
_stream.Flush();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Close()
|
||||
{
|
||||
_stream.Close();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
{
|
||||
_stream.Dispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override long Seek(long offset, Il2CppSystem.IO.SeekOrigin origin)
|
||||
{
|
||||
return _stream.Seek(offset, origin switch
|
||||
@@ -65,16 +78,25 @@ public static class StreamExtensions
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
_stream.SetLength(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanRead => _stream.CanRead;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanSeek => _stream.CanSeek;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanWrite => _stream.CanWrite;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override long Length => _stream.Length;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override long Position
|
||||
{
|
||||
get => _stream.Position;
|
||||
@@ -83,13 +105,17 @@ public static class StreamExtensions
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wraps a System Stream into an Il2Cpp Stream
|
||||
/// Wraps a <see cref="System.IO.Stream"/> into a <see cref="Il2CppSystem.IO.Stream"/>.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to wrap.</param>
|
||||
/// <returns>A <see cref="StreamWrapper"/> for the specified <paramref name="stream"/>.</returns>
|
||||
public static StreamWrapper AsIl2Cpp(this Stream stream) => new(stream);
|
||||
|
||||
/// <summary>
|
||||
/// Fully read <paramref name="input"/> stream, can be used as workaround for il2cpp streams.
|
||||
/// Fully reads the <paramref name="input"/> stream.
|
||||
/// </summary>
|
||||
/// <param name="input">The stream to read.</param>
|
||||
/// <returns>A byte array read from the <see cref="Stream"/>.</returns>
|
||||
public static byte[] ReadFully(this Stream input)
|
||||
{
|
||||
using var ms = new MemoryStream();
|
||||
|
||||
@@ -2,6 +2,9 @@ using UnityEngine;
|
||||
|
||||
namespace Reactor.Utilities.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for unity.
|
||||
/// </summary>
|
||||
public static class UnityExtensions
|
||||
{
|
||||
/// <summary>
|
||||
@@ -9,7 +12,7 @@ public static class UnityExtensions
|
||||
/// </summary>
|
||||
/// <param name="color">The color to be converted.</param>
|
||||
/// <returns>Hexadecimal string representing the color.</returns>
|
||||
/// <remarks>https://docs.unity3d.com/ScriptReference/ColorUtility.ToHtmlStringRGBA.html</remarks>
|
||||
/// <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}";
|
||||
@@ -26,8 +29,10 @@ public static class UnityExtensions
|
||||
private static readonly int _addColor = Shader.PropertyToID("_AddColor");
|
||||
|
||||
/// <summary>
|
||||
/// Sets color outline for renderers using default Among Us shader
|
||||
/// Sets the outline for renderer using the default Among Us shader.
|
||||
/// </summary>
|
||||
/// <param name="renderer">The renderer to set the color on.</param>
|
||||
/// <param name="color">The color or null to disable the outline.</param>
|
||||
public static void SetOutline(this Renderer renderer, Color? color)
|
||||
{
|
||||
renderer.material.SetFloat(_outline, color.HasValue ? 1 : 0);
|
||||
|
||||
@@ -1,42 +1,67 @@
|
||||
using UnityEngine;
|
||||
using UnityObject = UnityEngine.Object;
|
||||
|
||||
namespace Reactor.Utilities.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for <see cref="UnityObject"/>.
|
||||
/// </summary>
|
||||
public static class UnityObjectExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Stops <paramref name="obj"/> from being destroyed
|
||||
/// Stops <paramref name="obj"/> from being destroyed.
|
||||
/// </summary>
|
||||
/// <param name="obj">Object to stop from being destroyed</param>
|
||||
/// <returns>Passed <paramref name="obj"/></returns>
|
||||
public static T DontDestroy<T>(this T obj) where T : Object
|
||||
/// <param name="obj">The object to stop from being destroyed.</param>
|
||||
/// <typeparam name="T">The type of the object.</typeparam>
|
||||
/// <returns>Passed <paramref name="obj"/>.</returns>
|
||||
public static T DontDestroy<T>(this T obj) where T : UnityObject
|
||||
{
|
||||
obj.hideFlags |= HideFlags.HideAndDontSave;
|
||||
|
||||
return obj.DontDestroyOnLoad();
|
||||
}
|
||||
|
||||
public static T DontUnload<T>(this T obj) where T : Object
|
||||
/// <summary>
|
||||
/// Stops <paramref name="obj"/> from being unloaded.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to stop from being unloaded.</param>
|
||||
/// <typeparam name="T">The type of the object.</typeparam>
|
||||
/// <returns>Passed <paramref name="obj"/>.</returns>
|
||||
public static T DontUnload<T>(this T obj) where T : UnityObject
|
||||
{
|
||||
obj.hideFlags |= HideFlags.DontUnloadUnusedAsset;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
public static T DontDestroyOnLoad<T>(this T obj) where T : Object
|
||||
/// <summary>
|
||||
/// Stops <paramref name="obj"/> from being destroyed on load.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to stop from being destroyed on load.</param>
|
||||
/// <typeparam name="T">The type of the object.</typeparam>
|
||||
/// <returns>Passed <paramref name="obj"/>.</returns>
|
||||
public static T DontDestroyOnLoad<T>(this T obj) where T : UnityObject
|
||||
{
|
||||
Object.DontDestroyOnLoad(obj);
|
||||
UnityObject.DontDestroyOnLoad(obj);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
public static void Destroy(this Object obj)
|
||||
/// <summary>
|
||||
/// Destroys the <paramref name="obj"/>.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to destroy.</param>
|
||||
public static void Destroy(this UnityObject obj)
|
||||
{
|
||||
Object.Destroy(obj);
|
||||
UnityObject.Destroy(obj);
|
||||
}
|
||||
|
||||
public static void DestroyImmediate(this Object obj)
|
||||
/// <summary>
|
||||
/// Destroys the <paramref name="obj"/> immediately.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to destroy immediately.</param>
|
||||
public static void DestroyImmediate(this UnityObject obj)
|
||||
{
|
||||
Object.DestroyImmediate(obj);
|
||||
UnityObject.DestroyImmediate(obj);
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,14 @@ using Il2CppSystem.Runtime.CompilerServices;
|
||||
|
||||
namespace Reactor.Utilities;
|
||||
|
||||
/// <inheritdoc />
|
||||
public sealed class Il2CppEqualityComparer<T> : IEqualityComparer<T> where T : Il2CppSystem.Object
|
||||
{
|
||||
private static Il2CppEqualityComparer<T>? _instance;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance.
|
||||
/// </summary>
|
||||
public static Il2CppEqualityComparer<T> Instance
|
||||
{
|
||||
get
|
||||
|
||||
@@ -4,20 +4,25 @@ using UnityEngine;
|
||||
|
||||
namespace Reactor.Utilities.ImGui;
|
||||
|
||||
/// <summary>
|
||||
/// Draggable version of <see cref="Window"/>.
|
||||
/// </summary>
|
||||
public class DragWindow : Window
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public DragWindow(Rect rect, string title, Action<int> func) : base(rect, title, func)
|
||||
{
|
||||
Func = id =>
|
||||
{
|
||||
func(id);
|
||||
|
||||
UnityEngine.GUI.DragWindow(new Rect(0, 0, 10000, 20));
|
||||
GUI.DragWindow(new Rect(0, 0, 10000, 20));
|
||||
Rect = Rect.ClampScreen();
|
||||
};
|
||||
}
|
||||
|
||||
public DragWindow(Rect rect, string title, Action func) : this(rect, title, id => func())
|
||||
/// <inheritdoc />
|
||||
public DragWindow(Rect rect, string title, Action func) : this(rect, title, _ => func())
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -4,22 +4,53 @@ using UnityEngine;
|
||||
|
||||
namespace Reactor.Utilities.ImGui;
|
||||
|
||||
/// <summary>
|
||||
/// Utility wrapper over <see cref="GUI"/>.<see cref="GUI.Window(int,UnityEngine.Rect,UnityEngine.GUI.WindowFunction,UnityEngine.GUIContent,UnityEngine.GUIStyle)"/>.
|
||||
/// </summary>
|
||||
public class Window
|
||||
{
|
||||
private static int _lastWindowId;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next window id.
|
||||
/// </summary>
|
||||
/// <returns>A window id.</returns>
|
||||
public static int NextWindowId()
|
||||
{
|
||||
return _lastWindowId++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the id of the window.
|
||||
/// </summary>
|
||||
public int Id { get; set; } = NextWindowId();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the window is enabled and shown.
|
||||
/// </summary>
|
||||
public bool Enabled { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the rect of the window.
|
||||
/// </summary>
|
||||
public Rect Rect { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the render function of the window.
|
||||
/// </summary>
|
||||
public Action<int> Func { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the title of the window.
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Window"/> class.
|
||||
/// </summary>
|
||||
/// <param name="rect">The rect.</param>
|
||||
/// <param name="title">The title.</param>
|
||||
/// <param name="func">The render function.</param>
|
||||
public Window(Rect rect, string title, Action<int> func)
|
||||
{
|
||||
Rect = rect;
|
||||
@@ -27,11 +58,15 @@ public class Window
|
||||
Func = func;
|
||||
}
|
||||
|
||||
public Window(Rect rect, string title, Action func) : this(rect, title, id => func())
|
||||
/// <inheritdoc />
|
||||
public Window(Rect rect, string title, Action func) : this(rect, title, _ => func())
|
||||
{
|
||||
}
|
||||
|
||||
public void OnGUI()
|
||||
/// <summary>
|
||||
/// Draws the window gui.
|
||||
/// </summary>
|
||||
public virtual void OnGUI()
|
||||
{
|
||||
if (Enabled)
|
||||
{
|
||||
|
||||
@@ -5,8 +5,15 @@ using BepInEx.Unity.IL2CPP;
|
||||
|
||||
namespace Reactor.Utilities;
|
||||
|
||||
/// <summary>
|
||||
/// Provides singleton access to plugins logger.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the plugin.</typeparam>
|
||||
public static class Logger<T> where T : BasePlugin
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the instance.
|
||||
/// </summary>
|
||||
public static ManualLogSource Instance => PluginSingleton<T>.Instance.Log;
|
||||
|
||||
/// <inheritdoc cref="ManualLogSource.Log(BepInEx.Logging.LogLevel,object)"/>
|
||||
|
||||
@@ -5,10 +5,17 @@ using BepInEx.Unity.IL2CPP;
|
||||
|
||||
namespace Reactor.Utilities;
|
||||
|
||||
/// <summary>
|
||||
/// Provides singleton access to plugins instance.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the plugin.</typeparam>
|
||||
public static class PluginSingleton<T> where T : BasePlugin
|
||||
{
|
||||
private static T? _instance;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an instance of <typeparamref name="T"/> plugin.
|
||||
/// </summary>
|
||||
public static T Instance
|
||||
{
|
||||
get => _instance ??= IL2CPPChainloader.Instance.Plugins.Values.Select(x => x.Instance).OfType<T>().Single();
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
},
|
||||
"BepInEx.AutoPlugin": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.0.1, )",
|
||||
"resolved": "1.0.1",
|
||||
"contentHash": "G4YpTHG9rtkDJof+ld7Er4E+sCOlgsjG4bJ1itx1ub7dgff6qqHLy9ksX1ehgO0AdBOPVyVl/GKJg9o7iNl8HA=="
|
||||
"requested": "[1.1.0, )",
|
||||
"resolved": "1.1.0",
|
||||
"contentHash": "/7Nmc1EA9lYlGBiyz6qPAaHvlcJHEjFZ1RFpGX+8EBj3GrgoX5kbpnZusuSHoOlLx0l+CnzKuQNE54B8UaGPyw=="
|
||||
},
|
||||
"BepInEx.IL2CPP.MSBuild": {
|
||||
"type": "Direct",
|
||||
@@ -47,6 +47,15 @@
|
||||
"Microsoft.SourceLink.Common": "1.1.1"
|
||||
}
|
||||
},
|
||||
"StyleCop.Analyzers": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.2.0-beta.435, )",
|
||||
"resolved": "1.2.0-beta.435",
|
||||
"contentHash": "TADk7vdGXtfTnYCV7GyleaaRTQjfoSfZXprQrVMm7cSJtJbFc1QIbWPyLvrgrfGdfHbGmUPvaN4ODKNxg2jgPQ==",
|
||||
"dependencies": {
|
||||
"StyleCop.Analyzers.Unstable": "1.2.0.435"
|
||||
}
|
||||
},
|
||||
"AssetRipper.VersionUtilities": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.2.1",
|
||||
@@ -323,6 +332,11 @@
|
||||
"resolved": "2.0.2",
|
||||
"contentHash": "4EQgYdNZ92SyaO7YFk6olVnebF5V+jrHyMUjvPq89tLeMo8NSfgDF+6Zwq/lgh9j/0yfQp9Lkm0ZA0rUATCZFA=="
|
||||
},
|
||||
"StyleCop.Analyzers.Unstable": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.2.0.435",
|
||||
"contentHash": "ouwPWZxbOV3SmCZxIRqHvljkSzkCyi1tDoMzQtDb/bRP8ctASV/iRJr+A2Gdj0QLaLmWnqTWDrH82/iP+X80Lg=="
|
||||
},
|
||||
"System.Collections": {
|
||||
"type": "Transitive",
|
||||
"resolved": "4.3.0",
|
||||
|
||||
Reference in new issue
Block a user