From 70d1925e3b8e9e4731559277aea0c52e776810bf Mon Sep 17 00:00:00 2001 From: Nicolas Busseneau Date: Sun, 25 Feb 2024 15:18:02 +0100 Subject: [PATCH] one-way public pins sharing to vanilla --- CHANGELOG.md | 4 +++ README.md | 4 +-- src/Extensions/MapTable.cs | 14 ++++---- src/Managers/InteractionManager.cs | 4 +-- src/Patches/Minimap.cs | 51 ++++++++++++++---------------- 5 files changed, 39 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81f1f2e..29b8e54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add limited one-way compatibility for public pins sharing from modded clients to vanilla non-modded clients. + ### Fixed - Fix shared pins disappearing if a cartography table is in use while a save occurs. diff --git a/README.md b/README.md index de6a3a6..9cb56b6 100644 --- a/README.md +++ b/README.md @@ -71,13 +71,13 @@ How it works: - **Private pins** can be interacted with. - **Public** or **guild pins** previously retrieved from a **cartography table** can be displayed / hidden, but cannot be interacted with. -**Table pins** are stored in a custom ZDO key under the **cartography table**'s ZNetView's ZDO, thus there is no risk if a client without the mod installed interacts with a modded table. However vanilla clients will not be able to use a modded table: they cannot retrieve **table pins**, cannot store **table pins**, and cannot even use the table to store vanilla shared pins (because these get overwritten by design when a modded client interacts with a modded table). +**Table pins** are stored in a custom ZDO key under the **cartography table**'s ZNetView's ZDO, thus there is no conflict between non-modded vanilla clients and modded clients. Limited one-way compatibility is even provided, as modded clients will also write public pins to the vanilla shared data, allowing non-modded vanilla clients to receive public pins seamlessly. However non-modded vanilla clients will not be able to contribute anything: modded clients can only receive pins from other modded clients. ## Install This is a client-side mod, it does not need to be installed on the server. -It is not required for all clients on the server to install the mod, however vanilla clients without the mod will not have access to any shared pins. This is kinda pointless so of course it's better if all clients install it 😅 +It is not strictly required for all clients on the server to install the mod, however non-modded vanilla clients will not be able to share any of their pins with modded clients, so of course it's better if all clients install it 😅 ### Thunderstore (recommended) diff --git a/src/Extensions/MapTable.cs b/src/Extensions/MapTable.cs index 95419d8..6360e77 100644 --- a/src/Extensions/MapTable.cs +++ b/src/Extensions/MapTable.cs @@ -118,12 +118,12 @@ private static void RPC_StoreOwnerInZDO(this MapTable mapTable, string owner) #endregion - #region Explored map storage (vanilla ZDO key) + #region Explored map and vanilla pins storage (vanilla ZDO key) - public static void SyncExploredMap(this MapTable mapTable) + public static void SyncVanillaSharedData(this MapTable mapTable) { mapTable.RetrieveExploredMapFromZDO(); - mapTable.StoreExploredMapInZDO(); + mapTable.StoreExploredMapAndPublicPinsInZDO(); } private static void RetrieveExploredMapFromZDO(this MapTable mapTable) @@ -132,17 +132,17 @@ private static void RetrieveExploredMapFromZDO(this MapTable mapTable) { array = Utils.Decompress(array); // AddSharedMapData(...) has been truncated via a Harmony patch so that it only retrieves - // explored map data from vanilla shared data, and not pins. + // explored map data from vanilla shared data, ignoring pins coming from non-modded clients. Minimap.instance.AddSharedMapData(array); } } - private static void StoreExploredMapInZDO(this MapTable mapTable) + private static void StoreExploredMapAndPublicPinsInZDO(this MapTable mapTable) { var array = mapTable.m_nview.GetZDO().GetByteArray(ZDOVars.s_data); if (array is not null) array = Utils.Decompress(array); - // Underlying call to GetSharedMapData(...) has been truncated via a Harmony patch so that it - // only stores explored map data in vanilla shared data, and not pins. + // Underlying call to GetSharedMapData(...) has been modified via a Harmony patch so that it + // stores public pins in vanilla shared data, for limited compatibility with non-modded clients. var zPackage = mapTable.GetMapData(array); mapTable.m_nview.InvokeRPC("MapData", zPackage); } diff --git a/src/Managers/InteractionManager.cs b/src/Managers/InteractionManager.cs index 299e08e..2da26d1 100644 --- a/src/Managers/InteractionManager.cs +++ b/src/Managers/InteractionManager.cs @@ -33,8 +33,8 @@ public static void OnMapTableUse(MapTable mapTable, Humanoid user) else { CurrentMapTable = mapTable; - CurrentMapTable.SyncExploredMap(); MinimapManager.ReplaceMinimapPinsWithTablePins(CurrentMapTable); + CurrentMapTable.SyncVanillaSharedData(); CurrentMapTable.StartListeningForPinEvents(); MinimapManager.OpenMap(mapTable); } @@ -93,7 +93,7 @@ public static string GetHoverText(MapTable mapTable) public static void OnMapClose() { if (!IsInteracting) return; - CurrentMapTable.SyncExploredMap(); + CurrentMapTable.SyncVanillaSharedData(); CurrentMapTable.ReplaceTablePinsWithMinimapPins(); CurrentMapTable.StopListeningForPinEvents(); MinimapManager.OnMapClose(); diff --git a/src/Patches/Minimap.cs b/src/Patches/Minimap.cs index ca14a64..3c38dd5 100644 --- a/src/Patches/Minimap.cs +++ b/src/Patches/Minimap.cs @@ -125,51 +125,48 @@ private static IEnumerable Transpiler(IEnumerable - /// Truncate Minimap.AddSharedMapData() so that it only retrieves explored map data from vanilla shared data, and not pins. + /// Inject public pins as Minimap.instance.m_pins replacement in Minimap.GetSharedMapData() so that + /// non-modded clients can retrieve public pins stored in vanilla shared data. /// - [HarmonyPatch(typeof(Minimap), nameof(Minimap.AddSharedMapData))] - private class TruncateAddSharedMapData + [HarmonyPatch(typeof(Minimap), nameof(Minimap.GetSharedMapData))] + private class InjectPublicPinsInGetSharedMapData { + private static List publicPins; + private static void Prefix() => publicPins = MinimapManager.PublicPins.ToList(); + private static IEnumerable Transpiler(IEnumerable instructions) { return new CodeMatcher(instructions) - .MatchForward(false, - new CodeMatch(OpCodes.Ldc_I4_0), - new CodeMatch(OpCodes.Stloc_S), - new CodeMatch(OpCodes.Ldloc_1), - new CodeMatch(OpCodes.Ldc_I4_2), - new CodeMatch(OpCodes.Blt)) - .ThrowIfInvalid("Could not truncate Minimap.AddSharedMapData()") - .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_3)) - .InsertAndAdvance(new CodeInstruction(OpCodes.Ret)) + .MatchForward(false, new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(Minimap), nameof(Minimap.m_pins)))) + .ThrowIfInvalid("Could not inject pins replacement in Minimap.GetSharedMapData()") + .SetAndAdvance(OpCodes.Ldsfld, AccessTools.Field(typeof(InjectPublicPinsInGetSharedMapData), nameof(publicPins))) + .MatchForward(false, new CodeMatch(OpCodes.Ldfld, AccessTools.Field(typeof(Minimap), nameof(Minimap.m_pins)))) + .ThrowIfInvalid("Could not inject pins replacement in Minimap.GetSharedMapData()") + .SetAndAdvance(OpCodes.Ldsfld, AccessTools.Field(typeof(InjectPublicPinsInGetSharedMapData), nameof(publicPins))) .InstructionEnumeration(); } } /// - /// Truncate Minimap.GetSharedMapData() so that it only stores explored map data in vanilla shared data, and not pins. + /// Truncate Minimap.AddSharedMapData() after retrieving explored map data from vanilla shared data, ignoring + /// pins coming from non-modded clients. /// - [HarmonyPatch(typeof(Minimap), nameof(Minimap.GetSharedMapData))] - private class TruncateGetSharedMapData + [HarmonyPatch(typeof(Minimap), nameof(Minimap.AddSharedMapData))] + private class TruncateAddSharedMapData { private static IEnumerable Transpiler(IEnumerable instructions) { return new CodeMatcher(instructions) .MatchForward(false, new CodeMatch(OpCodes.Ldc_I4_0), - new CodeMatch(OpCodes.Stloc_2), - new CodeMatch(OpCodes.Ldarg_0), - new CodeMatch(OpCodes.Ldfld), - new CodeMatch(OpCodes.Callvirt)) - .ThrowIfInvalid("Could not truncate Minimap.GetSharedMapData()") - .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_1)) - .InsertAndAdvance(new CodeInstruction(OpCodes.Ldc_I4_0)) - .InsertAndAdvance(new CodeInstruction(OpCodes.Callvirt, AccessTools.Method(typeof(ZPackage), nameof(ZPackage.Write), [typeof(int)]))) - .InsertAndAdvance(new CodeInstruction(OpCodes.Ldloc_1)) - .InsertAndAdvance(new CodeInstruction(OpCodes.Callvirt, AccessTools.Method(typeof(ZPackage), nameof(ZPackage.GetArray)))) - .InsertAndAdvance(new CodeInstruction(OpCodes.Ret)) + new CodeMatch(OpCodes.Stloc_S), + new CodeMatch(OpCodes.Ldloc_1), + new CodeMatch(OpCodes.Ldc_I4_2), + new CodeMatch(OpCodes.Blt)) + .ThrowIfInvalid("Could not truncate Minimap.AddSharedMapData()") + .SetAndAdvance(OpCodes.Ldloc_3, null) + .SetAndAdvance(OpCodes.Ret, null) .InstructionEnumeration(); } }