Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of FehuRoads v420.0.7
plugins/FehuRoads/FehuRoads.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using Forteca.FehuRoads.Commands; using Forteca.FehuRoads.Core; using Forteca.FehuRoads.Detection; using Forteca.FehuRoads.Integration; using Forteca.FehuRoads.Networking; using Forteca.FehuRoads.Plugin; using Forteca.FehuRoads.Rendering; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyTitle("FehuRoads")] [assembly: AssemblyFileVersion("0.1.7.0")] [assembly: AssemblyVersion("0.1.7.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Forteca.FehuRoads.Rendering { internal static class FcwTacticalMapBridge { internal static void BeforeClanMapDrawings(Rect __0, Vector2 __1, float __2, float __3) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) FehuRoadsPlugin instance = FehuRoadsPlugin.Instance; if ((Object)(object)instance != (Object)null && (Object)(object)instance.Overlay != (Object)null) { instance.Overlay.DrawClanMapLayer(__0, __1, __2, __3); } } } internal sealed class RoadMapOverlay : MonoBehaviour { private const float ClanMapWorldRadius = 10500f; private readonly List<KeyValuePair<long, RoadType>> _visible = new List<KeyValuePair<long, RoadType>>(); private readonly List<KeyValuePair<long, RoadType>> _clanMapRoads = new List<KeyValuePair<long, RoadType>>(); private Rect _lastUv; private int _lastMode = -1; private int _lastClanMapCount = -1; private int _clanMapLastFrame = -100; private int _lastVanillaDirt = -1; private int _lastVanillaPaved = -1; private int _lastVanillaRendered = -1; private int _lastClanDirt = -1; private int _lastClanPaved = -1; private int _lastClanRendered = -1; private bool _dirty = true; private bool _clanMapDirty = true; private bool _clanMapOpen; private static readonly FieldInfo Explored = typeof(Minimap).GetField("m_explored", BindingFlags.Instance | BindingFlags.NonPublic); private void Awake() { FehuRoadsPlugin instance = FehuRoadsPlugin.Instance; if (!((Object)(object)instance == (Object)null)) { instance.RoadDataChanged += delegate { Invalidate(); }; instance.Roads.ChunkChanged += delegate { Invalidate(); }; instance.LogInfo("[FehuRoads] Minimap hook installed"); instance.LogInfo("[FehuRoads] Large map overlay initialized"); } } internal void Invalidate() { _dirty = true; _clanMapDirty = true; } private void Update() { if (_clanMapOpen && Time.frameCount - _clanMapLastFrame > 2) { _clanMapOpen = false; _lastClanMapCount = -1; } } private void Rebuild(Minimap minimap, RawImage image) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between I4 and Unknown //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected I4, but got Unknown //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) Rect uvRect = image.uvRect; if (_dirty || _lastMode != (int)minimap.m_mode || !(uvRect == _lastUv)) { _lastMode = (int)minimap.m_mode; _lastUv = uvRect; _dirty = false; float num = (float)minimap.m_textureSize * minimap.m_pixelSize; FehuRoadsPlugin.Instance.Roads.Query(Mathf.FloorToInt((((Rect)(ref uvRect)).xMin - 0.5f) * num), Mathf.FloorToInt((((Rect)(ref uvRect)).yMin - 0.5f) * num), Mathf.CeilToInt((((Rect)(ref uvRect)).xMax - 0.5f) * num), Mathf.CeilToInt((((Rect)(ref uvRect)).yMax - 0.5f) * num), _visible); } } private void OnGUI() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Invalid comparison between Unknown and I4 //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Invalid comparison between Unknown and I4 //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Invalid comparison between Unknown and I4 FehuRoadsPlugin instance = FehuRoadsPlugin.Instance; Minimap instance2 = Minimap.instance; if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance2 == (Object)null) && (int)Event.current.type == 7) { bool flag = (int)instance2.m_mode == 2; bool flag2 = (int)instance2.m_mode == 1; if ((!flag || instance.LargeMap.Value) && (!flag2 || instance.MiniMap.Value) && (flag || flag2)) { DrawVanillaMap(instance2, flag ? instance2.m_mapImageLarge : instance2.m_mapImageSmall); } } } private void DrawVanillaMap(Minimap minimap, RawImage image) { //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_02a5: Unknown result type (might be due to invalid IL or missing references) //IL_0309: Unknown result type (might be due to invalid IL or missing references) //IL_030f: Invalid comparison between Unknown and I4 //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)image == (Object)null) { return; } Rebuild(minimap, image); Vector3[] array = (Vector3[])(object)new Vector3[4]; ((Graphic)image).rectTransform.GetWorldCorners(array); Rect val = default(Rect); ((Rect)(ref val))..ctor(array[0].x, (float)Screen.height - array[2].y, array[2].x - array[0].x, array[2].y - array[0].y); Rect uvRect = image.uvRect; float num = (float)minimap.m_textureSize * minimap.m_pixelSize; float num2 = ((Rect)(ref val)).width / (num * ((Rect)(ref uvRect)).width); float num3 = ((Rect)(ref val)).height / (num * ((Rect)(ref uvRect)).height); FehuRoadsPlugin instance = FehuRoadsPlugin.Instance; GUI.BeginGroup(val); int num4 = 0; int num5 = 0; int num6 = 0; foreach (KeyValuePair<long, RoadType> item in _visible) { RoadDatabase.Decode(item.Key, out var x, out var z); RoadType value = item.Value; if (!TypeEnabled(instance, value)) { continue; } float num7 = ((float)x / minimap.m_pixelSize + (float)minimap.m_textureSize / 2f) / (float)minimap.m_textureSize; float num8 = ((float)z / minimap.m_pixelSize + (float)minimap.m_textureSize / 2f) / (float)minimap.m_textureSize; if (IsExplored(minimap, num7, num8, instance.RespectFog.Value)) { float num9 = (num7 - ((Rect)(ref uvRect)).x) / ((Rect)(ref uvRect)).width * ((Rect)(ref val)).width; float num10 = (1f - (num8 - ((Rect)(ref uvRect)).y) / ((Rect)(ref uvRect)).height) * ((Rect)(ref val)).height; float num11 = ((value == RoadType.Paved) ? instance.PavedWidth.Value : instance.DirtWidth.Value); float num12 = Mathf.Max(num11, num2 + 0.35f); float num13 = Mathf.Max(num11, num3 + 0.35f); GUI.color = ((value == RoadType.Paved) ? instance.PavedColor : instance.DirtColor); GUI.DrawTexture(new Rect(num9 - num12 * 0.5f, num10 - num13 * 0.5f, num12, num13), (Texture)(object)Texture2D.whiteTexture); switch (value) { case RoadType.Dirt: num4++; break; case RoadType.Paved: num5++; break; } num6++; } } GUI.EndGroup(); GUI.color = Color.white; if (num4 != _lastVanillaDirt || num5 != _lastVanillaPaved || num6 != _lastVanillaRendered) { _lastVanillaDirt = num4; _lastVanillaPaved = num5; _lastVanillaRendered = num6; instance.LogInfo("[FehuRoads] render surface=" + (((int)minimap.m_mode == 2) ? "large-map" : "mini-map") + " dirt=" + num4 + " paved=" + num5 + " total=" + num6); } } internal void DrawClanMapLayer(Rect local, Vector2 center, float baseDiameter, float zoom) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Invalid comparison between Unknown and I4 //IL_026d: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) FehuRoadsPlugin instance = FehuRoadsPlugin.Instance; if ((Object)(object)instance == (Object)null || !instance.KingdomsMap.Value || (int)Event.current.type != 7) { return; } _clanMapLastFrame = Time.frameCount; if (!_clanMapOpen) { _clanMapOpen = true; _clanMapDirty = true; instance.LogInfo("[FehuRoads] FCW tactical UI OPEN"); instance.LogInfo("[FehuRoads] FCW tactical root path=IMGUI/FortecaClanWarfare.UI.ClanWorldMapPanel/DrawCanvas"); instance.LogInfo("[FehuRoads] FCW tactical parent path=GUI.BeginGroup(canvas) instance=static"); instance.LogInfo("[FehuRoads] FCW tactical layer attached after=DrawPolygons before=ClanMapDrawingUi.Render"); } if (_clanMapDirty) { instance.Roads.Query(-10500, -10500, 10500, 10500, _clanMapRoads); _clanMapDirty = false; } float num = baseDiameter * 0.5f * zoom; float num2 = baseDiameter * zoom / 21000f; int num3 = 0; int num4 = 0; int num5 = 0; foreach (KeyValuePair<long, RoadType> clanMapRoad in _clanMapRoads) { RoadDatabase.Decode(clanMapRoad.Key, out var x, out var z); RoadType value = clanMapRoad.Value; if (!TypeEnabled(instance, value) || (double)x * (double)x + (double)z * (double)z > 110250000.0) { continue; } float num6 = center.x + (float)x / 10500f * num; float num7 = center.y - (float)z / 10500f * num; if (!(num6 < 0f) && !(num7 < 0f) && !(num6 > ((Rect)(ref local)).width) && !(num7 > ((Rect)(ref local)).height)) { float num8 = ((value == RoadType.Paved) ? instance.PavedWidth.Value : instance.DirtWidth.Value); float num9 = Mathf.Max(num8, num2 + 0.35f); GUI.color = ((value == RoadType.Paved) ? instance.PavedColor : instance.DirtColor); GUI.DrawTexture(new Rect(num6 - num9 * 0.5f, num7 - num9 * 0.5f, num9, num9), (Texture)(object)Texture2D.whiteTexture); switch (value) { case RoadType.Dirt: num3++; break; case RoadType.Paved: num4++; break; } num5++; } } GUI.color = Color.white; if (num3 != _lastClanDirt || num4 != _lastClanPaved || num5 != _lastClanRendered) { _lastClanMapCount = num5; _lastClanDirt = num3; _lastClanPaved = num4; _lastClanRendered = num5; instance.LogInfo("[FehuRoads] render surface=kingdom-map dirt=" + num3 + " paved=" + num4 + " total=" + num5 + " canvas=" + ((Rect)(ref local)).width.ToString("F0") + "x" + ((Rect)(ref local)).height.ToString("F0") + " zoom=" + zoom.ToString("F2")); } } private static bool TypeEnabled(FehuRoadsPlugin plugin, RoadType type) { return type switch { RoadType.Dirt => plugin.DirtEnabled.Value, RoadType.Paved => plugin.PavedEnabled.Value, _ => false, }; } private static bool IsExplored(Minimap minimap, float mapX, float mapY, bool required) { if (!required) { return true; } bool[] array = ((Explored == null) ? null : (Explored.GetValue(minimap) as bool[])); int num = Mathf.RoundToInt(mapX * (float)minimap.m_textureSize); int num2 = Mathf.RoundToInt(mapY * (float)minimap.m_textureSize); return array != null && num >= 0 && num2 >= 0 && num < minimap.m_textureSize && num2 < minimap.m_textureSize && array[num2 * minimap.m_textureSize + num]; } } } namespace Forteca.FehuRoads.Plugin { [BepInPlugin("forteca.fehuroads", "Fehu Roads", "0.1.7")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class FehuRoadsPlugin : BaseUnityPlugin, IRoadDataProvider { internal static FehuRoadsPlugin Instance; private RoadDatabase _roads; private ClientRoadNetwork _network; private RoadMapOverlay _overlay; private Harmony _harmony; private long _worldUid; private bool _snapshotComplete; internal ConfigEntry<bool> Enabled; internal ConfigEntry<bool> LargeMap; internal ConfigEntry<bool> MiniMap; internal ConfigEntry<bool> KingdomsMap; internal ConfigEntry<bool> RespectFog; internal ConfigEntry<bool> DirtEnabled; internal ConfigEntry<bool> PavedEnabled; internal ConfigEntry<bool> NetworkEnabled; internal ConfigEntry<bool> DebugLogging; internal ConfigEntry<string> DirtColorHex; internal ConfigEntry<string> PavedColorHex; internal ConfigEntry<float> DirtWidth; internal ConfigEntry<float> PavedWidth; internal RoadDatabase Roads => _roads; internal long WorldUid => _worldUid; internal bool SnapshotComplete => _snapshotComplete; internal RoadMapOverlay Overlay => _overlay; internal bool FcwTacticalAttached { get; private set; } internal Color DirtColor { get { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) Color val = default(Color); return (Color)(ColorUtility.TryParseHtmlString(DirtColorHex.Value, ref val) ? val : new Color(0.94f, 0.64f, 0.24f, 1f)); } } internal Color PavedColor { get { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) Color val = default(Color); return (Color)(ColorUtility.TryParseHtmlString(PavedColorHex.Value, ref val) ? val : new Color(0.7f, 0.71f, 0.73f, 0.82f)); } } public event Action<RoadCell, RoadType> RoadDataChanged; private void Awake() { Instance = this; Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable road display."); LargeMap = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "ShowOnLargeMap", true, "Show roads on the M map."); MiniMap = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "ShowOnMinimap", true, "Show roads on the HUD minimap."); KingdomsMap = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "ShowOnKingdomsMap", true, "Show roads with the Clan Warfare map."); RespectFog = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "RespectFogOfWar", true, "Do not draw in unexplored map areas."); DirtEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("DirtRoad", "Enabled", true, "Show dirt roads."); PavedEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("PavedRoad", "Enabled", true, "Show paved roads."); DirtColorHex = ((BaseUnityPlugin)this).Config.Bind<string>("DirtRoad", "Color", "#F0A43CFF", "High-contrast packed-earth color and opacity."); PavedColorHex = ((BaseUnityPlugin)this).Config.Bind<string>("PavedRoad", "Color", "#B2B6BBD0", "Cool stone color and opacity."); DirtWidth = ((BaseUnityPlugin)this).Config.Bind<float>("DirtRoad", "WidthMultiplier", 3f, "Minimum display pixels."); PavedWidth = ((BaseUnityPlugin)this).Config.Bind<float>("PavedRoad", "WidthMultiplier", 2f, "Minimum display pixels."); NetworkEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Network", "EnableSynchronization", true, "Receive authoritative server road chunks."); DebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "EnableDebugLogging", false, "Enable diagnostic logging."); _roads = new RoadDatabase(); _roads.Changed += ForwardChange; _network = new ClientRoadNetwork(this); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[FehuRoads] Client loaded version=0.1.7"); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[FehuRoads] RPC/network initialized"); InstallTerrainHook(); if (!Application.isBatchMode) { _overlay = ((Component)this).gameObject.AddComponent<RoadMapOverlay>(); RegisterConsoleCommandsSafe(); } InstallFcwTacticalBridge(); } private void RegisterConsoleCommandsSafe() { try { FehuRoadsCommands.Register(this); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[FehuRoads] Console commands registered"); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[FehuRoads] Console command registration unavailable; continuing without optional commands: " + ex)); } } private void InstallFcwTacticalBridge() { //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Expected O, but got Unknown if (!Chainloader.PluginInfos.TryGetValue("com.forteca.clanwarfare", out var value) || value == null || (Object)(object)value.Instance == (Object)null) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[FehuRoads] FCW map bridge not present; vanilla maps remain enabled"); return; } ((BaseUnityPlugin)this).Logger.LogInfo((object)"[FehuRoads] FCW map bridge detected"); try { Type type = ((object)value.Instance).GetType().Assembly.GetType("FortecaClanWarfare.UI.ClanMapDrawingUi", throwOnError: false); MethodInfo methodInfo = ((type == null) ? null : AccessTools.DeclaredMethod(type, "Render", (Type[])null, (Type[])null)); MethodInfo methodInfo2 = AccessTools.DeclaredMethod(typeof(FcwTacticalMapBridge), "BeforeClanMapDrawings", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null) { throw new MissingMethodException("FortecaClanWarfare.UI.ClanMapDrawingUi.Render"); } _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); FcwTacticalAttached = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)"[FehuRoads] FCW ClanMap bridge installed; waiting for actual ClanWorldMapPanel canvas"); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[FehuRoads] FCW tactical road layer unavailable; vanilla maps remain active: " + ex.Message)); } } private void InstallTerrainHook() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown _harmony = new Harmony("forteca.fehuroads.client"); try { MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(TerrainOp), "Awake", (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.DeclaredMethod(typeof(TerrainPaintClient), "TerrainOpPostfix", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null) { throw new MissingMethodException("TerrainOp.Awake()"); } _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[FehuRoads] Terrain change hint hook installed: TerrainOp.Awake()"); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("[FehuRoads] Terrain change hook failed; client networking and rendering remain active: " + ex)); } } private void Update() { if (Enabled.Value) { _network.Update(); } } private void ForwardChange(RoadCell cell, RoadType type) { this.RoadDataChanged?.Invoke(cell, type); } internal void BeginWorld(long uid) { if (_worldUid != uid) { _worldUid = uid; _snapshotComplete = false; _roads.Clear(); InvalidateRender(); } } internal void EndWorld() { if (_worldUid != 0) { _worldUid = 0L; _snapshotComplete = false; _roads.Clear(); InvalidateRender(); } } internal void BeginSnapshot() { _snapshotComplete = false; _roads.Clear(); InvalidateRender(); } internal void ApplyChunk(RoadChunkData chunk) { _roads.ReplaceChunk(chunk); } internal void CompleteSnapshot() { _snapshotComplete = true; InvalidateRender(); } public RoadType GetRoadType(Vector3 p) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) return _roads.Get(Mathf.RoundToInt(p.x), Mathf.RoundToInt(p.z)); } internal string Stats() { int num = 0; int num2 = 0; foreach (KeyValuePair<long, RoadType> entry in _roads.Entries) { if (entry.Value == RoadType.Dirt) { num++; } else if (entry.Value == RoadType.Paved) { num2++; } } return "FehuRoads client protocol=" + 1 + " world=" + _worldUid.ToString("X") + " snapshot=" + _snapshotComplete + " chunks=" + _roads.ChunkCount + " dirt=" + num + " paved=" + num2; } internal void InvalidateRender() { if ((Object)(object)_overlay != (Object)null) { _overlay.Invalidate(); } } internal void SendRoadUpdate(IList<KeyValuePair<long, RoadType>> cells) { _network.SendRoadUpdate(cells); } internal void LogInfo(string message) { ((BaseUnityPlugin)this).Logger.LogInfo((object)message); } internal void LogWarning(string message) { ((BaseUnityPlugin)this).Logger.LogWarning((object)message); } private void OnDestroy() { if (_harmony != null) { _harmony.UnpatchSelf(); } } } } namespace Forteca.FehuRoads.Networking { internal sealed class ClientRoadNetwork { private readonly FehuRoadsPlugin _plugin; private bool _registered; private bool _snapshotRequested; private long _requestedWorld; private ZRoutedRpc _routedRpc; internal ClientRoadNetwork(FehuRoadsPlugin plugin) { _plugin = plugin; } internal void Update() { //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Invalid comparison between Unknown and I4 if (!_plugin.NetworkEnabled.Value) { ResetSession(); return; } ZNet instance = ZNet.instance; ZRoutedRpc instance2 = ZRoutedRpc.instance; if ((Object)(object)instance == (Object)null || instance2 == null) { ResetSession(); return; } if (_routedRpc != instance2) { _routedRpc = instance2; _registered = false; ResetWorld(); } if (instance.IsServer() || (int)ZNet.GetConnectionStatus() != 2) { ResetWorld(); return; } ZNetPeer serverPeer = instance.GetServerPeer(); if (serverPeer == null || ZNet.World == null) { ResetWorld(); return; } if (!_registered) { instance2.Register<ZPackage>("FehuRoads_Begin", (Action<long, ZPackage>)OnBegin); instance2.Register<ZPackage>("FehuRoads_Chunk", (Action<long, ZPackage>)OnChunk); instance2.Register<ZPackage>("FehuRoads_End", (Action<long, ZPackage>)OnEnd); _registered = true; _plugin.LogInfo("[FehuRoads] Protocol registered version=" + 1); } long worldUID = instance.GetWorldUID(); if (worldUID != 0) { if (worldUID != _requestedWorld) { _requestedWorld = worldUID; _snapshotRequested = false; _plugin.BeginWorld(worldUID); } if (!_plugin.SnapshotComplete && !_snapshotRequested) { _snapshotRequested = true; instance2.InvokeRoutedRPC("FehuRoads_Request", new object[1] { RoadProtocol.Header(worldUID) }); _plugin.LogInfo("[FehuRoads] Snapshot requested world=" + worldUID.ToString("X")); } } } private void ResetWorld() { if (_requestedWorld != 0) { _plugin.EndWorld(); } _requestedWorld = 0L; _snapshotRequested = false; } private void ResetSession() { ResetWorld(); _registered = false; _routedRpc = null; } internal void SendRoadUpdate(IList<KeyValuePair<long, RoadType>> cells) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Invalid comparison between Unknown and I4 ZNet instance = ZNet.instance; ZRoutedRpc instance2 = ZRoutedRpc.instance; if (!_registered || cells == null || cells.Count == 0 || _plugin.WorldUid == 0L || (Object)(object)instance == (Object)null || instance2 == null || ZNet.World == null || instance.GetServerPeer() == null || (int)ZNet.GetConnectionStatus() != 2) { return; } instance2.InvokeRoutedRPC("FehuRoads_Update", new object[1] { RoadProtocol.EncodeUpdate(_plugin.WorldUid, cells) }); int num = 0; int num2 = 0; int num3 = 0; foreach (KeyValuePair<long, RoadType> cell in cells) { if (cell.Value == RoadType.Dirt) { num++; } else if (cell.Value == RoadType.Paved) { num2++; } else if (cell.Value == RoadType.None) { num3++; } } _plugin.LogInfo("[FehuRoads] road update type=DIRT cells=" + num + " type=PAVED cells=" + num2 + " removed=" + num3); } private bool IsServer(long sender) { ZNetPeer val = (((Object)(object)ZNet.instance == (Object)null) ? null : ZNet.instance.GetServerPeer()); return val != null && val.m_uid == sender; } private void OnBegin(long sender, ZPackage package) { if (IsServer(sender) && RoadProtocol.ReadAndValidateHeader(package, _plugin.WorldUid)) { _plugin.BeginSnapshot(); } } private void OnChunk(long sender, ZPackage package) { if (!IsServer(sender) || !RoadProtocol.TryDecodeChunk(package, _plugin.WorldUid, out var chunk)) { return; } int num = 0; int num2 = 0; RoadChunkData chunk2 = _plugin.Roads.GetChunk(chunk.Coord); HashSet<long> hashSet = new HashSet<long>(); KeyValuePair<long, RoadType>[] cells = chunk.Cells; for (int i = 0; i < cells.Length; i++) { KeyValuePair<long, RoadType> keyValuePair = cells[i]; hashSet.Add(keyValuePair.Key); if (keyValuePair.Value == RoadType.Dirt) { num++; } else if (keyValuePair.Value == RoadType.Paved) { num2++; } } int num3 = 0; KeyValuePair<long, RoadType>[] cells2 = chunk2.Cells; foreach (KeyValuePair<long, RoadType> keyValuePair2 in cells2) { if (!hashSet.Contains(keyValuePair2.Key)) { num3++; } } _plugin.ApplyChunk(chunk); _plugin.LogInfo("[FehuRoads] delta dirt=" + num + " paved=" + num2 + " removed=" + num3); } private void OnEnd(long sender, ZPackage package) { if (!IsServer(sender) || !RoadProtocol.ReadAndValidateHeader(package, _plugin.WorldUid)) { return; } int num = 0; int num2 = 0; foreach (KeyValuePair<long, RoadType> entry in _plugin.Roads.Entries) { if (entry.Value == RoadType.Dirt) { num++; } else if (entry.Value == RoadType.Paved) { num2++; } } _plugin.CompleteSnapshot(); _plugin.LogInfo("[FehuRoads] snapshot dirt=" + num + " paved=" + num2); } } } namespace Forteca.FehuRoads.Integration { public interface IRoadDataProvider { event Action<RoadCell, RoadType> RoadDataChanged; RoadType GetRoadType(Vector3 worldPosition); } } namespace Forteca.FehuRoads.Detection { internal static class TerrainPaintClient { internal static void TerrainOpPostfix(TerrainOp __instance) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Expected I4, but got Unknown //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null) { return; } Vector3 position = ((Component)__instance).transform.position; Settings settings = __instance.m_settings; FehuRoadsPlugin instance = FehuRoadsPlugin.Instance; Player localPlayer = Player.m_localPlayer; if ((Object)(object)instance == (Object)null || (Object)(object)localPlayer == (Object)null || settings == null || !settings.m_paintCleared || Vector3.Distance(((Component)localPlayer).transform.position, position) > 32f) { return; } PaintType paintType = settings.m_paintType; PaintType val = paintType; RoadType value; switch ((int)val) { default: return; case 0: value = RoadType.Dirt; break; case 2: value = RoadType.Paved; break; case 1: case 3: value = RoadType.None; break; } float num = Mathf.Clamp(settings.m_paintRadius, 0.5f, 12f); int num2 = Mathf.FloorToInt(position.x - num); int num3 = Mathf.CeilToInt(position.x + num); int num4 = Mathf.FloorToInt(position.z - num); int num5 = Mathf.CeilToInt(position.z + num); List<KeyValuePair<long, RoadType>> list = new List<KeyValuePair<long, RoadType>>(); float num6 = num * num; for (int i = num4; i <= num5; i++) { for (int j = num2; j <= num3; j++) { Vector2 val2 = new Vector2((float)j - position.x, (float)i - position.z); if (((Vector2)(ref val2)).sqrMagnitude <= num6) { list.Add(new KeyValuePair<long, RoadType>(RoadDatabase.Key(j, i), value)); } } } instance.LogInfo("[FehuRoads] detected type=" + value.ToString().ToUpperInvariant() + " cells=" + list.Count); instance.SendRoadUpdate(list); } } } namespace Forteca.FehuRoads.Core { public struct RoadCell { public readonly int X; public readonly int Z; public RoadCell(int x, int z) { X = x; Z = z; } } public struct RoadChunkCoord : IEquatable<RoadChunkCoord> { public readonly int X; public readonly int Z; public RoadChunkCoord(int x, int z) { X = x; Z = z; } public bool Equals(RoadChunkCoord o) { return X == o.X && Z == o.Z; } public override int GetHashCode() { return (X * 486187739) ^ Z; } } public sealed class RoadChunkData { public readonly RoadChunkCoord Coord; public readonly uint Revision; public readonly KeyValuePair<long, RoadType>[] Cells; public RoadChunkData(RoadChunkCoord c, uint r, KeyValuePair<long, RoadType>[] a) { Coord = c; Revision = r; Cells = a; } } public sealed class RoadDatabase { public const int ChunkSize = 32; private readonly Dictionary<long, RoadType> _cells = new Dictionary<long, RoadType>(); private readonly Dictionary<RoadChunkCoord, HashSet<long>> _chunks = new Dictionary<RoadChunkCoord, HashSet<long>>(); private readonly Dictionary<RoadChunkCoord, uint> _revisions = new Dictionary<RoadChunkCoord, uint>(); public int Count => _cells.Count; public int ChunkCount => _chunks.Count; public IEnumerable<KeyValuePair<long, RoadType>> Entries => _cells; public IEnumerable<RoadChunkData> Chunks { get { foreach (RoadChunkCoord c in _chunks.Keys) { yield return GetChunk(c); } } } public event Action<RoadCell, RoadType> Changed; public event Action<RoadChunkCoord, uint> ChunkChanged; public static long Key(int x, int z) { return ((long)x << 32) ^ (uint)z; } public static void Decode(long k, out int x, out int z) { x = (int)(k >> 32); z = (int)k; } private static int FloorDiv(int n, int d) { int num = n / d; int num2 = n % d; return (num2 < 0) ? (num - 1) : num; } public static RoadChunkCoord ChunkOf(int x, int z) { return new RoadChunkCoord(FloorDiv(x, 32), FloorDiv(z, 32)); } public RoadType Get(int x, int z) { RoadType value; return _cells.TryGetValue(Key(x, z), out value) ? value : RoadType.None; } public bool Set(int x, int z, RoadType t) { long num = Key(x, z); RoadType roadType = Get(x, z); if (roadType == t) { return false; } RoadChunkCoord roadChunkCoord = ChunkOf(x, z); if (t == RoadType.None) { _cells.Remove(num); if (_chunks.TryGetValue(roadChunkCoord, out var value)) { value.Remove(num); if (value.Count == 0) { _chunks.Remove(roadChunkCoord); } } } else { _cells[num] = t; if (!_chunks.TryGetValue(roadChunkCoord, out var value2)) { value2 = new HashSet<long>(); _chunks[roadChunkCoord] = value2; } value2.Add(num); } Touch(roadChunkCoord); this.Changed?.Invoke(new RoadCell(x, z), t); return true; } private void Touch(RoadChunkCoord c) { _revisions.TryGetValue(c, out var value); value++; _revisions[c] = value; this.ChunkChanged?.Invoke(c, value); } public void Query(int minX, int minZ, int maxX, int maxZ, List<KeyValuePair<long, RoadType>> dst) { dst.Clear(); RoadChunkCoord roadChunkCoord = ChunkOf(minX, minZ); RoadChunkCoord roadChunkCoord2 = ChunkOf(maxX, maxZ); for (int i = roadChunkCoord.Z; i <= roadChunkCoord2.Z; i++) { for (int j = roadChunkCoord.X; j <= roadChunkCoord2.X; j++) { if (!_chunks.TryGetValue(new RoadChunkCoord(j, i), out var value)) { continue; } foreach (long item in value) { Decode(item, out var x, out var z); if (x >= minX && x <= maxX && z >= minZ && z <= maxZ) { dst.Add(new KeyValuePair<long, RoadType>(item, _cells[item])); } } } } } public RoadChunkData GetChunk(RoadChunkCoord c) { _revisions.TryGetValue(c, out var value); if (!_chunks.TryGetValue(c, out var value2)) { return new RoadChunkData(c, value, new KeyValuePair<long, RoadType>[0]); } KeyValuePair<long, RoadType>[] array = new KeyValuePair<long, RoadType>[value2.Count]; int num = 0; foreach (long item in value2) { array[num++] = new KeyValuePair<long, RoadType>(item, _cells[item]); } return new RoadChunkData(c, value, array); } public bool ReplaceChunk(RoadChunkData d) { _revisions.TryGetValue(d.Coord, out var value); if (d.Revision <= value) { return false; } if (_chunks.TryGetValue(d.Coord, out var value2)) { foreach (long item in value2) { _cells.Remove(item); } _chunks.Remove(d.Coord); } if (d.Cells.Length != 0) { HashSet<long> hashSet = new HashSet<long>(); KeyValuePair<long, RoadType>[] cells = d.Cells; for (int i = 0; i < cells.Length; i++) { KeyValuePair<long, RoadType> keyValuePair = cells[i]; if (keyValuePair.Value == RoadType.Dirt || keyValuePair.Value == RoadType.Paved) { _cells[keyValuePair.Key] = keyValuePair.Value; hashSet.Add(keyValuePair.Key); } } if (hashSet.Count > 0) { _chunks[d.Coord] = hashSet; } } _revisions[d.Coord] = d.Revision; this.ChunkChanged?.Invoke(d.Coord, d.Revision); return true; } public void Clear() { _cells.Clear(); _chunks.Clear(); _revisions.Clear(); } public void Write(BinaryWriter w) { w.Write(_cells.Count); foreach (KeyValuePair<long, RoadType> cell in _cells) { w.Write(cell.Key); w.Write((byte)cell.Value); } } public void Read(BinaryReader r) { Clear(); int num = r.ReadInt32(); if (num < 0 || num > 20000000) { throw new InvalidDataException("Invalid road-cell count"); } for (int i = 0; i < num; i++) { long k = r.ReadInt64(); RoadType roadType = (RoadType)r.ReadByte(); if (roadType == RoadType.Dirt || roadType == RoadType.Paved) { Decode(k, out var x, out var z); Set(x, z, roadType); } } } } public static class RoadProtocol { public const int Version = 1; public const int MaxCellsPerChunk = 2048; public const string RequestRpc = "FehuRoads_Request"; public const string UpdateRpc = "FehuRoads_Update"; public const string BeginRpc = "FehuRoads_Begin"; public const string ChunkRpc = "FehuRoads_Chunk"; public const string EndRpc = "FehuRoads_End"; public const int MaxUpdateCells = 512; public static ZPackage Header(long worldUid) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(1); val.Write(worldUid); return val; } public static bool ReadAndValidateHeader(ZPackage package, long expectedWorldUid) { try { return package.ReadInt() == 1 && package.ReadLong() == expectedWorldUid; } catch { return false; } } public static ZPackage EncodeUpdate(long worldUid, IList<KeyValuePair<long, RoadType>> cells) { ZPackage val = Header(worldUid); val.Write(cells.Count); for (int i = 0; i < cells.Count; i++) { val.Write(cells[i].Key); val.Write((byte)cells[i].Value); } return val; } public static bool TryDecodeUpdate(ZPackage package, long expectedWorldUid, out KeyValuePair<long, RoadType>[] cells) { cells = null; if (!ReadAndValidateHeader(package, expectedWorldUid)) { return false; } try { int num = package.ReadInt(); if (num < 1 || num > 512) { return false; } cells = new KeyValuePair<long, RoadType>[num]; for (int i = 0; i < num; i++) { long key = package.ReadLong(); RoadType roadType = (RoadType)package.ReadByte(); if (roadType != RoadType.None && roadType != RoadType.Dirt && roadType != RoadType.Paved) { return false; } cells[i] = new KeyValuePair<long, RoadType>(key, roadType); } return true; } catch { cells = null; return false; } } public static ZPackage EncodeChunk(long worldUid, RoadChunkData chunk) { ZPackage val = Header(worldUid); val.Write(chunk.Coord.X); val.Write(chunk.Coord.Z); val.Write(chunk.Revision); val.Write(chunk.Cells.Length); KeyValuePair<long, RoadType>[] cells = chunk.Cells; for (int i = 0; i < cells.Length; i++) { KeyValuePair<long, RoadType> keyValuePair = cells[i]; val.Write(keyValuePair.Key); val.Write((byte)keyValuePair.Value); } return val; } public static bool TryDecodeChunk(ZPackage package, long expectedWorldUid, out RoadChunkData chunk) { chunk = null; if (!ReadAndValidateHeader(package, expectedWorldUid)) { return false; } try { int x = package.ReadInt(); int z = package.ReadInt(); uint r = package.ReadUInt(); int num = package.ReadInt(); if (num < 0 || num > 2048) { return false; } KeyValuePair<long, RoadType>[] array = new KeyValuePair<long, RoadType>[num]; for (int i = 0; i < num; i++) { long key = package.ReadLong(); RoadType roadType = (RoadType)package.ReadByte(); if (roadType != RoadType.Dirt && roadType != RoadType.Paved) { return false; } array[i] = new KeyValuePair<long, RoadType>(key, roadType); } chunk = new RoadChunkData(new RoadChunkCoord(x, z), r, array); return true; } catch { return false; } } } public enum RoadType : byte { None, Dirt, Paved } } namespace Forteca.FehuRoads.Commands { internal static class FehuRoadsCommands { internal static void Register(FehuRoadsPlugin plugin) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_002e: Unknown result type (might be due to invalid IL or missing references) new ConsoleCommand("fehuroads", "fehuroads stats | rebuild-render", (ConsoleEvent)delegate(ConsoleEventArgs args) { if (args.Length < 2) { args.Context.AddString("fehuroads stats | rebuild-render"); } else if (args[1] == "stats") { args.Context.AddString(plugin.Stats()); } else if (args[1] == "rebuild-render") { plugin.InvalidateRender(); args.Context.AddString("FehuRoads renderer invalidated."); } }, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } } }