Skip to content

Commit

Permalink
0.2.0-dev.10
Browse files Browse the repository at this point in the history
CustomOption RPC now uses SHA-1 hashes to match remote objects.
Added documentation to Extensions.TryCastTo<T>.
Patched NumberOption.FixedUpdate and StringOption.FixedUpdate to address values not being presented correctly.
(2021.3.31.3s) Adjusted CustomNumberOption.ModifierStringFormat and CustomNumberOption.SecondsStringFormat for differing mono behaviour.
  • Loading branch information
DorCoMaNdO committed Apr 5, 2021
1 parent 1959b7c commit 31b60d1
Show file tree
Hide file tree
Showing 10 changed files with 92 additions and 23 deletions.
2 changes: 1 addition & 1 deletion Essentials/Essentials.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<LangVersion>latest</LangVersion>
<Version>0.2.0-dev.9</Version>
<Version>0.2.0-dev.10</Version>

<Description>Among Us modding essentials</Description>
<Authors>CoMaNdO</Authors>
Expand Down
2 changes: 1 addition & 1 deletion Essentials/EssentialsPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Essentials
[BepInPlugin(Id)]
[BepInProcess("Among Us.exe")]
[BepInDependency(ReactorPlugin.Id, BepInDependency.DependencyFlags.HardDependency)]
[ReactorPluginSide(PluginSide.ClientOnly)]
[ReactorPluginSide(PluginSide.Both)]
public partial class EssentialsPlugin : BasePlugin
{
public const string Id = "com.comando.essentials";
Expand Down
7 changes: 7 additions & 0 deletions Essentials/Extensions/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ public static bool CompareName(this OptionBehaviour a, OptionBehaviour b)
return CompareName(a?.gameObject, b?.gameObject);
}

/// <summary>
/// Attempt to cast IL2CPP object <paramref name="obj"/> to type <typeparamref name="T"/>
/// </summary>
/// <typeparam name="T">Type to cast to</typeparam>
/// <param name="obj">IL2CPP object to cast</param>
/// <param name="cast"><typeparamref name="T"/>-casted object</param>
/// <returns>Successful cast</returns>
public static bool TryCastTo<T>(this Il2CppObjectBase obj, out T cast) where T : Il2CppObjectBase
{
cast = obj.TryCast<T>();
Expand Down
13 changes: 10 additions & 3 deletions Essentials/Helpers/PluginHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,19 @@ public static class PluginHelpers
/// <returns>A plugin id or <see cref="string.Empty"/></returns>
public static string GetCallingPluginId(int frameIndex = 3)
{
//string pluginId = string.Empty;

StackTrace stackTrace = new StackTrace(frameIndex);
for (int i = 0; i < stackTrace.GetFrames().Length; i++)
{
MethodBase method = stackTrace.GetFrame(i).GetMethod();
Type type = method.ReflectedType;

//EssentialsPlugin.Logger.LogInfo($"Frame {i}: {method.Name}, type: {type}, IsClass: {type.IsClass}, IsPlugin: {type.IsClass && type.IsSubclassOf(typeof(BasePlugin))}");
//EssentialsPlugin.Logger.LogInfo($"Frame {frameIndex + i}: {method.Name}, type: {type}, IsClass: {type.IsClass}, IsPlugin: {type.IsClass && type.IsSubclassOf(typeof(BasePlugin))}");

if (!type.IsClass || !type.IsSubclassOf(typeof(BasePlugin)) || type.IsAbstract) continue;

if (!type.IsClass || !type.IsSubclassOf(typeof(BasePlugin))) continue;
//EssentialsPlugin.Logger.LogInfo($"Frame {frameIndex + i}: {method.Name}, type: {type}");

//EssentialsPlugin.Logger.LogInfo($"Match: {IL2CPPChainloader.Instance?.Plugins?.Values?.Where(x => x?.Instance?.GetType() == type)?.SingleOrDefault()}");
//return IL2CPPChainloader.Instance.Plugins.Values.SingleOrDefault(x => x.Instance.GetType() == type)?.Metadata?.GUID ?? string.Empty;
Expand All @@ -37,11 +41,14 @@ public static string GetCallingPluginId(int frameIndex = 3)
CustomAttributeTypedArgument arg = attribute.ConstructorArguments.FirstOrDefault();
if (arg == null || arg.ArgumentType != typeof(string) || arg.Value is not string value) continue;

//EssentialsPlugin.Logger.LogInfo($"pluginId: {value}");

//pluginId = value;
return value;
}
}

return string.Empty;
return string.Empty;//pluginId;
}
}
}
19 changes: 19 additions & 0 deletions Essentials/Helpers/SHA1Helper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using System.Security.Cryptography;
using System.Text;

