Decompiled source of FrostVale ServerPack 3 v1.0.1

plugins/FrostValeCompat/FrostValeCompat.dll

Decompiled 2 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace FrostValeCompat;

[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInPlugin("frostvale.compat", "FrostVale Compat", "1.0.1")]
public sealed class RiceWaterLevelPlugin : BaseUnityPlugin
{
	public const string PluginGuid = "frostvale.compat";

	public const string PluginName = "FrostVale Compat";

	public const string PluginVersion = "1.0.1";

	internal static ConfigEntry<float> MinGroundOffset;

	internal static ConfigEntry<float> MaxGroundOffset;

	internal static ConfigEntry<bool> BlockRiceAwayFromWater;

	internal static RiceWaterLevelPlugin Instance;

	internal static ManualLogSource Log;

	private void Awake()
	{
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		BlockRiceAwayFromWater = ((BaseUnityPlugin)this).Config.Bind<bool>("RtDOcean Rice", "Block rice away from water level", true, "If enabled, RtDOcean rice saplings can only be placed near the configured water-level band.");
		MinGroundOffset = ((BaseUnityPlugin)this).Config.Bind<float>("RtDOcean Rice", "Minimum ground offset from water", -0.35f, "Lowest allowed terrain height relative to water level, in meters.");
		MaxGroundOffset = ((BaseUnityPlugin)this).Config.Bind<float>("RtDOcean Rice", "Maximum ground offset from water", 0.15f, "Highest allowed terrain height relative to water level, in meters.");
		Harmony.CreateAndPatchAll(typeof(RiceWaterLevelPlugin).Assembly, "frostvale.compat");
		DiscordNameDisplayPatches.Patch();
		((BaseUnityPlugin)this).Logger.LogInfo((object)"FrostVale rice water-level placement patch loaded.");
	}
}
[HarmonyPatch(typeof(Player), "TryPlacePiece")]
internal static class RicePlacementPatch
{
	private static bool Prefix(Piece piece, ref bool __result)
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		if (!RiceWaterLevelPlugin.BlockRiceAwayFromWater.Value || !RiceWaterLevelRules.IsRiceSapling(piece))
		{
			return true;
		}
		Vector3 position = ((Component)piece).transform.position;
		if (RiceWaterLevelRules.IsWithinWaterBand(position, out var groundHeight, out var waterLevel))
		{
			return true;
		}
		__result = false;
		ShowMessage($"Rice must be planted at water level. Ground {groundHeight - waterLevel:0.00}m from water.");
		return false;
	}

	private static void ShowMessage(string message)
	{
		try
		{
			if ((Object)(object)MessageHud.instance != (Object)null)
			{
				MessageHud.instance.ShowMessage((MessageType)2, message, 0, (Sprite)null, false);
			}
		}
		catch (Exception ex)
		{
			RiceWaterLevelPlugin.Log.LogDebug((object)("Could not show rice placement message: " + ex.Message));
		}
	}
}
[HarmonyPatch(typeof(Plant), "GetStatus")]
internal static class RicePlantStatusPatch
{
	private static void Postfix(Plant __instance, ref Status __result)
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		if (RiceWaterLevelPlugin.BlockRiceAwayFromWater.Value && (int)__result == 0 && RiceWaterLevelRules.IsRicePlant(__instance) && !RiceWaterLevelRules.IsWithinWaterBand(((Component)__instance).transform.position, out var _, out var _))
		{
			__result = (Status)3;
		}
	}
}
internal static class RiceWaterLevelRules
{
	public static bool IsRiceSapling(Piece piece)
	{
		if ((Object)(object)piece == (Object)null)
		{
			return false;
		}
		if (!LooksLikeRice(((Object)piece).name) && !LooksLikeRice(((Object)((Component)piece).gameObject).name))
		{
			return LooksLikeRice(piece.m_name);
		}
		return true;
	}

	public static bool IsRicePlant(Plant plant)
	{
		if ((Object)(object)plant == (Object)null)
		{
			return false;
		}
		if (!LooksLikeRice(((Object)plant).name) && !LooksLikeRice(((Object)((Component)plant).gameObject).name))
		{
			return LooksLikeRice(plant.m_name);
		}
		return true;
	}

	public static bool IsWithinWaterBand(Vector3 position, out float groundHeight, out float waterLevel)
	{
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		ZoneSystem instance = ZoneSystem.instance;
		waterLevel = (((Object)(object)instance != (Object)null) ? instance.m_waterLevel : 30f);
		groundHeight = position.y;
		float num = default(float);
		if ((Object)(object)instance != (Object)null && instance.GetGroundHeight(position, ref num))
		{
			groundHeight = num;
		}
		float num2 = groundHeight - waterLevel;
		if (num2 >= RiceWaterLevelPlugin.MinGroundOffset.Value)
		{
			return num2 <= RiceWaterLevelPlugin.MaxGroundOffset.Value;
		}
		return false;
	}

	private static bool LooksLikeRice(string value)
	{
		if (!string.IsNullOrEmpty(value) && value.IndexOf("Rice", StringComparison.OrdinalIgnoreCase) >= 0)
		{
			return value.IndexOf("_RtD", StringComparison.OrdinalIgnoreCase) >= 0;
		}
		return false;
	}
}
internal static class DiscordNameDisplayPatches
{
	private static readonly Dictionary<string, string> NamesById = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

	private static readonly Regex NumericIdPattern = new Regex("\\b\\d{15,20}\\b", RegexOptions.Compiled);

	public static void Patch()
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Expected O, but got Unknown
		try
		{
			Harmony harmony = new Harmony("frostvale.compat.discord-names");
			PatchPrefix(harmony, "FiresDiscordIntegration.Discord.DiscordIntegrationCore", "OnPlayerJoin", typeof(DiscordPlayerNameTracker).GetMethod("PlayerNameFirstPrefix", BindingFlags.Static | BindingFlags.NonPublic));
			PatchPrefix(harmony, "FiresDiscordIntegration.Discord.DiscordIntegrationCore", "OnPlayerLeave", typeof(DiscordPlayerNameTracker).GetMethod("PlayerNameFirstPrefix", BindingFlags.Static | BindingFlags.NonPublic));
			PatchPrefix(harmony, "FiresDiscordIntegration.Discord.DiscordIntegrationCore", "OnClientLoginArtifacts", typeof(DiscordPlayerNameTracker).GetMethod("PlayerNameFirstPrefix", BindingFlags.Static | BindingFlags.NonPublic));
			PatchPrefix(harmony, "FiresDiscordIntegration.Discord.DiscordIntegrationCore", "SendClientLogOnRequest", typeof(DiscordPlayerNameTracker).GetMethod("PlayerNameFirstPrefix", BindingFlags.Static | BindingFlags.NonPublic));
			PatchPrefix(harmony, "FiresDiscordIntegration.ClientLogRelay.ClientLogRelayIntegration", "PostDisconnectLog", typeof(DiscordPlayerNameTracker).GetMethod("IdFirstPrefix", BindingFlags.Static | BindingFlags.NonPublic));
			PatchPrefix(harmony, "FiresDiscordIntegration.ClientLogRelay.ClientLogRelayIntegration", "PostDisconnectLogAsync", typeof(DiscordPlayerNameTracker).GetMethod("IdFirstPrefix", BindingFlags.Static | BindingFlags.NonPublic));
			PatchPrefix(harmony, "FiresDiscordIntegration.Discord.DiscordStatusTracker", "RecordJoin", typeof(DiscordPlayerNameTracker).GetMethod("PlayerNameFirstPrefix", BindingFlags.Static | BindingFlags.NonPublic));
			PatchPrefix(harmony, "FiresDiscordIntegration.Discord.DiscordStatusTracker", "RecordLeave", typeof(DiscordPlayerNameTracker).GetMethod("PlayerNameFirstPrefix", BindingFlags.Static | BindingFlags.NonPublic));
			PatchPrefix(harmony, "FiresDiscordIntegration.Discord.DiscordWebhookService", "SendMessage", typeof(DiscordVisibleTextRewriter).GetMethod("Prefix", BindingFlags.Static | BindingFlags.NonPublic));
			PatchPrefix(harmony, "FiresDiscordIntegration.Discord.DiscordWebhookService", "SendEmbed", typeof(DiscordVisibleTextRewriter).GetMethod("Prefix", BindingFlags.Static | BindingFlags.NonPublic));
			PatchPrefix(harmony, "FiresDiscordIntegration.Discord.DiscordWebhookService", "SendEmbedWithFiles", typeof(DiscordVisibleTextRewriter).GetMethod("Prefix", BindingFlags.Static | BindingFlags.NonPublic));
			PatchPrefix(harmony, "FiresDiscordIntegration.Discord.DiscordWebhookService", "EnqueuePost", typeof(DiscordVisibleTextRewriter).GetMethod("Prefix", BindingFlags.Static | BindingFlags.NonPublic));
			RiceWaterLevelPlugin.Log.LogInfo((object)"FrostVale Discord name-display patch loaded.");
		}
		catch (Exception ex)
		{
			RiceWaterLevelPlugin.Log.LogWarning((object)("FrostVale Discord name-display patch skipped: " + ex.Message));
		}
	}

	private static void PatchPrefix(Harmony harmony, string typeName, string methodName, MethodInfo prefix)
	{
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Expected O, but got Unknown
		if (prefix == null)
		{
			return;
		}
		Type type = AccessTools.TypeByName(typeName);
		if (type == null)
		{
			return;
		}
		foreach (MethodInfo declaredMethod in AccessTools.GetDeclaredMethods(type))
		{
			if (declaredMethod.Name == methodName)
			{
				harmony.Patch((MethodBase)declaredMethod, new HarmonyMethod(prefix), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
		}
	}

	internal static void Remember(string platformId, string playerName)
	{
		if (!string.IsNullOrWhiteSpace(platformId) && !string.IsNullOrWhiteSpace(playerName) && !IsRawId(playerName))
		{
			NamesById[platformId.Trim()] = playerName.Trim();
		}
	}

	internal static string RewriteText(string value)
	{
		if (string.IsNullOrEmpty(value) || NamesById.Count == 0)
		{
			return value;
		}
		string value2;
		return NumericIdPattern.Replace(value, (Match match) => (!NamesById.TryGetValue(match.Value, out value2)) ? match.Value : value2);
	}

	internal static bool IsRawId(string value)
	{
		if (!string.IsNullOrWhiteSpace(value))
		{
			return NumericIdPattern.IsMatch(value.Trim());
		}
		return false;
	}
}
internal static class DiscordPlayerNameTracker
{
	private static void PlayerNameFirstPrefix(object[] __args)
	{
		if (__args != null && __args.Length >= 2)
		{
			DiscordNameDisplayPatches.Remember(__args[1] as string, __args[0] as string);
		}
	}

	private static void IdFirstPrefix(object[] __args)
	{
		if (__args != null && __args.Length >= 2)
		{
			DiscordNameDisplayPatches.Remember(__args[0] as string, __args[1] as string);
		}
	}
}
internal static class DiscordVisibleTextRewriter
{
	private static void Prefix(object[] __args)
	{
		if (__args == null)
		{
			return;
		}
		for (int i = 0; i < __args.Length; i++)
		{
			object obj = __args[i];
			if (obj is string value)
			{
				__args[i] = DiscordNameDisplayPatches.RewriteText(value);
			}
			else
			{
				RewriteObject(obj, new HashSet<object>(ReferenceEqualityComparer.Instance));
			}
		}
	}

	private static void RewriteObject(object value, HashSet<object> visited)
	{
		if (value == null || value is string || value.GetType().IsPrimitive || !visited.Add(value))
		{
			return;
		}
		if (value is IDictionary dictionary)
		{
			List<object> list = new List<object>();
			foreach (object key in dictionary.Keys)
			{
				list.Add(key);
			}
			{
				foreach (object item in list)
				{
					object obj = dictionary[item];
					string text = obj as string;
					dictionary[item] = ((text != null) ? DiscordNameDisplayPatches.RewriteText(text) : RewriteAndReturn(obj, visited));
				}
				return;
			}
		}
		if (value is IList list2)
		{
			for (int i = 0; i < list2.Count; i++)
			{
				object obj2 = list2[i];
				string text2 = obj2 as string;
				list2[i] = ((text2 != null) ? DiscordNameDisplayPatches.RewriteText(text2) : RewriteAndReturn(obj2, visited));
			}
			return;
		}
		FieldInfo[] fields = value.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		foreach (FieldInfo fieldInfo in fields)
		{
			if (fieldInfo.FieldType == typeof(string))
			{
				string value2 = fieldInfo.GetValue(value) as string;
				fieldInfo.SetValue(value, DiscordNameDisplayPatches.RewriteText(value2));
			}
			else if (!fieldInfo.FieldType.IsValueType)
			{
				RewriteObject(fieldInfo.GetValue(value), visited);
			}
		}
	}

	private static object RewriteAndReturn(object value, HashSet<object> visited)
	{
		RewriteObject(value, visited);
		return value;
	}
}
internal sealed class ReferenceEqualityComparer : IEqualityComparer<object>
{
	public static readonly ReferenceEqualityComparer Instance = new ReferenceEqualityComparer();

	private ReferenceEqualityComparer()
	{
	}

	public new bool Equals(object x, object y)
	{
		return object.ReferenceEquals(x, y);
	}

	public int GetHashCode(object obj)
	{
		return RuntimeHelpers.GetHashCode(obj);
	}
}