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 MapAutoSync v0.3.0
plugins/MapAutoSync.dll
Decompiled a week agousing System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: AssemblyCompany("MapAutoSync")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.3.0.0")] [assembly: AssemblyInformationalVersion("0.3.0+b7b2b3f542e8058c6b4572d77d2e97811b3387fe")] [assembly: AssemblyProduct("MapAutoSync")] [assembly: AssemblyTitle("MapAutoSync")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.3.0.0")] [module: UnverifiableCode] [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 MapAutoSync { internal static class ClientSync { private const float JoinDelay = 20f; private static float s_readyAt = -1f; private static bool s_synced; private static int s_lastDay = -1; private static bool s_now; internal static void Reset() { s_readyAt = -1f; s_synced = false; s_lastDay = -1; s_now = false; } internal static void RequestNow() { s_now = true; } internal static void Tick() { if (!((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)Minimap.instance == (Object)null) && !((Object)(object)EnvMan.instance == (Object)null) && ZRoutedRpc.instance != null) { if (s_readyAt < 0f) { s_readyAt = Time.time + 20f; } int day = EnvMan.instance.GetDay(); bool flag = MapAutoSyncPlugin.AutoSync.Value && ((!s_synced && Time.time >= s_readyAt) || (s_synced && day != s_lastDay)); if (s_now || flag) { s_now = false; s_synced = true; s_lastDay = day; Upload(); } } } private static void Upload() { try { byte[] array = Utils.Compress(Minimap.instance.GetSharedMapData((byte[])null)); Transfer.Send(ZRoutedRpc.instance.GetServerPeerID(), "MapAutoSync", 1, Player.m_localPlayer.GetPlayerID(), array); MapAutoSyncPlugin.Log.LogInfo((object)$"sent map to the server: {array.Length / 1024} KB"); } catch (Exception arg) { MapAutoSyncPlugin.Log.LogError((object)$"map upload failed: {arg}"); } } internal static void Merged(byte[] data) { if (!((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)Minimap.instance == (Object)null)) { bool flag = Minimap.instance.AddSharedMapData(Utils.Decompress(data)); bool num = MapToggle.SyncDone(flag); MapAutoSyncPlugin.Log.LogInfo((object)string.Format("merged the server's map: {0} KB, {1}", data.Length / 1024, flag ? "new areas or pins" : "nothing new")); if (!num && MapAutoSyncPlugin.ShowMessage.Value) { MapBanner.Show(flag ? "Map synced: new areas or pins" : "Map synced", (MessageType)1); } } } } [BepInPlugin("liekos47.mapautosync", "MapAutoSync", "0.3.0")] public class MapAutoSyncPlugin : BaseUnityPlugin { public const string Guid = "liekos47.mapautosync"; public const string Name = "MapAutoSync"; public const string Version = "0.3.0"; internal static ManualLogSource Log; internal static MapAutoSyncPlugin Instance; internal static ConfigEntry<bool> AutoSync; internal static ConfigEntry<bool> ShowMessage; internal static ConfigEntry<string> ButtonLabel; internal static ConfigEntry<string> SyncNowLabel; internal static ConfigEntry<float> ButtonOffsetX; internal static ConfigEntry<float> ButtonOffsetY; internal static ConfigEntry<float> BannerSize; internal static ConfigEntry<float> BannerOffset; private Harmony harmony; private ZRoutedRpc registeredFor; private ServerStore store; private string storeWorld; private float nextTick; private readonly HashSet<long> reminded = new HashSet<long>(); private readonly ConcurrentQueue<Action> mainThread = new ConcurrentQueue<Action>(); private void Awake() { //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Instance = this; AutoSync = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AutoSync", true, "Share your explored map and pins with the server when you join and once every in-game day. This is the tick-box on the large map. The \"Sync now\" button above it works either way."); ShowMessage = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowMessage", true, "Tell you when the map has been synced automatically: in large text on the map if it is open, otherwise as a short top-left message. A \"Sync now\" always reports on screen."); ButtonLabel = ((BaseUnityPlugin)this).Config.Bind<string>("Button", "Label", "Auto sync map", "The text beside the tick-box on the large map."); SyncNowLabel = ((BaseUnityPlugin)this).Config.Bind<string>("Button", "SyncNowLabel", "Sync now", "The text beside the button above the tick-box that syncs the map straight away."); ButtonOffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("Button", "OffsetX", 0f, "Moves the tick-box sideways from the \"Visible to other players\" tick-box, in screen units. Applied when the map screen is created (rejoin to see a change)."); ButtonOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("Button", "OffsetY", 0f, "Moves the tick-box up or down. 0 places it one row above \"Visible to other players\", or above the game's \"Cartography Table\" row when that is showing; use a negative number to move it down."); BannerSize = ((BaseUnityPlugin)this).Config.Bind<float>("Message", "Size", 36f, "Text size of the message shown on the large map (\"Syncing map...\", \"Map synced\"). Rejoin to see a change."); BannerOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Message", "OffsetFromTop", 90f, "How far below the top of the screen that message sits, in screen units. Rejoin to see a change."); harmony = new Harmony(string.Format("{0}.{1}", "liekos47.mapautosync", DateTime.Now.Ticks)); harmony.PatchAll(); Log.LogInfo((object)"MapAutoSync 0.3.0 loaded"); } private void Update() { Action result; while (mainThread.TryDequeue(out result)) { try { result(); } catch (Exception arg) { Log.LogError((object)$"{arg}"); } } MapBanner.Frame(); if (Time.time < nextTick) { return; } nextTick = Time.time + 2f; try { RegisterRpc(); if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer()) { MapToggle.Ensure(); ClientSync.Tick(); } else { ClientSync.Reset(); } } catch (Exception arg2) { Log.LogError((object)$"tick failed: {arg2}"); } } private void RegisterRpc() { if (ZRoutedRpc.instance != null && ZRoutedRpc.instance != registeredFor) { ZRoutedRpc.instance.m_functions.Remove(StringExtensionMethods.GetStableHashCode("MapAutoSync")); ZRoutedRpc.instance.m_functions.Remove(StringExtensionMethods.GetStableHashCode("AutoSyncMap")); ZRoutedRpc.instance.Register<ZPackage>("MapAutoSync", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg) { OnPackage(sender, pkg, legacy: false); }); ZRoutedRpc.instance.Register<ZPackage>("AutoSyncMap", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg) { OnPackage(sender, pkg, legacy: true); }); reminded.Clear(); registeredFor = ZRoutedRpc.instance; } } private void OnPackage(long sender, ZPackage pkg, bool legacy) { try { if (Transfer.Receive(sender, pkg, legacy, out var kind, out var playerId, out var version, out var data)) { bool flag = (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer(); if (kind == 1 && flag) { ServerMerge(sender, playerId, data, legacy ? "AutoSyncMap" : "MapAutoSync"); Remind(sender, version, legacy); } else if (kind == 2 && !flag && !legacy) { ClientSync.Merged(data); } } } catch (Exception arg) { Log.LogError((object)$"bad map package from {sender}: {arg}"); } } private void Remind(long sender, string version, bool legacy) { if ((legacy || (System.Version.TryParse(version, out Version result) && result < new Version("0.3.0"))) && reminded.Add(sender)) { string text = (legacy ? "Your map sync mod is out of date. Please replace AutoSyncMap with MapAutoSync 0.3.0 or later." : ("MapAutoSync " + version + " is out of date. This server runs 0.3.0, please update.")); ZRoutedRpc.instance.InvokeRoutedRPC(sender, "ShowMessage", new object[2] { 2, text }); string text2 = ZNet.instance.GetPeer(sender)?.m_playerName ?? sender.ToString(); Log.LogInfo((object)(text2 + " runs " + (legacy ? "a version from before 0.3.0 (AutoSyncMap)" : version) + ": reminded to update")); } } private void ServerMerge(long sender, long playerId, byte[] upload, string rpc) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) string worldName = ZNet.instance.GetWorldName(); if (store == null || storeWorld != worldName) { string worldsSaveRootPath = SaveSystem.GetWorldsSaveRootPath(ZNet.m_world.m_fileSource); string text = Path.Combine(worldsSaveRootPath, worldName + ".mapautosync.dat"); string text2 = Path.Combine(worldsSaveRootPath, worldName + ".autosyncmap.dat"); if (!File.Exists(text) && File.Exists(text2)) { File.Move(text2, text); Log.LogInfo((object)("took over the shared map from before 0.3.0: " + Path.GetFileName(text2) + " is now " + Path.GetFileName(text))); } store = new ServerStore(text); storeWorld = worldName; } ServerStore target = store; string who = ZNet.instance.GetPeer(sender)?.m_playerName ?? sender.ToString(); Task.Run(delegate { try { Stopwatch watch = Stopwatch.StartNew(); string info; byte[] merged = target.Merge(upload, playerId, out info); mainThread.Enqueue(delegate { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.GetPeer(sender) != null) { Transfer.Send(sender, rpc, 2, 0L, merged); } Log.LogInfo((object)$"synced {who}: {upload.Length / 1024} KB up, {info}, {watch.ElapsedMilliseconds} ms"); }); } catch (Exception ex) { Exception ex2 = ex; Exception e = ex2; mainThread.Enqueue(delegate { Log.LogError((object)$"merge failed for {who}: {e}"); }); } }); } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } MapToggle.Remove(); if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } } internal static class MapBanner { private const float Seconds = 4f; private static TMP_Text s_text; private static float s_until; internal static void Show(string text, MessageType otherwise) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (!OnMap(text) && (Object)(object)Player.m_localPlayer != (Object)null) { ((Character)Player.m_localPlayer).Message(otherwise, text, 0, (Sprite)null, false); } } private static bool OnMap(string text) { //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) Minimap instance = Minimap.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_largeRoot == (Object)null || !instance.m_largeRoot.activeInHierarchy || (Object)(object)instance.m_publicPosition == (Object)null) { return false; } if ((Object)(object)s_text == (Object)null) { TMP_Text componentInChildren = ((Component)instance.m_publicPosition).GetComponentInChildren<TMP_Text>(true); if ((Object)(object)componentInChildren == (Object)null) { return false; } s_text = Object.Instantiate<TMP_Text>(componentInChildren, instance.m_largeRoot.transform); ((Object)s_text).name = "MapAutoSyncBanner"; RectTransform rectTransform = s_text.rectTransform; Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(0.5f, 1f); rectTransform.anchorMax = val; rectTransform.anchorMin = val; rectTransform.pivot = new Vector2(0.5f, 1f); rectTransform.anchoredPosition = new Vector2(0f, 0f - MapAutoSyncPlugin.BannerOffset.Value); rectTransform.sizeDelta = new Vector2(1200f, 80f); s_text.alignment = (TextAlignmentOptions)514; s_text.enableAutoSizing = false; s_text.fontSize = MapAutoSyncPlugin.BannerSize.Value; ((Graphic)s_text).raycastTarget = false; } s_text.text = text; s_text.transform.SetAsLastSibling(); ((Component)s_text).gameObject.SetActive(true); s_until = Time.time + 4f; return true; } internal static void Frame() { if ((Object)(object)s_text != (Object)null && ((Component)s_text).gameObject.activeSelf && Time.time >= s_until) { ((Component)s_text).gameObject.SetActive(false); } } internal static void Remove() { if ((Object)(object)s_text != (Object)null) { Object.Destroy((Object)(object)((Component)s_text).gameObject); s_text = null; } } } internal static class MapToggle { private const float SyncNowTimeout = 10f; private const float ResultSeconds = 4f; private static Toggle s_toggle; private static Toggle s_syncNow; private static RectTransform s_toggleRow; private static RectTransform s_syncNowRow; private static float s_syncNowUntil; private static float s_labelResetAt = -1f; internal static void Ensure() { if ((Object)(object)Minimap.instance == (Object)null || (Object)(object)Minimap.instance.m_publicPosition == (Object)null) { return; } if ((Object)(object)s_toggle != (Object)null && (Object)(object)s_syncNow != (Object)null) { Place(); if (s_syncNow.isOn && Time.time >= s_syncNowUntil) { Finish("No reply from the server"); } else if (!s_syncNow.isOn && s_labelResetAt >= 0f && Time.time >= s_labelResetAt) { s_labelResetAt = -1f; SetLabel(((Component)s_syncNow).gameObject, MapAutoSyncPlugin.SyncNowLabel.Value); } return; } Remove(); RectTransform val = SourceRow(); s_toggle = Copy(val, "MapAutoSyncToggle", MapAutoSyncPlugin.ButtonLabel.Value, out s_toggleRow); s_toggle.isOn = MapAutoSyncPlugin.AutoSync.Value; ((UnityEvent<bool>)(object)s_toggle.onValueChanged).AddListener((UnityAction<bool>)delegate(bool on) { MapAutoSyncPlugin.AutoSync.Value = on; if (on) { ClientSync.RequestNow(); } }); s_syncNow = Copy(val, "MapAutoSyncSyncNow", MapAutoSyncPlugin.SyncNowLabel.Value, out s_syncNowRow); s_syncNow.isOn = false; ((UnityEvent<bool>)(object)s_syncNow.onValueChanged).AddListener((UnityAction<bool>)delegate(bool on) { if (on) { s_syncNowUntil = Time.time + 10f; s_labelResetAt = -1f; SetLabel(((Component)s_syncNow).gameObject, "Syncing..."); Say("Syncing map..."); ClientSync.RequestNow(); ClientSync.Tick(); } }); if (InLayoutGroup((Transform)(object)val)) { Transform val2 = (Transform)(object)val; GameObject sharedMapHint = Minimap.instance.m_sharedMapHint; if ((Object)(object)sharedMapHint != (Object)null && (Object)(object)sharedMapHint.transform.parent == (Object)(object)((Transform)val).parent && sharedMapHint.transform.GetSiblingIndex() < val2.GetSiblingIndex()) { val2 = sharedMapHint.transform; } ((Transform)s_toggleRow).SetSiblingIndex(val2.GetSiblingIndex()); ((Transform)s_syncNowRow).SetSiblingIndex(((Transform)s_toggleRow).GetSiblingIndex()); } Place(); MapAutoSyncPlugin.Log.LogInfo((object)$"added the Auto sync map tick-box and the Sync now button to the large map (copied {Path((Transform)(object)val)}, tick-box {Path(((Component)Minimap.instance.m_publicPosition).transform)}, layout group {InLayoutGroup((Transform)(object)val)})"); } internal static bool SyncDone(bool changed) { if ((Object)(object)s_syncNow == (Object)null || !s_syncNow.isOn) { return false; } Finish(changed ? "Sync complete: new areas or pins" : "Sync complete", "Sync complete"); return true; } private static void Finish(string message, string label = null) { s_syncNow.SetIsOnWithoutNotify(false); SetLabel(((Component)s_syncNow).gameObject, label ?? message); s_labelResetAt = Time.time + 4f; Say(message); } private static void Say(string message) { MapBanner.Show(message, (MessageType)2); } private static RectTransform SourceRow() { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown Transform transform = ((Component)Minimap.instance.m_publicPosition).transform; Transform val = (((Object)(object)Minimap.instance.m_largeRoot != (Object)null) ? Minimap.instance.m_largeRoot.transform : null); GameObject sharedMapHint = Minimap.instance.m_sharedMapHint; Transform val2 = transform; while (true) { Transform parent = val2.parent; RectTransform val3 = (RectTransform)(object)((parent is RectTransform) ? parent : null); if (val3 == null || !((Object)(object)val3 != (Object)(object)val) || (!((Object)(object)sharedMapHint == (Object)null) && sharedMapHint.transform.IsChildOf((Transform)(object)val3)) || ((Component)val3).GetComponentsInChildren<Selectable>(true).Length != 1) { break; } val2 = (Transform)(object)val3; } return (RectTransform)val2; } private static bool InLayoutGroup(Transform row) { if ((Object)(object)row.parent != (Object)null) { return (Object)(object)((Component)row.parent).GetComponent<LayoutGroup>() != (Object)null; } return false; } private static Toggle Copy(RectTransform source, string name, string label, out RectTransform row) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown GameObject val = Object.Instantiate<GameObject>(((Component)source).gameObject, ((Transform)source).parent); ((Object)val).name = name; row = (RectTransform)val.transform; Toggle componentInChildren = val.GetComponentInChildren<Toggle>(true); componentInChildren.onValueChanged = new ToggleEvent(); SetLabel(((Component)componentInChildren).gameObject, label); return componentInChildren; } private static void Place() { //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_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: 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_010d: 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) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //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_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) RectTransform val = SourceRow(); if (InLayoutGroup((Transform)(object)val) || (Object)(object)((Transform)val).parent == (Object)null) { return; } Rect rect = val.rect; float num; if (!(((Rect)(ref rect)).height > 1f)) { num = 32f; } else { rect = val.rect; num = ((Rect)(ref rect)).height + 4f; } float num2 = num; int num3 = 1; GameObject sharedMapHint = Minimap.instance.m_sharedMapHint; if ((Object)(object)sharedMapHint != (Object)null) { Transform transform = sharedMapHint.transform; RectTransform val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null); if (val2 != null) { float num4 = ((Transform)val).parent.InverseTransformPoint(Centre(val2)).y - ((Transform)val).parent.InverseTransformPoint(Centre(val)).y; if (num4 > 1f) { num2 = num4; } if (sharedMapHint.activeSelf) { num3 = 2; } } } Vector2 val3 = default(Vector2); ((Vector2)(ref val3))..ctor(MapAutoSyncPlugin.ButtonOffsetX.Value, MapAutoSyncPlugin.ButtonOffsetY.Value); s_toggleRow.anchoredPosition = val.anchoredPosition + val3 + new Vector2(0f, num2 * (float)num3); s_syncNowRow.anchoredPosition = val.anchoredPosition + val3 + new Vector2(0f, num2 * (float)(num3 + 1)); } private static Vector3 Centre(RectTransform rect) { //IL_0010: 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: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) Vector3[] array = (Vector3[])(object)new Vector3[4]; rect.GetWorldCorners(array); return (array[0] + array[2]) * 0.5f; } private static string Path(Transform transform) { List<string> list = new List<string>(); Transform val = (((Object)(object)Minimap.instance.m_largeRoot != (Object)null) ? Minimap.instance.m_largeRoot.transform : null); Transform val2 = transform; while ((Object)(object)val2 != (Object)null && list.Count < 8) { list.Insert(0, ((Object)val2).name); if ((Object)(object)val2 == (Object)(object)val) { break; } val2 = val2.parent; } return string.Join("/", list); } private static void SetLabel(GameObject root, string text) { Component[] componentsInChildren = root.GetComponentsInChildren<Component>(true); foreach (Component val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !(val is Toggle)) { PropertyInfo property = ((object)val).GetType().GetProperty("text"); if (property != null && property.PropertyType == typeof(string) && property.CanWrite) { property.SetValue(val, text, null); } } } } internal static void Remove() { if ((Object)(object)s_toggleRow != (Object)null) { Object.Destroy((Object)(object)((Component)s_toggleRow).gameObject); } if ((Object)(object)s_syncNowRow != (Object)null) { Object.Destroy((Object)(object)((Component)s_syncNowRow).gameObject); } MapBanner.Remove(); s_toggle = null; s_syncNow = null; s_toggleRow = null; s_syncNowRow = null; } } internal class ServerStore { private struct Pin { public long Owner; public string Name; public string Author; public float X; public float Y; public float Z; public int Type; public bool Checked; } private const int Version = 3; private readonly object m_lock = new object(); private readonly string m_file; private byte[] m_explored; private List<Pin> m_pins = new List<Pin>(); private bool m_loaded; internal ServerStore(string file) { m_file = file; } internal byte[] Merge(byte[] uploadCompressed, long playerId, out string info) { lock (m_lock) { Load(); Parse(Utils.Decompress(uploadCompressed), out var explored, out var pins); int num = 0; if (m_explored == null || m_explored.Length != explored.Length) { m_explored = explored; byte[] array = explored; foreach (byte b in array) { num += b; } } else { for (int j = 0; j < explored.Length; j++) { if (explored[j] != 0 && m_explored[j] == 0) { m_explored[j] = 1; num++; } } } int count = m_pins.Count; m_pins.RemoveAll((Pin p) => p.Owner == playerId); int count2 = m_pins.Count; int num2 = 0; foreach (Pin item in pins) { if (item.Owner == playerId) { m_pins.Add(item); num2++; } } byte[] array2 = Utils.Compress(Write()); string text = m_file + ".tmp"; File.WriteAllBytes(text, array2); if (File.Exists(m_file)) { File.Delete(m_file); } File.Move(text, m_file); info = $"{num} newly explored cells, {num2} own pins ({count - count2} before), {m_pins.Count} pins in total, {array2.Length / 1024} KB"; return array2; } } private void Load() { if (!m_loaded) { m_loaded = true; if (File.Exists(m_file)) { Parse(Utils.Decompress(File.ReadAllBytes(m_file)), out m_explored, out m_pins); } } } private static void Parse(byte[] data, out byte[] explored, out List<Pin> pins) { using BinaryReader binaryReader = new BinaryReader(new MemoryStream(data)); int num = binaryReader.ReadInt32(); if (num != 3) { throw new InvalidDataException($"shared map version {num}, expected {3}"); } int num2 = binaryReader.ReadInt32(); if (num2 < 0 || num2 > data.Length) { throw new InvalidDataException($"explored length {num2}"); } explored = binaryReader.ReadBytes(num2); int num3 = binaryReader.ReadInt32(); if (num3 < 0 || num3 > 100000) { throw new InvalidDataException($"pin count {num3}"); } pins = new List<Pin>(num3); for (int i = 0; i < num3; i++) { pins.Add(new Pin { Owner = binaryReader.ReadInt64(), Name = binaryReader.ReadString(), X = binaryReader.ReadSingle(), Y = binaryReader.ReadSingle(), Z = binaryReader.ReadSingle(), Type = binaryReader.ReadInt32(), Checked = binaryReader.ReadBoolean(), Author = binaryReader.ReadString() }); } } private byte[] Write() { using MemoryStream memoryStream = new MemoryStream(m_explored.Length + 64 * m_pins.Count + 16); using BinaryWriter binaryWriter = new BinaryWriter(memoryStream); binaryWriter.Write(3); binaryWriter.Write(m_explored.Length); binaryWriter.Write(m_explored); binaryWriter.Write(m_pins.Count); foreach (Pin pin in m_pins) { binaryWriter.Write(pin.Owner); binaryWriter.Write(pin.Name ?? ""); binaryWriter.Write(pin.X); binaryWriter.Write(pin.Y); binaryWriter.Write(pin.Z); binaryWriter.Write(pin.Type); binaryWriter.Write(pin.Checked); binaryWriter.Write(pin.Author ?? ""); } binaryWriter.Flush(); return memoryStream.ToArray(); } } internal static class Transfer { private class Incoming { public byte[][] Chunks; public int Received; public float Started; } internal const string Rpc = "MapAutoSync"; internal const string LegacyRpc = "AutoSyncMap"; internal const byte Upload = 1; internal const byte Merged = 2; private const int ChunkSize = 65536; private const float StaleSeconds = 120f; private static readonly Dictionary<(long, int), Incoming> s_incoming = new Dictionary<(long, int), Incoming>(); private static int s_nextId = 1; internal static void Send(long target, string rpc, byte kind, long playerId, byte[] data) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown int num = s_nextId++; int num2 = Mathf.Max(1, (data.Length + 65536 - 1) / 65536); for (int i = 0; i < num2; i++) { int num3 = i * 65536; int num4 = Mathf.Min(65536, data.Length - num3); byte[] array = new byte[num4]; Buffer.BlockCopy(data, num3, array, 0, num4); ZPackage val = new ZPackage(); val.Write(kind); val.Write(num); val.Write(i); val.Write(num2); val.Write(playerId); if (rpc != "AutoSyncMap") { val.Write("0.3.0"); } val.Write(array); ZRoutedRpc.instance.InvokeRoutedRPC(target, rpc, new object[1] { val }); } } internal static bool Receive(long sender, ZPackage pkg, bool legacy, out byte kind, out long playerId, out string version, out byte[] data) { kind = pkg.ReadByte(); int item = pkg.ReadInt(); int num = pkg.ReadInt(); int num2 = pkg.ReadInt(); playerId = pkg.ReadLong(); version = (legacy ? "" : pkg.ReadString()); byte[] array = pkg.ReadByteArray(); data = null; if (num2 < 1 || num2 > 4096 || num < 0 || num >= num2) { return false; } foreach (var item2 in (from kv in s_incoming where Time.time - kv.Value.Started > 120f select kv.Key).ToList()) { s_incoming.Remove(item2); } (long, int) key = (sender, item); if (!s_incoming.TryGetValue(key, out var value)) { Dictionary<(long, int), Incoming> dictionary = s_incoming; Incoming obj = new Incoming { Chunks = new byte[num2][], Started = Time.time }; value = obj; dictionary[key] = obj; } if (value.Chunks.Length != num2 || value.Chunks[num] != null) { return false; } value.Chunks[num] = array; if (++value.Received < num2) { return false; } s_incoming.Remove(key); data = new byte[value.Chunks.Sum((byte[] c) => c.Length)]; int num3 = 0; byte[][] chunks = value.Chunks; foreach (byte[] array2 in chunks) { Buffer.BlockCopy(array2, 0, data, num3, array2.Length); num3 += array2.Length; } return true; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }