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 SatelliteMap v0.2.0
SatelliteMap.dll
Decompiled 19 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Threading; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using SatelliteMap.Render; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("SatelliteMap")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.2.0.0")] [assembly: AssemblyInformationalVersion("0.2.0+c6634ddb2b62585c7dbb7e33e0955f3e3da99683")] [assembly: AssemblyProduct("SatelliteMap")] [assembly: AssemblyTitle("SatelliteMap")] [assembly: AssemblyVersion("0.2.0.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 SatelliteMap { public static class SatelliteMapApi { private static readonly List<Func<int>> limits = new List<Func<int>>(); private static Func<int, int, int> revisions; public static void SetZoneRevisionProvider(Func<int, int, int> provider) { revisions = provider; } public static bool Survey(int[] zoneXs, int[] zoneYs, Action<int[], int[], int[]> done) { return ZoneFeed.Survey(zoneXs, zoneYs, done); } internal static int RevisionOf(int zoneX, int zoneY) { Func<int, int, int> func = revisions; if (func == null) { return 0; } try { return Math.Max(0, func(zoneX, zoneY)); } catch (Exception) { return 0; } } public static void AddDetailLimit(Func<int> provider) { if (provider != null) { limits.Add(provider); } } public static void NotifyExploredChanged() { SatMap.OnExploredChanged(); } internal static int Limit(int level) { for (int i = 0; i < limits.Count; i++) { int num; try { num = limits[i](); } catch (Exception) { continue; } if (num >= 0 && num < level) { level = num; } } return level; } } internal struct Explored { private static readonly FieldRef<Minimap, BitArray> OwnRef = AccessTools.FieldRefAccess<Minimap, BitArray>("m_explored"); private static readonly FieldRef<Minimap, BitArray> OthersRef = AccessTools.FieldRefAccess<Minimap, BitArray>("m_exploredOthers"); private static readonly FieldRef<Minimap, bool> ShowSharedRef = AccessTools.FieldRefAccess<Minimap, bool>("m_showSharedMapData"); private BitArray own; private BitArray others; private int size; internal bool SharedShown { get; private set; } internal static Explored Of(Minimap minimap) { Explored result = default(Explored); result.own = OwnRef.Invoke(minimap); result.others = OthersRef.Invoke(minimap); result.size = minimap.m_textureSize; result.SharedShown = ShowSharedRef.Invoke(minimap) && result.others != null; return result; } internal bool At(int x, int y) { int num = y * size + x; if (own == null || num < 0 || num >= own.Length) { return false; } if (!own[num]) { if (SharedShown && num < others.Length) { return others[num]; } return false; } return true; } } [HarmonyPatch(typeof(Minimap), "Awake")] internal static class Minimap_Awake_Patch { private static void Postfix() { SatMap.Reset(); } } [HarmonyPatch(typeof(ZNet), "OnDestroy")] internal static class ZNet_OnDestroy_Patch { private static void Postfix() { Snapshots.Close(); } } [HarmonyPatch(typeof(Minimap), "Update")] internal static class Minimap_Update_Patch { private static void Postfix(Minimap __instance) { SatMap.Tick(__instance); } } [HarmonyPatch(typeof(Minimap), "Explore", new Type[] { typeof(int), typeof(int) })] internal static class Minimap_ExploreCell_Patch { private static void Postfix(bool __result) { if (__result) { SatMap.OnExploredChanged(); } } } [HarmonyPatch(typeof(Minimap), "ExploreOthers", new Type[] { typeof(int), typeof(int) })] internal static class Minimap_ExploreOthers_Patch { private static void Postfix(bool __result) { if (__result) { SatMap.OnExploredChanged(); } } } [HarmonyPatch(typeof(Minimap), "ResetAndExplore", new Type[] { typeof(BitArray), typeof(BitArray) })] internal static class Minimap_ResetAndExplore_Patch { private static void Postfix() { SatMap.OnExploredChanged(); } } [HarmonyPatch(typeof(Minimap), "Reset")] internal static class Minimap_Reset_Patch { private static void Postfix() { SatMap.OnExploredChanged(); } } [HarmonyPatch(typeof(Minimap), "ResetSharedMapData")] internal static class Minimap_ResetSharedMapData_Patch { private static void Postfix() { SatMap.OnExploredChanged(); } } [BepInPlugin("qua8ion.valheim.satellitemap", "SatelliteMap", "0.2.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class SatelliteMapPlugin : BaseUnityPlugin { public const string PluginGuid = "qua8ion.valheim.satellitemap"; public const string PluginName = "SatelliteMap"; public const string PluginVersion = "0.2.0"; internal static ManualLogSource Log; internal static SatelliteMapPlugin Instance; internal static ConfigEntry<int> DetailLevel; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> DebugTiles; internal static ConfigEntry<string> ServerSelfTest; private float selfTestAt = -1f; private bool selfTestDone; private void Awake() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown //IL_00c0: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; Instance = this; DetailLevel = ((BaseUnityPlugin)this).Config.Bind<int>("Rules", "DetailLevel", 3, new ConfigDescription("How detailed the picture of the explored part of the map is when you zoom in. 0: the plain game map only. 1: a sketch, 2 metres per pixel: relief, water, paths, fields, buildings, ruins and boss altars. 2: a good map, 1 metre per pixel: also the forest and large rocks. 3: the best map, half a metre per pixel: everything down to bushes. On a dedicated server the server's value applies to everyone.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 3), new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } })); Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Draw the detailed picture on the big map. Your own switch, not synced."); DebugTiles = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DumpMapTiles", false, "Save every drawn piece of the map as PNG into BepInEx/cache/SatelliteMap/tiles and log the timings."); ServerSelfTest = ((BaseUnityPlugin)this).Config.Bind<string>("Debug", "ServerSelfTest", "", "For checking the server part without a client: \"x,z,radius\" in metres. Shortly after the world is loaded the server describes the zones around that point, writes the packet to BepInEx/cache/SatelliteMap/selftest.bin and runs the same zones through the request path a client would use. Empty: off."); ZoneFeed.RegisterRpc(); new Harmony("qua8ion.valheim.satellitemap").PatchAll(Assembly.GetExecutingAssembly()); } private void Update() { if (selfTestDone || string.IsNullOrEmpty(ServerSelfTest.Value)) { return; } if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null || (Object)(object)ZoneSystem.instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null) { selfTestAt = -1f; return; } if (selfTestAt < 0f) { selfTestAt = Time.time + 15f; } if (Time.time >= selfTestAt) { selfTestDone = true; ((MonoBehaviour)this).StartCoroutine(ZoneFeed.SelfTest(ServerSelfTest.Value)); } } } internal static class SatGame { private static ShapeLibrary shapes; private const float HelperReach = 20f; internal static ShapeLibrary Shapes { get { if (shapes == null) { using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("shapes.bin")) { shapes = ShapeLibrary.Load(stream); } SatelliteMapPlugin.Log.LogInfo((object)("Top-down shapes of game objects: " + shapes.Count)); } return shapes; } } internal static int DetailLevel() { if (!SatelliteMapPlugin.Enabled.Value) { return 0; } return SatelliteMapApi.Limit(Mathf.Clamp(SatelliteMapPlugin.DetailLevel.Value, 0, 3)); } internal static bool IsForeignPiece(int prefabHash) { if (Shapes.Find(prefabHash, location: false) != null || Shapes.IsGamePrefab(prefabHash) || (Object)(object)ZNetScene.instance == (Object)null) { return false; } GameObject prefab = ZNetScene.instance.GetPrefab(prefabHash); if ((Object)(object)prefab != (Object)null && (Object)(object)prefab.GetComponent<Piece>() != (Object)null && (Object)(object)prefab.GetComponent<WearNTear>() != (Object)null) { return (Object)(object)prefab.GetComponent<Rigidbody>() == (Object)null; } return false; } internal static bool TryRegisterForeign(int prefabHash) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_0253: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Unknown result type (might be due to invalid IL or missing references) //IL_0265: Unknown result type (might be due to invalid IL or missing references) //IL_026b: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: 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_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Expected I4, but got Unknown //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0143: 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) //IL_0145: Unknown result type (might be due to invalid IL or missing references) try { if (!IsForeignPiece(prefabHash)) { return false; } GameObject prefab = ZNetScene.instance.GetPrefab(prefabHash); bool flag = false; Vector3 val = Vector3.zero; Vector3 val2 = Vector3.zero; Transform transform = prefab.transform; MeshFilter[] componentsInChildren = prefab.GetComponentsInChildren<MeshFilter>(false); foreach (MeshFilter val3 in componentsInChildren) { MeshRenderer component = ((Component)val3).GetComponent<MeshRenderer>(); if ((Object)(object)val3.sharedMesh == (Object)null || (Object)(object)component == (Object)null || !((Renderer)component).enabled) { continue; } Bounds bounds = val3.sharedMesh.bounds; Vector3 val4 = transform.InverseTransformPoint(((Component)val3).transform.TransformPoint(((Bounds)(ref bounds)).center)); if (val4.y < -20f || val4.y > 20f) { continue; } for (int j = 0; j < 8; j++) { Vector3 val5 = ((Bounds)(ref bounds)).center + Vector3.Scale(((Bounds)(ref bounds)).extents, new Vector3(((j & 1) == 0) ? (-1f) : 1f, ((j & 2) == 0) ? (-1f) : 1f, ((j & 4) == 0) ? (-1f) : 1f)); Vector3 val6 = transform.InverseTransformPoint(((Component)val3).transform.TransformPoint(val5)); if (!flag) { val = (val2 = val6); flag = true; } else { val = Vector3.Min(val, val6); val2 = Vector3.Max(val2, val6); } } } val2.y = Mathf.Min(val2.y, 20f); if (!flag || val2.x - val.x > 60f || val2.z - val.z > 60f) { return false; } byte r = 120; byte g = 92; byte b = 62; WearNTear component2 = prefab.GetComponent<WearNTear>(); if ((Object)(object)component2 != (Object)null) { MaterialType materialType = component2.m_materialType; switch (materialType - 1) { case 0: case 4: r = 128; g = 126; b = 120; break; case 3: r = 190; g = 188; b = 180; break; case 1: r = 84; g = 80; b = 76; break; case 2: r = 84; g = 62; b = 44; break; } } Shapes.Register(Shape.Box(prefabHash, val.x, val.z, val2.x, val2.z, val2.y, r, g, b)); return true; } catch (Exception ex) { SatelliteMapPlugin.Log.LogWarning((object)("Could not outline prefab " + prefabHash + ": " + ex.Message)); return false; } } } internal sealed class GameWorldSource : IWorldSource { private readonly WorldGenerator generator; internal GameWorldSource(WorldGenerator generator) { this.generator = generator; } public int GetBiome(float wx, float wz) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected I4, but got Unknown return (int)generator.GetBiome(wx, wz, 0.02f, false); } public float GetBiomeHeight(int biome, float wx, float wz, out float lava) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) Color val = default(Color); float biomeHeight = generator.GetBiomeHeight((Biome)biome, wx, wz, ref val, false, true); lava = ((biome == 32) ? val.a : 0f); return biomeHeight; } } internal static class SatMap { private enum TileState { New, Empty, Waiting, Queued, Ready, Failed } private sealed class Tile { public int Lod; public int X; public int Z; public TileSpec Spec; public TileState State; public byte[] Rgba; public Texture2D Texture; public RawImage Image; public float LastUsed; public float RetryAt; public long ZoneStamps; public int ExploredVersion = -1; public int EmptyForVersion = -1; public bool Dirty; public float NextPoll; public float SortKey; public List<long> Zones; public int ZonesForVersion = -1; public bool InFlight; public float ShownAt = -1f; } private sealed class Job { public Tile Tile; public TileSpec Spec; public Dictionary<long, ZoneData> Zones; public long ZoneStamps; public int Generation; public byte[] Result; public Exception Error; public double Milliseconds; } private const int TileSize = 256; private const int CacheLimit = 120; private const int VisibleLimit = 96; private const int UploadsPerFrame = 2; private const float ZoneMaxAge = 180f; private const float RetryAfterError = 30f; private static readonly Dictionary<long, Tile> tiles = new Dictionary<long, Tile>(); private static readonly Queue<Job> jobs = new Queue<Job>(); private static readonly List<Job> finished = new List<Job>(); private static readonly HashSet<int> foreignTried = new HashSet<int>(); private static readonly List<Tile> visible = new List<Tile>(); private static Thread[] workers; private static TileLayer layer; private static TileRenderer renderer; private static WorldGenerator rendererWorld; private static int generation; private static int exploredVersion; private static int queued; private static bool sharedShown; private static bool faulted; private static float lastLog; private static readonly Comparison<Tile> NearestFirst = (Tile a, Tile b) => a.SortKey.CompareTo(b.SortKey); internal static void Reset() { generation++; lock (jobs) { jobs.Clear(); queued = 0; } lock (finished) { finished.Clear(); } foreach (Tile value in tiles.Values) { if ((Object)(object)value.Texture != (Object)null) { Object.Destroy((Object)(object)value.Texture); } } tiles.Clear(); if (layer != null) { layer.Destroy(); } layer = null; renderer = null; rendererWorld = null; foreignTried.Clear(); ZoneCache.Reset(); faulted = false; } internal static void OnExploredChanged() { exploredVersion++; } internal static void Tick(Minimap minimap) { if (faulted) { return; } try { TickCore(minimap); } catch (Exception ex) { faulted = true; Hide(); SatelliteMapPlugin.Log.LogError((object)("Detailed map picture is switched off until you re-enter the world: " + ex)); } } private static void TickCore(Minimap minimap) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Invalid comparison between Unknown and I4 //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0361: Unknown result type (might be due to invalid IL or missing references) //IL_0362: Unknown result type (might be due to invalid IL or missing references) DetailSpec spec = MapDetail.ForLevel(SatGame.DetailLevel()); RawImage mapImageLarge = minimap.m_mapImageLarge; if (!spec.Enabled || (int)minimap.m_mode != 2 || (Object)(object)mapImageLarge == (Object)null || WorldGenerator.instance == null) { Hide(); return; } Rect uvRect = mapImageLarge.uvRect; Rect rect = ((Graphic)mapImageLarge).rectTransform.rect; if (((Rect)(ref uvRect)).width <= 0f || ((Rect)(ref rect)).width <= 1f || ((Rect)(ref rect)).height <= 1f) { Hide(); return; } float num = (float)minimap.m_textureSize * minimap.m_pixelSize; float num2 = num / 2f; float num3 = ((Rect)(ref uvRect)).xMin * num - num2; float num4 = ((Rect)(ref uvRect)).xMax * num - num2; float num5 = ((Rect)(ref uvRect)).yMin * num - num2; float num6 = ((Rect)(ref uvRect)).yMax * num - num2; float num7 = (((Object)(object)((Graphic)mapImageLarge).canvas != (Object)null) ? ((Graphic)mapImageLarge).canvas.scaleFactor : 1f); float screenMetersPerPixel = (num6 - num5) / Mathf.Max(1f, ((Rect)(ref rect)).height * num7); float num8 = MapDetail.ResolutionFor(spec, screenMetersPerPixel); float num9 = MapDetail.Opacity(num6 - num5); if (num8 <= 0f || num9 <= 0f) { Hide(); return; } while (TilesAcross(num3, num4, num8) * TilesAcross(num5, num6, num8) > 96) { float num10 = MapDetail.Coarser(num8); if (num10 <= 0f) { break; } num8 = num10; } if (layer == null || !layer.Alive) { layer = new TileLayer(((Graphic)mapImageLarge).rectTransform, MapDetail.Resolutions.Length); } layer.SetVisible(visible: true); EnsureRenderer(); ZoneCache.Tick(); Upload(minimap); int lod = LodOf(num8); float num11 = 256f * num8; int num12 = Mathf.FloorToInt((num3 + 32f) / num11); int num13 = Mathf.FloorToInt((num4 + 32f) / num11); int num14 = Mathf.FloorToInt((num5 + 32f) / num11); int num15 = Mathf.FloorToInt((num6 + 32f) / num11); float x = (num3 + num4) / 2f; float z = (num5 + num6) / 2f; ZoneLayers zoneLayers = MapDetail.LayersFor(spec, num8); Explored drawn = Explored.Of(minimap); if (drawn.SharedShown != sharedShown) { sharedShown = drawn.SharedShown; exploredVersion++; } visible.Clear(); for (int i = num14; i <= num15; i++) { for (int j = num12; j <= num13; j++) { visible.Add(GetTile(lod, j, i, num8, zoneLayers)); } } foreach (Tile item in visible) { item.SortKey = Distance(item, x, z); } visible.Sort(NearestFirst); if (visible.Count > 96) { visible.RemoveRange(96, visible.Count - 96); } foreach (Tile item2 in visible) { item2.LastUsed = Time.time; Advance(minimap, item2, drawn); } Layout(uvRect, rect, num, lod, num9); Evict(); if (!SatelliteMapPlugin.DebugTiles.Value || !(Time.time - lastLog > 5f)) { return; } lastLog = Time.time; int num16 = 0; foreach (Tile item3 in visible) { num16 += ((item3.State == TileState.Ready) ? 1 : 0); } SatelliteMapPlugin.Log.LogInfo((object)("Detail map: " + num8 + " m/px, " + screenMetersPerPixel.ToString("F2") + " m per screen px, tiles " + num16 + "/" + visible.Count + " ready, queue " + queued + ", cached " + tiles.Count)); } private static int TilesAcross(float min, float max, float mpp) { float num = 256f * mpp; return Mathf.FloorToInt((max + 32f) / num) - Mathf.FloorToInt((min + 32f) / num) + 1; } private static void Hide() { if (layer != null) { layer.SetVisible(visible: false); } } private static int LodOf(float mpp) { for (int i = 0; i < MapDetail.Resolutions.Length; i++) { if (Mathf.Approximately(MapDetail.Resolutions[i], mpp)) { return i; } } return MapDetail.Resolutions.Length - 1; } private static float Distance(Tile tile, float x, float z) { float num = tile.Spec.OriginX + tile.Spec.WorldSize / 2f - x; float num2 = tile.Spec.OriginZ + tile.Spec.WorldSize / 2f - z; return num * num + num2 * num2; } private static Tile GetTile(int lod, int x, int z, float mpp, ZoneLayers zoneLayers) { long key = ((long)lod << 56) ^ ((long)(x & 0xFFFFFFF) << 28) ^ (z & 0xFFFFFFF); if (!tiles.TryGetValue(key, out var value)) { float num = 256f * mpp; value = new Tile { Lod = lod, X = x, Z = z, Spec = new TileSpec { OriginX = (float)x * num - 32f, OriginZ = (float)z * num - 32f, Size = 256, MetersPerPixel = mpp, Layers = zoneLayers } }; tiles[key] = value; } else if (value.Spec.Layers != zoneLayers) { value.Spec = new TileSpec { OriginX = value.Spec.OriginX, OriginZ = value.Spec.OriginZ, Size = 256, MetersPerPixel = mpp, Layers = zoneLayers }; value.Dirty = value.State == TileState.Ready; if (value.State == TileState.Failed) { value.State = TileState.New; } } return value; } private static void Advance(Minimap minimap, Tile tile, Explored drawn) { if (tile.InFlight) { return; } if (tile.State == TileState.Failed) { if (Time.time < tile.RetryAt) { return; } tile.State = TileState.New; } if (tile.State == TileState.Empty && tile.EmptyForVersion == exploredVersion) { return; } if (tile.State == TileState.New || tile.State == TileState.Empty) { if (!AnyDrawn(minimap, tile.Spec, drawn)) { tile.State = TileState.Empty; tile.EmptyForVersion = exploredVersion; return; } tile.State = TileState.Waiting; } if (Time.time < tile.NextPoll) { return; } if (tile.Zones == null || tile.ZonesForVersion != exploredVersion) { tile.Zones = NeededZones(minimap, tile.Spec, drawn); tile.ZonesForVersion = exploredVersion; } if (tile.State == TileState.Ready && !tile.Dirty) { tile.NextPoll = Time.time + 2f + (float)(tile.X & 3) * 0.25f; if (CollectZones(tile, null, out var stamps) && stamps != tile.ZoneStamps) { tile.Dirty = true; } if (!tile.Dirty) { return; } } if (queued >= 6) { return; } Dictionary<long, ZoneData> dictionary = new Dictionary<long, ZoneData>(); if (!CollectZones(tile, dictionary, out var stamps2)) { tile.NextPoll = Time.time + 0.2f; return; } RegisterForeign(dictionary); Job item = new Job { Tile = tile, Spec = tile.Spec, Zones = dictionary, ZoneStamps = stamps2, Generation = generation }; tile.Dirty = false; tile.InFlight = true; if (tile.State != TileState.Ready) { tile.State = TileState.Queued; } lock (jobs) { jobs.Enqueue(item); queued++; Monitor.Pulse(jobs); } } private static bool AnyDrawn(Minimap minimap, TileSpec spec, Explored drawn) { int textureSize = minimap.m_textureSize; float pixelSize = minimap.m_pixelSize; int num = Mathf.FloorToInt(spec.OriginX / pixelSize + (float)textureSize / 2f - 0.5f); int num2 = Mathf.FloorToInt((spec.OriginX + spec.WorldSize) / pixelSize + (float)textureSize / 2f - 0.5f) + 1; int num3 = Mathf.FloorToInt(spec.OriginZ / pixelSize + (float)textureSize / 2f - 0.5f); int num4 = Mathf.FloorToInt((spec.OriginZ + spec.WorldSize) / pixelSize + (float)textureSize / 2f - 0.5f) + 1; for (int i = Mathf.Max(0, num3); i <= Mathf.Min(textureSize - 1, num4); i++) { for (int j = Mathf.Max(0, num); j <= Mathf.Min(textureSize - 1, num2); j++) { if (drawn.At(j, i)) { return true; } } } return false; } private static List<long> NeededZones(Minimap minimap, TileSpec spec, Explored drawn) { TileRenderer.ZoneRange(spec, out var minZoneX, out var minZoneY, out var maxZoneX, out var maxZoneY); List<long> list = new List<long>(); float num = 64f; TileSpec tileSpec = new TileSpec { Size = 1, MetersPerPixel = 64f + num * 2f }; for (int i = minZoneY; i <= maxZoneY; i++) { for (int j = minZoneX; j <= maxZoneX; j++) { tileSpec.OriginX = (float)j * 64f - 32f - num; tileSpec.OriginZ = (float)i * 64f - 32f - num; if (AnyDrawn(minimap, tileSpec, drawn)) { list.Add(ZoneCache.Key(j, i)); } } } return list; } private static bool CollectZones(Tile tile, Dictionary<long, ZoneData> target, out long stamps) { bool result = true; stamps = 0L; foreach (long zone in tile.Zones) { int zoneX = (int)(zone >> 32); int zoneY = (int)(zone & 0xFFFFFFFFu); int zoneStamp; ZoneData zoneData = ZoneCache.Get(zoneX, zoneY, tile.Spec.Layers, 180f, tile.SortKey, SatelliteMapApi.RevisionOf(zoneX, zoneY), out zoneStamp); if (zoneData == null) { result = false; continue; } stamps = stamps * 31 + zoneStamp; if (target != null) { target[zone] = zoneData; } } return result; } private static void RegisterForeign(Dictionary<long, ZoneData> zones) { ShapeLibrary shapes = SatGame.Shapes; foreach (ZoneData value in zones.Values) { foreach (ZoneObject @object in value.Objects) { if (!@object.IsLocation && shapes.Find(@object.Prefab, location: false) == null && foreignTried.Add(@object.Prefab)) { SatGame.TryRegisterForeign(@object.Prefab); } } } } private static void EnsureRenderer() { if (renderer == null || rendererWorld != WorldGenerator.instance) { rendererWorld = WorldGenerator.instance; renderer = new TileRenderer(new GameWorldSource(rendererWorld), SatGame.Shapes); } if (workers == null) { workers = new Thread[2]; for (int i = 0; i < workers.Length; i++) { workers[i] = new Thread(Work) { IsBackground = true, Priority = ThreadPriority.BelowNormal, Name = "SatelliteMap tiles " + i }; workers[i].Start(); } } } private static void Work() { while (true) { Job job; lock (jobs) { while (jobs.Count == 0) { Monitor.Wait(jobs); } job = jobs.Dequeue(); } TileRenderer tileRenderer = renderer; if (job.Generation == generation && tileRenderer != null) { try { DateTime utcNow = DateTime.UtcNow; Dictionary<long, ZoneData> zones = job.Zones; job.Result = tileRenderer.Render(job.Spec, (int zx, int zy) => (!zones.TryGetValue(ZoneCache.Key(zx, zy), out var value)) ? null : value); job.Milliseconds = (DateTime.UtcNow - utcNow).TotalMilliseconds; if (SatelliteMapPlugin.DebugTiles.Value) { Dump(job); } } catch (Exception error) { job.Error = error; } } lock (finished) { finished.Add(job); } } } private static void Dump(Job job) { try { string text = Path.Combine(Paths.CachePath, "SatelliteMap", "tiles"); Directory.CreateDirectory(text); string path = "lod" + job.Tile.Lod + "_" + job.Tile.X + "_" + job.Tile.Z + ".png"; File.WriteAllBytes(Path.Combine(text, path), PngWriter.Encode(job.Result, job.Spec.Size, job.Spec.Size)); } catch (Exception) { } } private static void Upload(Minimap minimap) { List<Job> list = null; lock (finished) { if (finished.Count > 0) { list = new List<Job>(finished); finished.Clear(); } } if (list != null) { foreach (Job item in list) { queued = Mathf.Max(0, queued - 1); if (item.Generation != generation) { continue; } Tile tile = item.Tile; tile.InFlight = false; if (item.Error != null || item.Result == null) { if (tile.State != TileState.Ready) { tile.State = TileState.Failed; tile.RetryAt = Time.time + 30f; } SatelliteMapPlugin.Log.LogWarning((object)("Map tile " + tile.Lod + ":" + tile.X + "," + tile.Z + " failed: " + item.Error)); continue; } tile.Rgba = item.Result; tile.ZoneStamps = item.ZoneStamps; tile.ExploredVersion = -1; tile.State = TileState.Ready; if (SatelliteMapPlugin.DebugTiles.Value) { SatelliteMapPlugin.Log.LogInfo((object)("Map tile " + tile.Lod + ":" + tile.X + "," + tile.Z + " drawn in " + item.Milliseconds.ToString("F0") + " ms, zones " + item.Zones.Count)); } } } int num = 0; Explored explored = Explored.Of(minimap); int textureSize = minimap.m_textureSize; foreach (Tile value in tiles.Values) { if (value.Rgba != null && value.ExploredVersion != exploredVersion && !(Time.time - value.LastUsed > 1f)) { if (num++ >= 2) { break; } TileMask.Apply(value.Rgba, value.Spec, ((Explored)explored).At, textureSize, minimap.m_pixelSize); if ((Object)(object)value.Texture != (Object)null) { Object.Destroy((Object)(object)value.Texture); } value.Texture = TileLayer.CreateTexture(value.Rgba, 256); value.ExploredVersion = exploredVersion; } } } private static void Layout(Rect uv, Rect rect, float worldSize, int lod, float opacity) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) foreach (Tile value in tiles.Values) { bool flag = (Object)(object)value.Texture != (Object)null && value.ExploredVersion >= 0 && value.Lod >= lod; Rect placed = default(Rect); if (flag) { placed = TileLayer.Place(uv, rect, worldSize, value.Spec.OriginX, value.Spec.OriginZ, value.Spec.WorldSize); flag = TileLayer.Overlaps(placed, rect); } if (!flag) { layer.Hide(value.Image); value.Image = null; continue; } if (value.Lod > lod) { value.LastUsed = Time.time; } if (value.ShownAt < 0f) { value.ShownAt = Time.time; } float num = Mathf.Clamp01((Time.time - value.ShownAt) / 0.3f); value.Image = layer.Show(value.Image, value.Lod, value.Texture, placed, new Color(1f, 1f, 1f, opacity * num)); } } private static void Evict() { if (tiles.Count <= 120) { return; } List<KeyValuePair<long, Tile>> list = new List<KeyValuePair<long, Tile>>(tiles); list.Sort((KeyValuePair<long, Tile> a, KeyValuePair<long, Tile> b) => a.Value.LastUsed.CompareTo(b.Value.LastUsed)); for (int num = 0; num < list.Count; num++) { if (tiles.Count <= 120) { break; } Tile value = list[num].Value; if (!value.InFlight && !(Time.time - value.LastUsed < 2f)) { layer.Hide(value.Image); if ((Object)(object)value.Texture != (Object)null) { Object.Destroy((Object)(object)value.Texture); } tiles.Remove(list[num].Key); } } } } internal static class Snapshots { private const string Folder = "satellitemap"; private static SnapshotStore store; private static long storeWorld; private static long failedWorld; private static bool failed; internal static SnapshotStore Current() { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZNet.World == null) { return null; } long worldUID = ZNet.instance.GetWorldUID(); if (store != null && storeWorld == worldUID) { return store; } Close(); if (failed && failedWorld == worldUID) { return null; } try { string text = Path.Combine(Utils.GetSaveDataPath((FileSource)2), "satellitemap"); Directory.CreateDirectory(text); string text2 = Path.Combine(text, SafeName(ZNet.instance.GetWorldName()) + "." + worldUID.ToString("X") + ".zones"); store = new SnapshotStore(text2); storeWorld = worldUID; failed = false; SatelliteMapPlugin.Log.LogInfo((object)("Zone snapshots: " + text2 + ", " + store.Snapshots + " snapshots of " + store.Zones + " zones, " + store.Bytes / 1024 + " KB" + ((store.TornBytes > 0) ? (", dropped a torn tail of " + store.TornBytes + " bytes") : ""))); } catch (Exception ex) { store = null; failed = true; failedWorld = worldUID; SatelliteMapPlugin.Log.LogWarning((object)("Zone snapshots are off, maps will show the world as it is now: " + ex.Message)); } return store; } internal static void Flush() { if (store != null) { try { store.Flush(); } catch (Exception ex) { SatelliteMapPlugin.Log.LogWarning((object)("Could not write zone snapshots: " + ex.Message)); } } } internal static void Close() { if (store != null) { try { store.Dispose(); } catch (Exception ex) { SatelliteMapPlugin.Log.LogWarning((object)("Could not close the zone snapshot file: " + ex.Message)); } store = null; } } private static string SafeName(string name) { if (string.IsNullOrEmpty(name)) { return "world"; } char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); foreach (char oldChar in invalidFileNameChars) { name = name.Replace(oldChar, '_'); } return name; } } internal sealed class TileLayer { private readonly RectTransform root; private readonly RectTransform[] lods; private readonly Stack<RawImage> pool = new Stack<RawImage>(); internal bool Alive => (Object)(object)root != (Object)null; internal TileLayer(RectTransform mapImage, int lodCount) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Expected O, but got Unknown GameObject val = new GameObject("SatelliteMap", new Type[2] { typeof(RectTransform), typeof(RectMask2D) }); root = (RectTransform)val.transform; ((Transform)root).SetParent((Transform)(object)mapImage, false); ((Transform)root).SetAsFirstSibling(); Stretch(root); lods = (RectTransform[])(object)new RectTransform[lodCount]; for (int num = lodCount - 1; num >= 0; num--) { GameObject val2 = new GameObject("lod" + num, new Type[1] { typeof(RectTransform) }); lods[num] = (RectTransform)val2.transform; ((Transform)lods[num]).SetParent((Transform)(object)root, false); Stretch(lods[num]); } } internal void SetVisible(bool visible) { if ((Object)(object)root != (Object)null && ((Component)root).gameObject.activeSelf != visible) { ((Component)root).gameObject.SetActive(visible); } } internal void Destroy() { pool.Clear(); if ((Object)(object)root != (Object)null) { Object.Destroy((Object)(object)((Component)root).gameObject); } } internal static Texture2D CreateTexture(byte[] rgba, int size) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0023: 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) //IL_0033: Expected O, but got Unknown Texture2D val = new Texture2D(size, size, (TextureFormat)4, false, false) { filterMode = (FilterMode)1, wrapMode = (TextureWrapMode)1, name = "SatelliteMap tile" }; val.LoadRawTextureData(rgba); val.Apply(false, true); return val; } internal static Rect Place(Rect uv, Rect mapRect, float worldSize, float originX, float originZ, float tileWorld) { //IL_0071: Unknown result type (might be due to invalid IL or missing references) float num = worldSize / 2f; float num2 = ((originX + num) / worldSize - ((Rect)(ref uv)).xMin) / ((Rect)(ref uv)).width * ((Rect)(ref mapRect)).width; float num3 = ((originZ + num) / worldSize - ((Rect)(ref uv)).yMin) / ((Rect)(ref uv)).height * ((Rect)(ref mapRect)).height; float num4 = tileWorld / worldSize / ((Rect)(ref uv)).width * ((Rect)(ref mapRect)).width; float num5 = tileWorld / worldSize / ((Rect)(ref uv)).height * ((Rect)(ref mapRect)).height; return new Rect(num2, num3, num4, num5); } internal static bool Overlaps(Rect placed, Rect mapRect) { if (((Rect)(ref placed)).x < ((Rect)(ref mapRect)).width && ((Rect)(ref placed)).y < ((Rect)(ref mapRect)).height && ((Rect)(ref placed)).xMax > 0f) { return ((Rect)(ref placed)).yMax > 0f; } return false; } internal RawImage Show(RawImage image, int lod, Texture2D texture, Rect placed, Color tint) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)image == (Object)null) { image = ((pool.Count > 0) ? pool.Pop() : CreateImage()); ((Transform)((Graphic)image).rectTransform).SetParent((Transform)(object)lods[lod], false); ((Component)image).gameObject.SetActive(true); } if ((object)image.texture != texture) { image.texture = (Texture)(object)texture; } ((Graphic)image).color = tint; RectTransform rectTransform = ((Graphic)image).rectTransform; rectTransform.anchoredPosition = new Vector2(((Rect)(ref placed)).x - 0.25f, ((Rect)(ref placed)).y - 0.25f); rectTransform.sizeDelta = new Vector2(((Rect)(ref placed)).width + 0.5f, ((Rect)(ref placed)).height + 0.5f); return image; } internal void Hide(RawImage image) { if ((Object)(object)image != (Object)null) { ((Component)image).gameObject.SetActive(false); image.texture = null; pool.Push(image); } } private static RawImage CreateImage() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) RawImage component = new GameObject("tile", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(RawImage) }).GetComponent<RawImage>(); ((Graphic)component).raycastTarget = false; RectTransform rectTransform = ((Graphic)component).rectTransform; rectTransform.anchorMin = Vector2.zero; rectTransform.anchorMax = Vector2.zero; rectTransform.pivot = Vector2.zero; ((Transform)rectTransform).localScale = Vector3.one; return component; } private static void Stretch(RectTransform rt) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: 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_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) rt.anchorMin = Vector2.zero; rt.anchorMax = Vector2.one; rt.offsetMin = Vector2.zero; rt.offsetMax = Vector2.zero; ((Transform)rt).localScale = Vector3.one; } } internal static class ZoneFeed { private sealed class MapRequest { public readonly List<KeyValuePair<int, int>> Zones = new List<KeyValuePair<int, int>>(); public ZoneLayers Layers; public int[] Fingerprints; public int[] Revs; } private sealed class SurveyCall { public Action<int[], int[], int[]> Done; public float SentAt; } [CompilerGenerated] private static class <>O { public static CoroutineHandler <0>__OnServerReceive; public static CoroutineHandler <1>__OnClientReceive; public static CoroutineHandler <2>__OnServerSurvey; public static CoroutineHandler <3>__OnClientSurveyed; } private const string RpcName = "zones"; private const string SurveyRpcName = "survey"; private const byte RequestVersion = 1; private const byte SurveyVersion = 1; private const byte FingerprintMarker = 197; private const byte RevisionMarker = 199; internal const int MaxZonesPerRequest = 96; private const int MaxRequestsPerPeer = 2; private const int MaxSurveysPerPeer = 4; private const float SurveyTimeout = 30f; private const long SelfTestSender = -9223372036854775801L; private static readonly FieldRef<ZDOMan, List<ZDO>[]> SectorsRef = AccessTools.FieldRefAccess<ZDOMan, List<ZDO>[]>("m_objectsBySector"); private static readonly FieldRef<ZDOMan, Dictionary<SectorIndex, List<ZDO>>> PortalsRef = AccessTools.FieldRefAccess<ZDOMan, Dictionary<SectorIndex, List<ZDO>>>("m_portalObjects"); private static readonly FieldRef<ZDO, Vector3> RotationRef = AccessTools.FieldRefAccess<ZDO, Vector3>("m_rotation"); private static readonly int LocationProxyHash = StringExtensionMethods.GetStableHashCode("LocationProxy"); private static readonly int TerrainCompilerHash = StringExtensionMethods.GetStableHashCode("_TerrainCompiler"); private static CustomRPC rpc; private static CustomRPC surveyRpc; private static ZoneAssembler assembler; private static byte[] selfTestReply; private static readonly Dictionary<int, bool> foreignPieces = new Dictionary<int, bool>(); private static readonly Dictionary<long, int> activeRequests = new Dictionary<long, int>(); private static readonly Dictionary<long, int> activeSurveys = new Dictionary<long, int>(); private const double FrameBudgetMs = 4.0; private static int budgetFrame = -1; private static double budgetSpentMs; private static readonly Dictionary<int, SurveyCall> surveyCalls = new Dictionary<int, SurveyCall>(); private static int nextSurveyId; internal static bool IsLocal { get { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } internal static void RegisterRpc() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //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_006f: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown NetworkManager instance = NetworkManager.Instance; object obj = <>O.<0>__OnServerReceive; if (obj == null) { CoroutineHandler val = OnServerReceive; <>O.<0>__OnServerReceive = val; obj = (object)val; } object obj2 = <>O.<1>__OnClientReceive; if (obj2 == null) { CoroutineHandler val2 = OnClientReceive; <>O.<1>__OnClientReceive = val2; obj2 = (object)val2; } rpc = instance.AddRPC("zones", (CoroutineHandler)obj, (CoroutineHandler)obj2); NetworkManager instance2 = NetworkManager.Instance; object obj3 = <>O.<2>__OnServerSurvey; if (obj3 == null) { CoroutineHandler val3 = OnServerSurvey; <>O.<2>__OnServerSurvey = val3; obj3 = (object)val3; } object obj4 = <>O.<3>__OnClientSurveyed; if (obj4 == null) { CoroutineHandler val4 = OnClientSurveyed; <>O.<3>__OnClientSurveyed = val4; obj4 = (object)val4; } surveyRpc = instance2.AddRPC("survey", (CoroutineHandler)obj3, (CoroutineHandler)obj4); } internal static bool Request(List<long> zones, ZoneLayers layers, ZoneStore known, Dictionary<long, int> revs) { if (rpc == null || (Object)(object)ZNet.instance == (Object)null || ZRoutedRpc.instance == null || zones.Count == 0) { return false; } ZNetPeer serverPeer = ZNet.instance.GetServerPeer(); if (serverPeer == null) { return false; } rpc.SendPackage(serverPeer.m_uid, WriteRequest(zones, layers, known, revs)); return true; } private static ZPackage WriteRequest(List<long> zones, ZoneLayers layers, ZoneStore known, Dictionary<long, int> revs) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write((byte)1); val.Write((byte)layers); val.Write((ushort)zones.Count); int[] array = null; int[] array2 = null; for (int i = 0; i < zones.Count; i++) { long num = zones[i]; val.Write((short)(num >> 32)); val.Write((short)(num & 0xFFFFFFFFu)); if (revs != null && revs.TryGetValue(num, out var value) && value > 0) { (array2 ?? (array2 = new int[zones.Count]))[i] = value; continue; } ZoneData zoneData = known?.Peek(num); if (zoneData != null && zoneData.Rev == 0 && zoneData.Layers == layers && zoneData.ContentHash != 0) { (array ?? (array = new int[zones.Count]))[i] = zoneData.ContentHash; } } WriteTail(val, 197, array); WriteTail(val, 199, array2); return val; } private static void WriteTail(ZPackage pkg, byte marker, int[] values) { if (values != null) { pkg.Write(marker); foreach (int num in values) { pkg.Write(num); } } } private static IEnumerator OnClientReceive(long sender, ZPackage package) { try { ZoneCache.Accept(ZoneCodec.DecodePacket(package.ReadByteArray())); } catch (Exception ex) { SatelliteMapPlugin.Log.LogWarning((object)("Could not read zone descriptions: " + ex.Message)); } yield break; } private static bool BudgetLeft() { int frameCount = Time.frameCount; if (frameCount != budgetFrame) { budgetFrame = frameCount; budgetSpentMs = 0.0; } return budgetSpentMs < 4.0; } private static void Spend(long startedTicks) { budgetSpentMs += (double)(Stopwatch.GetTimestamp() - startedTicks) * 1000.0 / (double)Stopwatch.Frequency; } private static IEnumerator OnServerReceive(long sender, ZPackage package) { MapRequest request = TryReadRequest(package); if (request == null) { yield break; } activeRequests.TryGetValue(sender, out var value); if (value >= 2) { yield break; } activeRequests[sender] = value + 1; try { List<ZoneData> zones = new List<ZoneData>(request.Zones.Count); List<long> unchanged = new List<long>(); for (int i = 0; i < request.Zones.Count; i++) { while (!BudgetLeft()) { yield return null; } long timestamp = Stopwatch.GetTimestamp(); KeyValuePair<int, int> keyValuePair = request.Zones[i]; int num = ((request.Revs != null) ? request.Revs[i] : 0); ZoneData zoneData = TryBuild(keyValuePair.Key, keyValuePair.Value, request.Layers, num); if (zoneData != null && num <= 0 && request.Fingerprints != null && request.Fingerprints[i] != 0) { zoneData.ComputeHash(); if (zoneData.ContentHash == request.Fingerprints[i]) { unchanged.Add(ZoneStore.Key(keyValuePair.Key, keyValuePair.Value)); zoneData = null; } } if (zoneData != null) { zones.Add(zoneData); } Spend(timestamp); } long timestamp2 = Stopwatch.GetTimestamp(); Reply(sender, zones, unchanged); Spend(timestamp2); } finally { if (activeRequests.TryGetValue(sender, out var value2) && value2 > 1) { activeRequests[sender] = value2 - 1; } else { activeRequests.Remove(sender); } } } private static MapRequest TryReadRequest(ZPackage package) { try { if (package.ReadByte() != 1) { return null; } MapRequest mapRequest = new MapRequest(); mapRequest.Layers = (ZoneLayers)(package.ReadByte() & 7); int num = package.ReadUShort(); int num2 = Math.Min(num, 96); for (int i = 0; i < num2; i++) { int key = package.ReadShort(); int value = package.ReadShort(); mapRequest.Zones.Add(new KeyValuePair<int, int>(key, value)); } while (num == num2 && package.GetPos() < package.Size()) { byte b = package.ReadByte(); if (b != 197 && b != 199) { break; } int[] array = new int[num2]; for (int j = 0; j < num2; j++) { array[j] = package.ReadInt(); } if (b == 197) { mapRequest.Fingerprints = array; } else { mapRequest.Revs = array; } } return mapRequest; } catch (Exception ex) { SatelliteMapPlugin.Log.LogWarning((object)("Unreadable map request: " + ex.Message)); return null; } } private static ZoneData TryBuild(int zoneX, int zoneY, ZoneLayers layers, int rev) { try { if (rev > 0) { ZoneData zoneData = Snapshots.Current()?.Load(zoneX, zoneY, rev); if (zoneData != null) { ZoneData zoneData2 = Assembler().Filter(zoneData, layers); zoneData2.Rev = rev; return zoneData2; } } ZoneData zoneData3 = Build(zoneX, zoneY, layers); zoneData3.Rev = Math.Max(0, rev); return zoneData3; } catch (Exception ex) { SatelliteMapPlugin.Log.LogWarning((object)("Could not describe zone " + zoneX + "," + zoneY + " for the map: " + ex)); return null; } } private static ZoneAssembler Assembler() { return assembler ?? (assembler = new ZoneAssembler(SatGame.Shapes)); } private static void Reply(long sender, List<ZoneData> zones, List<long> unchanged) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown try { byte[] array = ZoneCodec.Encode(zones, unchanged); if (sender == -9223372036854775801L) { selfTestReply = array; return; } ZPackage val = new ZPackage(); val.Write(array); rpc.SendPackage(sender, val); } catch (Exception ex) { SatelliteMapPlugin.Log.LogWarning((object)("Could not send zone descriptions: " + ex)); } } internal static IEnumerator SelfTest(string spec) { List<long> keys = new List<long>(); Dictionary<long, int> direct = new Dictionary<long, int>(); try { string[] array = spec.Split(new char[1] { ',' }); float num = float.Parse(array[0], CultureInfo.InvariantCulture); float num2 = float.Parse(array[1], CultureInfo.InvariantCulture); float num3 = float.Parse(array[2], CultureInfo.InvariantCulture); DateTime utcNow = DateTime.UtcNow; List<ZoneData> list = new List<ZoneData>(); int num4 = 0; int num5 = 0; for (int i = SatConst.ZoneOf(num2 - num3); i <= SatConst.ZoneOf(num2 + num3); i++) { for (int j = SatConst.ZoneOf(num - num3); j <= SatConst.ZoneOf(num + num3); j++) { ZoneData zoneData = Build(j, i, ZoneLayers.All); num4 += zoneData.Objects.Count; num5 += ((zoneData.PaintDirt != null) ? 1 : 0); list.Add(zoneData); zoneData.ComputeHash(); keys.Add(ZoneStore.Key(j, i)); direct[ZoneStore.Key(j, i)] = zoneData.ContentHash; } } byte[] array2 = ZoneCodec.Encode(list); double totalMilliseconds = (DateTime.UtcNow - utcNow).TotalMilliseconds; string text = Path.Combine(Paths.CachePath, "SatelliteMap"); Directory.CreateDirectory(text); File.WriteAllBytes(Path.Combine(text, "selftest.bin"), array2); SatelliteMapPlugin.Log.LogInfo((object)("Self-test: " + list.Count + " zones, " + num4 + " objects, " + num5 + " zones with painted ground, packet " + array2.Length + " bytes, built in " + totalMilliseconds.ToString("F0") + " ms")); } catch (Exception ex) { SatelliteMapPlugin.Log.LogError((object)("Self-test failed: " + ex)); yield break; } ZoneStore received = new ZoneStore(); int requests = 0; int frames = 0; int mismatched = 0; int bytes = 0; double worstFrameMs = 0.0; for (int from = 0; from < keys.Count; from += 96) { ZPackage val = WriteRequest(keys.GetRange(from, Math.Min(96, keys.Count - from)), ZoneLayers.All, null, null); val.SetPos(0); selfTestReply = null; IEnumerator handler = OnServerReceive(-9223372036854775801L, val); while (true) { long timestamp = Stopwatch.GetTimestamp(); bool num6 = handler.MoveNext(); worstFrameMs = Math.Max(worstFrameMs, (double)(Stopwatch.GetTimestamp() - timestamp) * 1000.0 / (double)Stopwatch.Frequency); if (!num6) { break; } frames++; yield return handler.Current; } requests++; if (selfTestReply == null) { SatelliteMapPlugin.Log.LogError((object)("Self-test, request path: no reply to request " + requests)); yield break; } bytes += selfTestReply.Length; ZonePacket zonePacket; try { zonePacket = ZoneCodec.DecodePacket(selfTestReply); } catch (Exception ex2) { SatelliteMapPlugin.Log.LogError((object)("Self-test, request path: unreadable reply: " + ex2)); yield break; } foreach (ZoneData zone in zonePacket.Zones) { long key = ZoneStore.Key(zone.ZoneX, zone.ZoneY); if (!direct.TryGetValue(key, out var value) || value != zone.ContentHash) { mismatched++; } received.Put(zone, 0f); } } SatelliteMapPlugin.Log.LogInfo((object)("Self-test, request path: " + received.Count + " of " + keys.Count + " zones in " + requests + " requests, " + bytes + " bytes, spread over " + frames + " extra frames, worst slice " + worstFrameMs.ToString("F1") + " ms, fingerprint mismatches " + mismatched)); int unchanged = 0; int resent = 0; int repeatBytes = 0; for (int from = 0; from < keys.Count; from += 96) { ZPackage val2 = WriteRequest(keys.GetRange(from, Math.Min(96, keys.Count - from)), ZoneLayers.All, received, null); val2.SetPos(0); selfTestReply = null; IEnumerator handler = OnServerReceive(-9223372036854775801L, val2); while (handler.MoveNext()) { yield return handler.Current; } if (selfTestReply == null) { SatelliteMapPlugin.Log.LogError((object)"Self-test, repeat with fingerprints: no reply"); yield break; } repeatBytes += selfTestReply.Length; ZonePacket zonePacket2 = ZoneCodec.DecodePacket(selfTestReply); unchanged += zonePacket2.Unchanged.Count; resent += zonePacket2.Zones.Count; } SatelliteMapPlugin.Log.LogInfo((object)("Self-test, repeat with fingerprints: " + unchanged + " zones unchanged, " + resent + " sent again, " + repeatBytes + " bytes")); Dictionary<long, int> surveyed = new Dictionary<long, int>(); List<KeyValuePair<int, int>> list2 = new List<KeyValuePair<int, int>>(); foreach (long item in keys) { list2.Add(new KeyValuePair<int, int>((int)(item >> 32), (int)(item & 0xFFFFFFFFu))); } DateTime surveyStart = DateTime.UtcNow; IEnumerator surveying = SurveyZones(list2, delegate(int[] xs, int[] ys, int[] revs) { for (int k = 0; k < xs.Length; k++) { surveyed[ZoneStore.Key(xs[k], ys[k])] = revs[k]; } }); while (surveying.MoveNext()) { yield return surveying.Current; } SnapshotStore snapshots = Snapshots.Current(); if (snapshots == null || surveyed.Count != keys.Count) { SatelliteMapPlugin.Log.LogError((object)("Self-test, snapshots: " + surveyed.Count + " of " + keys.Count + " zones got a snapshot" + ((snapshots == null) ? " (the snapshot file is not open)" : ""))); yield break; } int wrongRev = 0; int wrongContent = 0; int snapshotBytes = 0; for (int from = 0; from < keys.Count; from += 96) { ZPackage val3 = WriteRequest(keys.GetRange(from, Math.Min(96, keys.Count - from)), ZoneLayers.All, null, surveyed); val3.SetPos(0); selfTestReply = null; IEnumerator handler = OnServerReceive(-9223372036854775801L, val3); while (handler.MoveNext()) { yield return handler.Current; } if (selfTestReply == null) { SatelliteMapPlugin.Log.LogError((object)"Self-test, snapshots: no reply"); yield break; } snapshotBytes += selfTestReply.Length; foreach (ZoneData zone2 in ZoneCodec.DecodePacket(selfTestReply).Zones) { long key2 = ZoneStore.Key(zone2.ZoneX, zone2.ZoneY); ZoneData zoneData2 = received.Peek(key2); wrongRev += ((zone2.Rev != surveyed[key2]) ? 1 : 0); wrongContent += ((zoneData2 == null || zoneData2.Objects.Count != zone2.Objects.Count || zoneData2.PaintDirt == null != (zone2.PaintDirt == null)) ? 1 : 0); } } SatelliteMapPlugin.Log.LogInfo((object)("Self-test, snapshots: " + surveyed.Count + " zones surveyed in " + (DateTime.UtcNow - surveyStart).TotalMilliseconds.ToString("F0") + " ms, the file holds " + snapshots.Snapshots + " snapshots of " + snapshots.Zones + " zones, " + snapshots.Bytes / 1024 + " KB; asked back by number: " + snapshotBytes + " bytes, wrong numbers " + wrongRev + ", content differs from the live zone in " + wrongContent)); } internal static ZoneData BuildLocal(int zoneX, int zoneY, ZoneLayers layers, int rev) { return TryBuild(zoneX, zoneY, layers, rev) ?? ZoneAssembler.Begin(zoneX, zoneY, layers); } internal static bool Survey(int[] zoneXs, int[] zoneYs, Action<int[], int[], int[]> done) { //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Expected O, but got Unknown if (zoneXs == null || zoneYs == null || zoneXs.Length != zoneYs.Length || zoneXs.Length == 0 || done == null || (Object)(object)ZNet.instance == (Object)null || (Object)(object)SatelliteMapPlugin.Instance == (Object)null) { return false; } if (IsLocal) { List<KeyValuePair<int, int>> list = new List<KeyValuePair<int, int>>(zoneXs.Length); for (int i = 0; i < zoneXs.Length; i++) { list.Add(new KeyValuePair<int, int>(zoneXs[i], zoneYs[i])); } ((MonoBehaviour)SatelliteMapPlugin.Instance).StartCoroutine(SurveyZones(list, done)); return true; } ZNetPeer serverPeer = ZNet.instance.GetServerPeer(); if (surveyRpc == null || serverPeer == null) { return false; } float time = Time.time; List<int> list2 = null; foreach (KeyValuePair<int, SurveyCall> surveyCall in surveyCalls) { if (time - surveyCall.Value.SentAt > 30f) { (list2 ?? (list2 = new List<int>())).Add(surveyCall.Key); } } if (list2 != null) { foreach (int item in list2) { surveyCalls.Remove(item); } } for (int j = 0; j < zoneXs.Length; j += 96) { int num = Math.Min(96, zoneXs.Length - j); int num2 = ++nextSurveyId; surveyCalls[num2] = new SurveyCall { Done = done, SentAt = time }; ZPackage val = new ZPackage(); val.Write((byte)1); val.Write(num2); val.Write((ushort)num); for (int k = j; k < j + num; k++) { val.Write((short)zoneXs[k]); val.Write((short)zoneYs[k]); } surveyRpc.SendPackage(serverPeer.m_uid, val); } return true; } internal static void ForgetSurveys() { surveyCalls.Clear(); } private static IEnumerator OnServerSurvey(long sender, ZPackage package) { int id = 0; List<KeyValuePair<int, int>> list = new List<KeyValuePair<int, int>>(); try { if (package.ReadByte() != 1) { yield break; } id = package.ReadInt(); int num = Math.Min((int)package.ReadUShort(), 96); for (int i = 0; i < num; i++) { int key = package.ReadShort(); int value = package.ReadShort(); list.Add(new KeyValuePair<int, int>(key, value)); } } catch (Exception ex) { SatelliteMapPlugin.Log.LogWarning((object)("Unreadable survey request: " + ex.Message)); yield break; } activeSurveys.TryGetValue(sender, out var value2); if (value2 >= 4) { yield break; } activeSurveys[sender] = value2 + 1; try { IEnumerator work = SurveyZones(list, delegate(int[] xs, int[] ys, int[] revs) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write((byte)1); val.Write(id); val.Write((ushort)xs.Length); for (int j = 0; j < xs.Length; j++) { val.Write((short)xs[j]); val.Write((short)ys[j]); val.Write(revs[j]); } surveyRpc.SendPackage(sender, val); }); while (work.MoveNext()) { yield return work.Current; } } finally { if (activeSurveys.TryGetValue(sender, out var value3) && value3 > 1) { activeSurveys[sender] = value3 - 1; } else { activeSurveys.Remove(sender); } } } private static IEnumerator SurveyZones(List<KeyValuePair<int, int>> zones, Action<int[], int[], int[]> done) { List<int> xs = new List<int>(zones.Count); List<int> ys = new List<int>(zones.Count); List<int> revs = new List<int>(zones.Count); int nowMinutes = (int)(DateTimeOffset.UtcNow.ToUnixTimeSeconds() / 60); for (int i = 0; i < zones.Count; i++) { while (!BudgetLeft()) { yield return null; } long timestamp = Stopwatch.GetTimestamp(); try { SnapshotStore snapshotStore = Snapshots.Current(); KeyValuePair<int, int> keyValuePair = zones[i]; if (snapshotStore != null && keyValuePair.Key > -256 && keyValuePair.Value > -256 && keyValuePair.Key < 256 && keyValuePair.Value < 256) { int item = snapshotStore.Survey(Build(keyValuePair.Key, keyValuePair.Value, ZoneLayers.All), nowMinutes); xs.Add(keyValuePair.Key); ys.Add(keyValuePair.Value); revs.Add(item); } } catch (Exception ex) { SatelliteMapPlugin.Log.LogWarning((object)("Could not take a snapshot of zone " + zones[i].Key + "," + zones[i].Value + ": " + ex.Message)); } Spend(timestamp); } Snapshots.Flush(); try { done(xs.ToArray(), ys.ToArray(), revs.ToArray()); } catch (Exception ex2) { SatelliteMapPlugin.Log.LogWarning((object)("Survey result was not accepted: " + ex2)); } } private static IEnumerator OnClientSurveyed(long sender, ZPackage package) { try { if (package.ReadByte() == 1) { int key = package.ReadInt(); int num = package.ReadUShort(); int[] array = new int[num]; int[] array2 = new int[num]; int[] array3 = new int[num]; for (int i = 0; i < num; i++) { array[i] = package.ReadShort(); array2[i] = package.ReadShort(); array3[i] = package.ReadInt(); } if (surveyCalls.TryGetValue(key, out var value)) { surveyCalls.Remove(key); value.Done(array, array2, array3); } } } catch (Exception ex) { SatelliteMapPlugin.Log.LogWarning((object)("Could not read the survey reply: " + ex)); } yield break; } private static ZoneData Build(int zoneX, int zoneY, ZoneLayers layers) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) ZoneData zoneData = ZoneAssembler.Begin(zoneX, zoneY, layers); ZDOMan instance = ZDOMan.instance; if (instance == null || zoneX <= -256 || zoneY <= -256 || zoneX >= 256 || zoneY >= 256) { return zoneData; } if (assembler == null) { assembler = new ZoneAssembler(SatGame.Shapes); } SectorIndex val = ZoneSystem.SectorToIndex(zoneX, zoneY); List<ZDO>[] array = SectorsRef.Invoke(instance); if (array != null && val.Sector < array.Length) { AddAll(zoneData, array[val.Sector]); } Dictionary<SectorIndex, List<ZDO>> dictionary = PortalsRef.Invoke(instance); if (dictionary != null && dictionary.TryGetValue(val, out var value)) { AddAll(zoneData, value); } return zoneData; } private static void AddAll(ZoneData zone, List<ZDO> objects) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009f: 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_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) if (objects == null) { return; } bool flag = (zone.Layers & ZoneLayers.Structures) != 0; for (int i = 0; i < objects.Count; i++) { ZDO val = objects[i]; if (val == null || !val.IsValid()) { continue; } int prefab = val.GetPrefab(); Vector3 position = val.GetPosition(); if (prefab == TerrainCompilerHash) { if (flag) { ReadTerrain(zone, val); } continue; } float y = RotationRef.Invoke(val).y; if (prefab == LocationProxyHash) { int num = val.GetInt(ZDOVars.s_location, 0); if (num != 0) { assembler.Add(zone, num, isLocation: true, position.x, position.y, position.z, y, 1f); } } else if (assembler.Knows(prefab)) { assembler.Add(zone, prefab, isLocation: false, position.x, position.y, position.z, y, ScaleOf(val)); } else if (flag && IsForeignPiece(prefab)) { assembler.AddUnknown(zone, prefab, position.x, position.y, position.z, y, ScaleOf(val)); } } } private static void ReadTerrain(ZoneData zone, ZDO zdo) { try { zone.ReadTerrainComp(zdo.GetByteArray(ZDOVars.s_TCData, (byte[])null)); } catch (Exception ex) { SatelliteMapPlugin.Log.LogWarning((object)("Terrain data of zone " + zone.ZoneX + "," + zone.ZoneY + " is unreadable: " + ex.Message)); } } private static float ScaleOf(ZDO zdo) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: 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) //IL_001e: Unknown result type (might be due to invalid IL or missing references) Vector3 vec = zdo.GetVec3(ZDOVars.s_scaleHash, Vector3.zero); if (vec != Vector3.zero) { return vec.x; } return zdo.GetFloat(ZDOVars.s_scaleScalarHash, 1f); } private static bool IsForeignPiece(int prefab) { if (foreignPieces.TryGetValue(prefab, out var value)) { return value; } value = false; try { value = SatGame.IsForeignPiece(prefab); } catch (Exception) { } foreignPieces[prefab] = value; return value; } } internal static class ZoneCache { private const float RequestTimeout = 12f; private const float ProbeEvery = 60f; private const int LocalZonesPerFrame = 24; private static readonly ZoneStore store = new ZoneStore(); private static readonly ZoneWants wants = new ZoneWants(); private static readonly HashSet<long> inFlight = new HashSet<long>(); private static readonly Dictionary<long, int> wantedRevs = new Dictionary<long, int>(); private static readonly Dictionary<long, int> inFlightRevs = new Dictionary<long, int>(); private static float inFlightSince; private static float lastProbe; private static int timeouts; private static long probeKey; private static bool hasProbeKey; internal static bool ServerSilent { get; private set; } internal static long Key(int zoneX, int zoneY) { return ZoneStore.Key(zoneX, zoneY); } internal static void Reset() { store.Clear(); wants.Clear(); inFlight.Clear(); wantedRevs.Clear(); inFlightRevs.Clear(); ZoneFeed.ForgetSurveys(); timeouts = 0; hasProbeKey = false; ServerSilent = false; } internal static ZoneData Get(int zoneX, int zoneY, ZoneLayers layers, float maxAge, float priority, int rev, out int zoneStamp) { long key = Key(zoneX, zoneY); float time = Time.time; rev = Math.Max(0, rev); float age; ZoneData zoneData = store.Find(key, time, out age); if (ServerSilent) { probeKey = key; hasProbeKey = true; if (zoneData == null) { zoneData = ZoneAssembler.Begin(zoneX, zoneY, ZoneLayers.All); store.Put(zoneData, time); } zoneStamp = ZoneStore.StampOf(zoneData); return zoneData; } if (zoneData != null && (zoneData.Layers & layers) == layers && zoneData.Rev == rev) { zoneStamp = ZoneStore.StampOf(zoneData); if (rev == 0 && age > maxAge) { Want(key, layers, priority, time, rev); } return zoneData; } zoneStamp = 0; Want(key, layers, priority, time, rev); return null; } private static void Want(long key, ZoneLayers layers, float priority, float now, int rev) { if (!inFlight.Contains(key)) { wants.Add(key, layers, priority, now); if (rev > 0) { wantedRevs[key] = rev; } else { wantedRevs.Remove(key); } } } internal static void Tick() { float time = Time.time; if (inFlight.Count > 0 && time - inFlightSince > 12f) { inFlight.Clear(); inFlightRevs.Clear(); if (!ServerSilent && ++timeouts >= 2) { ServerSilent = true; lastProbe = time; wants.Clear(); SatelliteMapPlugin.Log.LogWarning((object)"The server does not answer map requests (SatelliteMap is not installed there?). The map stays detailed, but without buildings, paths and forest."); } } store.Trim(time); if (ServerSilent) { Probe(time); } else { if (wants.Count == 0 || inFlight.Count > 0) { return; } bool isLocal = ZoneFeed.IsLocal; ZoneLayers layers; List<long> list = wants.Take(isLocal ? 24 : 96, time, out layers); if (list.Count == 0) { return; } inFlightRevs.Clear(); foreach (long item in list) { if (wantedRevs.TryGetValue(item, out var value)) { inFlightRevs[item] = value; wantedRevs.Remove(item); } } if (isLocal) { ZonePacket zonePacket = new ZonePacket(); foreach (long item2 in list) { inFlightRevs.TryGetValue(item2, out var value2); zonePacket.Zones.Add(ZoneFeed.BuildLocal((int)(item2 >> 32), (int)(item2 & 0xFFFFFFFFu), layers, value2)); } Accept(zonePacket); } else { if (!ZoneFeed.Request(list, layers, store, inFlightRevs)) { return; } foreach (long item3 in list) { inFlight.Add(item3); } inFlightSince = time; } } } private static void Probe(float now) { if (hasProbeKey && inFlight.Count <= 0 && !(now - lastProbe < 60f) && !ZoneFeed.IsLocal) { lastProbe = now; if (ZoneFeed.Request(new List<long> { probeKey }, ZoneLayers.All, null, null)) { inFlight.Add(probeKey); inFlightSince = now; } } } internal static void Accept(ZonePacket packet) { float time = Time.time; timeouts = 0; inFlight.Clear(); if (ServerSilent) { ServerSilent = false; store.Clear(); SatelliteMapPlugin.Log.LogInfo((object)"The server answers map requests again: buildings, paths and forest are back on the map."); } foreach (ZoneData zone in packet.Zones) { if (zone.ContentHash == 0) { zone.ComputeHash(); } if (zone.Rev == 0 && inFlightRevs.TryGetValue(ZoneStore.Key(zone.ZoneX, zone.ZoneY), out var value)) { zone.Rev = value; } store.Put(zone, time); } inFlightRevs.Clear(); foreach (long item in packet.Unchanged) { store.Confirm(item, time); } } } } namespace SatelliteMap.Render { public struct DetailSpec { public bool Enabled; public float FinestMetersPerPixel; public ZoneLayers Layers; } public static class MapDetail { public const int None = 0; public const int Max = 3; public static readonly float[] Resolutions = new float[4] { 0.5f, 1f, 2f, 4f }; public const float FadeOutStart = 3200f; public const float FadeOutEnd = 3900f; public static DetailSpec ForLevel(int level) { return level switch { 1 => new DetailSpec { Enabled = true, FinestMetersPerPixel = 2f, Layers = ZoneLayers.Structures }, 2 => new DetailSpec { Enabled = true, FinestMetersPerPixel = 1f, Layers = (ZoneLayers.Structures | ZoneLayers.Trees) }, 3 => new DetailSpec { Enabled = true, FinestMetersPerPixel = 0.5f, Layers = ZoneLayers.All }, _ => new DetailSpec { Enabled = false, FinestMetersPerPixel = 0f, Layers = ZoneLayers.None }, }; } public static float ResolutionFor(DetailSpec spec, float screenMetersPerPixel) { if (!spec.Enabled) { return 0f; } float num = Resolutions[0]; for (int i = 0; i < Resolutions.Length; i++) { if (Resolutions[i] <= screenMetersPerPixel * 1.5f) { num = Resolutions[i]; } } if (!(num < spec.FinestMetersPerPixel)) { return num; } return spec.FinestMetersPerPixel; } public static float Opacity(float viewHeightMeters) { float num = (3900f - viewHeightMeters) / 700f; if (!(num < 0f)) { if (!(num > 1f)) { return num; } return 1f; } return 0f; } public static float Coarser(float metersPerPixel) { for (int i = 0; i < Resolutions.Length - 1; i++) { if (Resolutions[i] >= metersPerPixel * 0.99f) { return Resolutions[i + 1]; } } return 0f; } public static ZoneLayers LayersFor(DetailSpec spec, float metersPerPixel) { return (ZoneLayers)(((metersPerPixel <= 0.5f) ? 7 : 3) & (int)spec.Layers); } } internal static class ObjectPainter { private struct Placed { public ZoneObject Object; public Shape Shape; public float Top; } public static void Paint(ShapeLibrary shapes, TileSpec spec, Func<int, int, ZoneData> zones, TerrainField field, float minX, float minZ, int w, float mpp, float[] surface, float[] shade, float[] cr, float[] cg, float[] cb) { float num = minX + (float)w * mpp; float num2 = minZ + (float)w * mpp; TileRenderer.ZoneRange(spec, out var minZoneX, out var minZoneY, out var maxZoneX, out var maxZoneY); List<Placed> list = new List<Placed>(); for (int i = minZoneY; i <= maxZoneY; i++) { for (int j = minZoneX; j <= maxZoneX; j++) { ZoneData zoneData = zones(j, i); if (zoneData == null) { continue; } foreach (ZoneObject @object in zoneData.Objects) { if (@object.Y > 1000f) { continue; } Shape shape = shapes.Find(@object.Prefab, @object.IsLocation); if (shape != null) { float num3 = ((@object.Scale > 0f) ? @object.Scale : 1f); float num4 = shape.Radius * num3; if ((ShapeLibrary.LayerOf(shape.Kind, num4) & spec.Layers) != ZoneLayers.None && !(@object.X + num4 < minX) && !(@object.X - num4 > num) && !(@object.Z + num4 < minZ) && !(@object.Z - num4 > num2)) { list.Add(new Placed { Object = @object, Shape = shape, Top = @object.Y + shape.MaxTop * num3 }); } } } } } list.Sort((Placed a, Placed b) => a.Top.CompareTo(b.Top)); foreach (Placed item in list) { if (!IsStructure(item.Shape)) { Draw(item.Object, item.Shape, minX, minZ, w, mpp, surface, surface, shade, cr, cg, cb); } } float[] array = null; foreach (Placed item2 in list) { if (!IsStructure(item2.Shape)) { continue; } if (array == null) { array = new float[surface.Length]; for (int num5 = 0; num5 < array.Length; num5++) { array[num5] = float.MinValue; } } Draw(item2.Object, item2.Shape, minX, minZ, w, mpp, array, surface, shade, cr, cg, cb); } } private static bool IsStructure(Shape shape) { if (shape.Kind != ShapeKind.Piece) { return shape.Kind == ShapeKind.Location; } return true; } private static void Draw(ZoneObject o, Shape shape, float minX, float minZ, int w, float mpp, float[] depth, float[] surface, float[] shade, float[] cr, float[] cg, float[] cb) { float num = ((o.Scale > 0f) ? o.Scale : 1f); ShapeLevel shapeLevel = shape.LevelFor(mpp / num); float num2 = shape.Radius * num; int num3 = Math.Max(0, (int)Math.Floor((o.X - num2 - minX) / mpp)); int num4 = Math.Min(w - 1, (int)Math.Ceiling((o.X + num2 - minX) / mpp)); int num5 = Math.Max(0, (int)Math.Floor((o.Z - num2 - minZ) / mpp)); int num6 = Math.Min(w - 1, (int)Math.Ceiling((o.Z + num2 - minZ) / mpp)); if (num4 < num3 || num6 < num5) { return; } double num7 = (double)o.Yaw * Math.PI / 180.0; float num8 = (float)Math.Cos(num7); float num9 = (float)Math.Sin(num7); float num10 = shapeLevel.PixelsPerMeter / num; float num11 = SatStyle.LightX * num8 - SatStyle.LightZ * num9; float num12 = SatStyle.LightX * num9 + SatStyle.LightZ * num8; float num13 = num / shapeLevel.PixelsPerMeter; float num14 = ((shape.Kind == ShapeKind.Tree || shape.Kind == ShapeKind.Small) ? 1.25f : 1f); for (int i = num5; i <= num6; i++) { float num15 = minZ + ((float)i + 0.5f) * mpp - o.Z; for (int j = num3; j <= num4; j++) { float num16 = minX + ((float)j + 0.5f) * mpp - o.X; float num17 = (num16 * num8 - num15 * num9) * num10 + shapeLevel.OriginX - 0.5f; float num18 = (num16 * num9 + num15 * num8) * num10 + shapeLevel.OriginZ - 0.5f; if (num17 < -1f || num18 < -1f || num17 > (float)shapeLevel.Width || num18 > (float)shapeLevel.Height) { continue; } int num19 = (int)Math.Floor(num17); int num20 = (int)Math.Floor(num18); float num21 = num17 - (float)num19; float num22 = num18 - (float)num20; float num23 = 0f; float num24 = 0f; float num25 = 0f; float num26 = 0f; float num27 = 0f; float num28 = 0f; float num29 = 0f; float num30 = 0f; float num31 = 0f; float num32 = 0f; float num33 = 0f; float num34 = 0f; float num35 = 0f; for (int k = 0; k < 4; k++) { int num36 = num19 + (k & 1); int num37 = num20 + (k >> 1); if (num36 < 0 || num37 < 0 || num36 >= shapeLevel.Width || num37 >= shapeLevel.Height) { continue; } int num38 = num37 * shapeLevel.Width + num36; float num39 = (int)shapeLevel.Rgba[num38 * 4 + 3]; if (!(num39 <= 0f)) { float num40 = (((k & 1) == 0) ? (1f - num21) : num21) * ((k >> 1 == 0) ? (1f - num22) : num22) * num39; num23 += num40; num24 += (float)(int)shapeLevel.Rgba[num38 * 4] * num40; num25 += (float)(int)shapeLevel.Rgba[num38 * 4 + 1] * num40; num26 += (float)(int)shapeLevel.Rgba[num38 * 4 + 2] * num40; num27 += shapeLevel.Top[num38] * num40; if ((k & 1) == 0) { num28 += shapeLevel.Top[num38] * num40; num32 += num40; } else { num29 += shapeLevel.Top[num38] * num40; num33 += num40; } if (k >> 1 == 0) { num30 += shapeLevel.Top[num38] * num40; num34 += num40; } else { num31 += shapeLevel.Top[num38] * num40; num35 += num40; } } } if (num23 < 20f) { continue; } float num41 = num23 / 255f; float num42 = o.Y + num27 / num23 * num; int num43 = i * w + j; if (num42 < depth[num43] - 0.05f) { continue; } float num44 = num14 / (num23 * 255f); float num45 = 1f; if (num32 > 0f && num33 > 0f && num34 > 0f && num35 > 0f) { float num46 = (num29 / num33 - num28 / num32) * num / num13; float num47 = (num31 / num35 - num30 / num34) * num / num13; float num48 = (float)Math.Sqrt(num46 * num46 + num47 * num47); if (num48 > 2.5f) { num46 *= 2.5f / num48; num47 *= 2.5f / num48; } float num49 = 0f - num46; float num50 = 0f - num47; float num51 = 1f / (float)Math.Sqrt(num49 * num49 + 1f + num50 * num50); num45 = 1f + 0.9f * ((num49 * num11 + SatStyle.LightY + num50 * num12) * num51 - SatStyle.LightY); num45 = ((num45 < 0.55f) ? 0.55f : ((num45 > 1.3f) ? 1.3f : num45)); } float num52 = ((num41 > 1f) ? 1f : num41); cr[num43] += (num24 * num44 - cr[num43]) * num52; cg[num43] += (num25 * num44 - cg[num43]) * num52; cb[num43] += (num26 * num44 - cb[num43]) * num52; shade[num43] += (num45 - shade[num43]) * num52; if (num41 > 0.5f) { depth[num43] = num42; surface[num43] = num42; } } } } } public static class PngWriter { private static uint[] crcTable; public static byte[] Encode(byte[] rgba, int width, int height) { using MemoryStream memoryStream = new MemoryStream(); memoryStream.Write(new byte[8] { 137, 80, 78, 71, 13, 10, 26, 10 }, 0, 8); byte[] array = new byte[13]; WriteBE(array, 0, width); WriteBE(array, 4, height); array[8] = 8; array[9] = 6; Chunk(memoryStream, "IHDR", array); using (MemoryStream memoryStream2 = new MemoryStream()) { memoryStream2.WriteByte(120); memoryStream2.WriteByte(156); uint num = 1u; uint num2 = 0u; using (DeflateStream deflateStream = new DeflateStream(memoryStream2, CompressionLevel.Fastest, leaveOpen: true)) { byte[] array2 = new byte[width * 4 + 1]; for (int num3 = height - 1; num3 >= 0; num3--) { Buffer.BlockCopy(rgba, num3 * width * 4, array2, 1, width * 4); deflateStream.Write(array2, 0, array2.Length); for (int i = 0; i < array2.Length; i++) { num = (num + array2[i]) % 65521; num2 = (num2 + num) % 65521; } } } byte[] array3 = new byte[4]; WriteBE(array3, 0, (int)((num2 << 16) | num)); memoryStream2.Write(array3, 0, 4); Chunk(memoryStream, "IDAT", memoryStream2.ToArray()); } Chunk(memoryStream, "IEND", new byte[0]); return memoryStream.ToArray(); } private static void Chunk(Stream png, string type, byte[] data) { byte[] array = new byte[4]; WriteBE(array, 0, data.Length); png.Write(array, 0, 4); byte[] array2 = new byte[4 + data.Length]; for (int i = 0; i < 4; i++) { array2[i] = (byte)type[i]; } Buffer.BlockCopy(data, 0, array2, 4, data.Length); png.Write(array2, 0, array2.Length); byte[] array3 = new byte[4]; WriteBE(array3, 0, (int)Crc(array2)); png.Write(array3, 0, 4); } private static uint Crc(byte[] bytes) { if (crcTable == null) { uint[] array = new uint[256]; for (uint num = 0u; num < 256; num++) { uint num2 = num; for (int i = 0; i < 8; i++) { num2 = (((num2 & 1) != 0) ? (0xEDB88320u ^ (num2 >> 1)) : (num2 >> 1)); } array[num] = num2; } crcTable = array; } uint num3 = uint.MaxValue; for (int j = 0; j < bytes.Length; j++) { num3 = crcTable[(num3 ^ bytes[j]) & 0xFF] ^ (num3 >> 8); } return num3 ^ 0xFFFFFFFFu; } private static void WriteBE(byte[] target, int offset, int value) { target[offset] = (byte)(value >> 24); target[offset + 1] = (byte)(value >> 16); target[offset + 2] = (byte)(value >> 8); target[offset + 3] = (byte)value; } } public struct Rgb { public float R; public float G; public float B; public Rgb(float r, float g, float b) { R = r; G = g; B = b; } public static Rgb Bytes(int r, int g, int b) { return new Rgb((float)r / 255f, (float)g / 255f, (float)b / 255f); } public static Rgb Lerp(Rgb a, Rgb b, float t) { t = ((t < 0f) ? 0f : ((t > 1f) ? 1f : t)); return new Rgb(a.R + (b.R - a.R) * t, a.G + (b.G - a.G) * t, a.B + (b.B - a.B) * t); } public Rgb Scale(float k) { return new Rgb(R * k, G * k, B * k); } } public static class SatStyle { public static readonly Rgb MeadowsLow = Rgb.Bytes(96, 132, 58); public static readonly Rgb MeadowsHigh = Rgb.Bytes(128, 146, 72); public static readonly Rgb ForestLow = Rgb.Bytes(58, 86, 46); public static readonly Rgb ForestHigh = Rgb.Bytes(78, 98, 56); public static readonly Rgb SwampLow = Rgb.Bytes(70, 74, 44); public static readonly Rgb SwampHigh = Rgb.Bytes(86, 88, 52); public static readonly Rgb Snow = Rgb.Bytes(232, 238, 244); public static readonly Rgb SnowShade = Rgb.Bytes(196, 208, 224); public static readonly Rgb PlainsLow = Rgb.Bytes(168, 156, 84); public static readonly Rgb PlainsHigh = Rgb.Bytes(186, 170, 100); public static readonly Rgb MistLow = Rgb.Bytes(62, 66, 74); public static readonly Rgb MistHigh = Rgb.Bytes(84, 80, 98); public static readonly Rgb AshLow = Rgb.Bytes(84, 58, 50); public static readonly Rgb AshHigh = Rgb.Bytes(112, 72, 58); public static readonly Rgb Lava = Rgb.Bytes(236, 104, 24); public static readonly Rgb NorthLow = Rgb.Bytes(214, 226, 238); public static readonly Rgb NorthHigh = Rgb.Bytes(240, 244, 248); public static readonly Rgb SeaBed = Rgb.Bytes(150, 146, 112); public static readonly Rgb Rock = Rgb.Bytes(116, 112, 104); public static readonly Rgb RockDark = Rgb.Bytes(84, 82, 80); public static readonly Rgb Sand = Rgb.Bytes(182, 170, 128); public static readonly Rgb Dirt = Rgb.Bytes(126, 100, 66); public static readonly Rgb Paved = Rgb.Bytes(146, 144, 138); public static readonly Rgb Cultivated = Rgb.Bytes(88, 66, 44); public static readonly Rgb WaterShallow = Rgb.Bytes(96, 158, 160); public static readonly Rgb WaterMid = Rgb.Bytes(44, 100, 136); public static readonly Rgb WaterDeep = Rgb.Bytes(22, 52, 92); public static readonly Rgb WaterSwamp = Rgb.Bytes(66, 76, 48); public static readonly Rgb Foam = Rgb.Bytes(226, 236, 232); public static readonly float LightX = -0.375f; public static readonly float LightY = 0.848f; public static readonly float LightZ = 0.375f; public const float ReliefExaggeration = 1.5f; public const float ReliefContrast = 1.25f; public const float ShadeMin = 0.66f; public const float ShadeMax = 1.3f; public const float ShadeSideSoftening = 0.65f; public const float ShadowReach = 16f; public const float ShadowDarkness = 0.3f; public const float ShadowSoftness = 1.2f; public const float FoliageGain = 1.25f; public static float Hash(int x, int z) { int num = x * 374761393 + z * 668265263; int num2 = (num ^ (num >>> 13)) * 1274126177; return (float)(uint)((num2 ^ (num2 >>> 16)) & 0xFFFF) / 65535f; } public static float ValueNoise(float x, float z) { int num = (int)Math.Floor(x); int num2 = (int)Math.Floor(z); float num3 = x - (float)num; float num4 = z - (float)num2; num3 = num3 * num3 * (3f - 2f * num3); num4 = num4 * num4 * (3f - 2f * num4); float num5 = Hash(num, num2); float num6 = Hash(num + 1, num2); float num7 = Hash(num, num2 + 1); float num8 = Hash(num + 1, num2 + 1); return (num5 + (num6 - num5) * num3) * (1f - num4) + (num7 + (num8 - num7) * num3) * num4; } public static Rgb Ground(int biome, float height, float slope, float mottle, float lava) { float num = height - 30f; float t = Clamp01(num / 40f); float num2 = 0.75f; float num3 = 1.5f; Rgb a; switch (biome) { case 1: a = Rgb.Lerp(MeadowsLow, MeadowsHigh, t); break; case 8: a = Rgb.Lerp(ForestLow, ForestHigh, t); break; case 2: a = Rgb.Lerp(SwampLow, SwampHigh, Clamp01(num / 6f)); break; case 4: a = Rgb.Lerp(SnowShade, Snow, Clamp01((num - 50f) / 60f)); num2 = 0.9f; num3 = 1.6f; break; case 16: a = Rgb.Lerp(PlainsLow, PlainsHigh, t); break; case 512: a = Rgb.Lerp(MistLow, MistHigh, t); num2 = 0.6f; break; case 32: a = Rgb.Lerp(AshLow, AshHigh, t); a = Rgb.Lerp(a, Lava, Clamp01(lava * 1.4f)); break; case 64: a = Rgb.Lerp(NorthLow, NorthHigh, t); break; default: a = SeaBed; break; } float num4 = Clamp01((slope - num2) / (num3 - num2)); if (num4 > 0f) { a = Rgb.Lerp(a, Rgb.Lerp(Rock, RockDark, mottle), num4); } if (biome != 2 && biome != 64 && biome != 4 && biome != 32) { float num5 = 1f - Clamp01((num - 0.3f) / 1.1f); a = Rgb.Lerp(a, Sand, num5 * 0.85f); } return a.Scale(0.9f + mottle * 0.2f); } public static float Clamp01(float v) { if (!(v < 0f)) { if (!(v > 1f)) { return v; } return 1f; } return 0f; } } public enum ShapeKind : byte { Piece, Tree, Rock, Small, Decor, Location } public sealed class ShapeLevel { public int Width; public int Height; public float PixelsPerMeter; public float OriginX; public float OriginZ; public byte[] Rgba; public float[] Top; } public sealed class Shape { public int Hash; public ShapeKind Kind; public float Radius; public float MaxTop; internal int Width; internal int Height; internal float PixelsPerMeter; internal float OriginX; internal float OriginZ; internal float MinHeight; internal float HeightStep; internal byte[] Index; internal byte[] HeightQ; internal ShapeLibrary Owner; private ShapeLevel[] levels; public ShapeLevel LevelFor(float metersPerTexel) { ShapeLevel[] array = Levels(); int num = 0; for (int i = 1; i < array.Length; i++) { if (1f / array[i].PixelsPerMeter <= metersPerTexel * 1.01f) { num = i; } } return array[num]; } private ShapeLevel[] Levels() { if (levels != null) { return levels; } lock (this) { if (levels != null) { return levels; } ShapeLevel shapeLevel = new ShapeLevel { Width = Width, Height = Height, PixelsPerMeter = PixelsPerMeter, OriginX = OriginX, OriginZ = OriginZ, Rgba = new byte[Width * Height * 4], Top = new float[Width * Height] }; byte[] palette = Owner.Palette; for (int i = 0; i < Width * Height; i++) { int num = Index[i]; if (num != 0) { int num2 = (num - 1) * 3; shapeLevel.Rgba[i * 4] = palette[num2]; shapeLevel.Rgba[i * 4 + 1] = palette[num2 + 1]; shapeLevel.Rgba[i * 4 + 2] = palette[num2 + 2]; shapeLevel.Rgba[i * 4 + 3] = byte.MaxValue; shapeLevel.Top[i] = MinHeight + (float)(HeightQ[i] - 1) * HeightStep; } } Index = null; HeightQ = null; levels = Chain(shapeLevel); return levels; } } private static ShapeLevel[] Chain(ShapeLevel level) { List<ShapeLevel> list = new List<ShapeLevel> { level }; while (level.Width > 2 && level.Height > 2 && list.Count < 6) { level = Halve(level); list.Add(level); } return list.ToArray(); } public static Shape Box(int hash, float minX, float minZ, float maxX, float maxZ, float top, byte r, byte g, byte b) { int num = Math.Max(1, (int)Math.Ceiling((maxX - minX) * 4f)); int num2 = Math.Max(1, (int)Math.Ceiling((maxZ - minZ) * 4f)); ShapeLevel shapeLevel = new ShapeLevel { Width = num, Height = num2, PixelsPerMeter = 4f, OriginX = (0f - minX) * 4f, OriginZ = (0f - minZ) * 4f, Rgba = new byte[num * num2 * 4], Top = new float[num * num2] }; for (int i = 0; i < num * num2; i++) { shapeLevel.Rgba[i * 4] = r; shapeLevel.Rgba[i * 4 + 1] = g; shapeLevel.Rgba[i * 4 + 2] = b; shapeLevel.Rgba[i * 4 + 3] = byte.MaxValue; shapeLevel.Top[i] = top; } float num3 = Math.Max(Math.Abs(minX), Math.Abs(maxX)); float num4 = Math.Max(Math.Abs(minZ), Math.Abs(maxZ)); return new Shape { Hash = hash, Kind = ShapeKind.Piece, Radius = (float)Math.Sqrt(num3 * num3 + num4 * num4), MaxTop = top, levels = Chain(shapeLevel) }; } private static ShapeLevel Halve(ShapeLevel src) { ShapeLevel shapeLevel = new ShapeLevel { Width = (src.Width + 1) / 2, Height = (src.Height + 1) / 2, PixelsPerMeter = src.PixelsPerMeter / 2f, OriginX = src.OriginX / 2f, OriginZ = src.OriginZ / 2f }; shapeLevel.Rgba = new byte[shapeLevel.Width * shapeLevel.Height * 4]; shapeLevel.Top = new float[shapeLevel.Width * shapeLevel.Height]; for (int i = 0; i < shapeLevel.Height; i++) { for (int j = 0; j < shapeLevel.Width; j++) { float num = 0f; float num2 = 0f; float num3 = 0f; float num4 = 0f; float num5 = 0f; for (int k = 0; k < 2; k++) { for (int l = 0; l < 2; l++) { int num6 = j * 2 + l; int num7 = i * 2 + k; if (num6 < src.Width && num7 < src.Height) { int num8 = num7 * src.Width + num6; float num9 = (int)src.Rgba[num8 * 4 + 3]; num += (float)(int)src.Rgba[num8 * 4] * num9; num2 += (float)(int)src.Rgba[num8 * 4 + 1] * num9; num3 += (float)(int)src.Rgba[num8 * 4 + 2] * num9; num5 += src.Top[num8] * num9; num4 += num9; } } } int num10 = i * shapeLevel.Width + j; if (num4 > 0f) { shapeLevel.Rgba[num10 * 4] = (byte)(num / num4 + 0.5f); shapeLevel.Rgba[num10 * 4 + 1] = (byte)(num2 / num4 + 0.5f); shapeLevel.Rgba[num10 * 4 + 2] = (byte)(num3 / num4 + 0.5f); shapeLevel.Rgba[num10 * 4 + 3] = (byte)(num4 / 4f + 0.5f); shapeLevel.Top[num10] = num5 / num4; } } } return shapeLevel; } } public sealed class ShapeLibrary { public const float MaxRadius = 48f; internal byte[] Palette; private volatile Dictionary<int, Shape> prefabs = new Dictionary<int, Shape>(); private readonly Dictionary<int, Shape> locations = new Dictionary<int, Shape>(); private readonly object writeLock = new object(); private readonly HashSet<int> gamePrefabs = new HashSet<int>(); public int Count => prefabs.Count + locations.Count; public bool IsGamePrefab(int hash) { return gamePrefabs.Contains(hash); } public void Register(Shape shape) { lock (writeLock) { Dictionary<int, Shape> dictionary = new Dictionary<int, Shape>(prefabs); dictionary[shape.Hash] = shape; prefabs = dictionary; } } public Shape Find(int hash, bool location) { if (!(location ? locations : prefabs).TryGetValue(hash, out var value)) { return null; } return value; } public static ZoneLayers LayerOf(ShapeKind kind, float radius) { if (kind == ShapeKind.Piece || kind == ShapeKind.Location) { return ZoneLayers.Structures; } if (!(radius >= 1f)) { return ZoneLayers.Small; } return ZoneLayers.Trees; } public static ShapeLib