namespace Essentials.Helpers
{
internal static class SHA1Helper
{
public const int Length = 16;

public static byte[] Create(string name)
{
byte[] buffer = new byte[Length];
using (SHA1 algorithm = SHA1.Create()) Array.Copy(algorithm.ComputeHash(Encoding.UTF8.GetBytes(name)), 0, buffer, 0, Length);

return buffer;
}
}
}
8 changes: 8 additions & 0 deletions Essentials/Options/CustomOption.Number.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,20 @@ public class CustomNumberOption : CustomOption, INumberOption
/// <summary>
/// A "modifier" string format, simply appending 'x' after the value.
/// </summary>
#if S20201209 || S20210305
public static Func<CustomOption, object, string> ModifierStringFormat { get; } = (sender, value) => $"{value:0.0}x";
#else
public static Func<CustomOption, object, string> ModifierStringFormat { get; } = (sender, value) => $"{value}x";
#endif

/// <summary>
/// A "seconds" string format, simply appending 's' after the value.
/// </summary>
#if S20201209 || S20210305
public static Func<CustomOption, object, string> SecondsStringFormat { get; } = (sender, value) => $"{value:0.0}s";
#else
public static Func<CustomOption, object, string> SecondsStringFormat { get; } = (sender, value) => $"{value}s";
#endif

//public new float Value { get { return (float)base.Value; } protected set { base.Value = value; } }

Expand Down
23 changes: 23 additions & 0 deletions Essentials/Options/CustomOption.Patches.cs
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,29 @@ private static bool Prefix(StringOption __instance)
}
}

private static bool OnFixedUpdate(OptionBehaviour opt)
{
return !Options.Any(option => option.GameSetting == opt);
}

[HarmonyPatch(typeof(NumberOption), nameof(NumberOption.FixedUpdate))]
private static class NumberOptionFixedUpdatePatch
{
private static bool Prefix(NumberOption __instance)
{
return OnFixedUpdate(__instance);
}
}

[HarmonyPatch(typeof(StringOption), nameof(StringOption.FixedUpdate))]
private static class StringOptionFixedUpdatePatch
{
private static bool Prefix(StringOption __instance)
{
return OnFixedUpdate(__instance);
}
}

[HarmonyPatch(typeof(ToggleOption), nameof(ToggleOption.Toggle))]
private class ToggleButtonPatch
{
Expand Down
31 changes: 16 additions & 15 deletions Essentials/Options/CustomOption.Rpc.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Hazel;
using Essentials.Helpers;
using Hazel;
using Reactor;
#if !S20201209 && !S20210305
using Reactor.Networking;
Expand Down Expand Up @@ -41,7 +42,7 @@ internal static void HandleRpc(PlayerControl sender, (string, CustomOptionType,
#else
[RegisterCustomRpc(0)]
#endif
private protected class Rpc : PlayerCustomRpc<EssentialsPlugin, (string, CustomOptionType, object)>
private protected class Rpc : PlayerCustomRpc<EssentialsPlugin, (byte[], CustomOptionType, object)>
{
public static Rpc Instance { get { return Rpc<Rpc>.Instance; } }

Expand All @@ -55,38 +56,38 @@ public Rpc(EssentialsPlugin plugin, uint id) : base(plugin, id)

public override RpcLocalHandling LocalHandling { get { return RpcLocalHandling.None; } }

public override void Write(MessageWriter writer, (string, CustomOptionType, object) option)
public override void Write(MessageWriter writer, (byte[], CustomOptionType, object) option)
{
writer.Write(option.Item1); // ID
writer.Write((int)option.Item2); // Type
writer.Write(option.Item1); // SHA1
writer.Write((byte)option.Item2); // Type
if (option.Item2 == CustomOptionType.Toggle) writer.Write((bool)option.Item3);
else if (option.Item2 == CustomOptionType.Number) writer.Write((float)option.Item3);
else if (option.Item2 == CustomOptionType.String) writer.Write((int)option.Item3);
}

public override (string, CustomOptionType, object) Read(MessageReader reader)
public override (byte[], CustomOptionType, object) Read(MessageReader reader)
{
string id = reader.ReadString();
CustomOptionType type = (CustomOptionType)reader.ReadInt32();
byte[] sha1 = reader.ReadBytes(SHA1Helper.Length);
CustomOptionType type = (CustomOptionType)reader.ReadByte();
object value = null;
if (type == CustomOptionType.Toggle) value = reader.ReadBoolean();
else if (type == CustomOptionType.Number) value = reader.ReadSingle();
else if (type == CustomOptionType.String) value = reader.ReadInt32();

return (id, type, value);
return (sha1, type, value);
}

public override void Handle(PlayerControl sender, (string, CustomOptionType, object) option)
public override void Handle(PlayerControl sender, (byte[], CustomOptionType, object) option)
{
if (sender?.Data == null) return;

string id = option.Item1;
byte[] sha1 = option.Item1;
CustomOptionType type = option.Item2;
CustomOption customOption = Options.FirstOrDefault(o => o.Type == type && o.ID.Equals(id, StringComparison.Ordinal));
CustomOption customOption = Options.FirstOrDefault(o => o.Type == type && o.SHA1.SequenceEqual(sha1));

if (customOption == null)
{
EssentialsPlugin.Logger.LogWarning($"Received option that could not be found, id: \"{id}\", type: {type}.");
EssentialsPlugin.Logger.LogWarning($"Received option that could not be found, sha1: \"{string.Join("", sha1.Select(b => $"{b:X2}"))}\", type: {type}.");

return;
}
Expand All @@ -101,9 +102,9 @@ public override void Handle(PlayerControl sender, (string, CustomOptionType, obj
}
}

public static implicit operator (string ID, CustomOptionType Type, object Value)(CustomOption option)
public static implicit operator (byte[] SHA1, CustomOptionType Type, object Value)(CustomOption option)
{
return (option.ID, option.Type, option.GetValue<object>());
return (option.SHA1, option.Type, option.GetValue<object>());
}

/*public static implicit operator (int ID, CustomOptionType Type, object Value)(CustomOption option)
Expand Down
8 changes: 6 additions & 2 deletions Essentials/Options/CustomOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
namespace Essentials.Options
{
//[Serializable]
public enum CustomOptionType
public enum CustomOptionType : byte
{
/// <summary>
/// A checkmark toggle option.
Expand Down Expand Up @@ -43,7 +43,7 @@ public partial class CustomOption
/// <summary>
/// Enables debug logging messages.
/// </summary>
public static bool Debug { get; set; } = true;
public static bool Debug { get; set; } = false;

/// <summary>
/// The size of HUD (lobby options) text, game default is 0.65F, Essentials default is 0.5F.
Expand Down Expand Up @@ -106,6 +106,8 @@ public partial class CustomOption
/// </summary>
protected virtual object Value { get; set; }

protected readonly byte[] SHA1;

/// <summary>
/// An event raised before a value change occurs, can alter the final value or cancel the value change. Only raised for the lobby host.
/// See <see cref="OptionOnValueChangedEventArgs"/> and childs <seealso cref="ToggleOptionOnValueChangedEventArgs"/>, <seealso cref="NumberOptionOnValueChangedEventArgs"/> and <seealso cref="StringOptionOnValueChangedEventArgs"/>.
Expand Down Expand Up @@ -190,6 +192,8 @@ protected CustomOption(string id, string name, bool saveValue, CustomOptionType
ConfigID = $"{id}_{i}";
}

SHA1 = SHA1Helper.Create(ID);

Options.Add(this);
}

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ This guide assumes Reactor.OxygenFilter.MSBuild is being used.
Newer versions of Essentials use configurations based on Among Us target version(s) and override the `AmongUs` environment variable as a result.
Essentials depends on Reactor, follow installation steps 1 and 2 before proceeding.
1. Add an environment variable for your targeted Among Us version(s), the environment variable needs to be prefixed with `AmongUs_` and then be followed by the client version, with dashes substituting dots, for example: `AmongUs_2020-12-9s` for version 2020.12.9s.
2. Select the configuration for the targeted Among Us version (in Visual Studio, if building for more than one version, you can use Build -> Batch Build... from the toolbar and select all the target versions and then `Build`).
2. Select the configuration for the targeted Among Us version (in Visual Studio, if building for more than one version, you can use `Build` -> `Batch Build...` from the toolbar and select all the target versions and then `Build`).
3. The compiled binary will be copied to the `plugins` folder of your targeted Among Us version(s), as well as a `bin` folder in the solution's folder.

0 comments on commit 31b60d1

Please sign in to comment.