From 379be57b846c7a20b9b66d934cf8ccaa0a2f3691 Mon Sep 17 00:00:00 2001 From: NotAKidoS <37721153+NotAKidoS@users.noreply.github.com> Date: Sat, 21 Sep 2024 01:21:15 -0500 Subject: [PATCH 001/142] Stickers: update format.json --- Stickers/format.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Stickers/format.json b/Stickers/format.json index b25086c..5c28daa 100644 --- a/Stickers/format.json +++ b/Stickers/format.json @@ -18,6 +18,6 @@ ], "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r41/Stickers.dll", "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/Stickers/", - "changelog": "- Added world restriction via `[DisableStickers]` GameObject (thx Sketch).\n- Added sticker placement preview.\n- Fixed stickers being hit by VR switch shader replacement.\n- Fixed Desktop Sticker placement bind firing when a text field was focused.", + "changelog": "- Added world restriction via `[DisableStickers]` GameObject (thx Sketch).\n- Added sticker placement preview.\n- Fixed stickers being hit by VR switch shader replacement.\n- Fixed Desktop Sticker placement bind firing when a text field was focused.\n- **This version is not backwards compatible with previous versions over network.**", "embedcolor": "#f61963" } \ No newline at end of file From d0c8298074c4dcfc089ccb34ed8b8bd7e0b9cedf Mon Sep 17 00:00:00 2001 From: NotAKidoS <37721153+NotAKidoS@users.noreply.github.com> Date: Tue, 29 Oct 2024 23:56:10 -0500 Subject: [PATCH 002/142] SearchWithSpacesFix: initial release --- SearchWithSpacesFix/Main.cs | 26 +++++++++++++++ .../Properties/AssemblyInfo.cs | 32 +++++++++++++++++++ SearchWithSpacesFix/README.md | 14 ++++++++ .../SearchWithSpacesFix.csproj | 2 ++ SearchWithSpacesFix/format.json | 23 +++++++++++++ 5 files changed, 97 insertions(+) create mode 100644 SearchWithSpacesFix/Main.cs create mode 100644 SearchWithSpacesFix/Properties/AssemblyInfo.cs create mode 100644 SearchWithSpacesFix/README.md create mode 100644 SearchWithSpacesFix/SearchWithSpacesFix.csproj create mode 100644 SearchWithSpacesFix/format.json diff --git a/SearchWithSpacesFix/Main.cs b/SearchWithSpacesFix/Main.cs new file mode 100644 index 0000000..8de7baa --- /dev/null +++ b/SearchWithSpacesFix/Main.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using ABI_RC.Core.InteractionSystem; +using HarmonyLib; +using MelonLoader; + +namespace NAK.SearchWithSpacesFix; + +public class SearchWithSpacesFixMod : MelonMod +{ + public override void OnInitializeMelon() + { + HarmonyInstance.Patch( + typeof(ViewManager).GetMethod(nameof(ViewManager.GetSearchResults), + BindingFlags.Public | BindingFlags.Instance), + prefix: new HarmonyMethod(typeof(SearchWithSpacesFixMod).GetMethod(nameof(OnPreGetSearchResults), + BindingFlags.NonPublic | BindingFlags.Static)) + ); + } + + // this is so crazy + + private static void OnPreGetSearchResults(ref string searchTerm) + => searchTerm = searchTerm.Replace(" ", "_"); + + // this is so crazy +} \ No newline at end of file diff --git a/SearchWithSpacesFix/Properties/AssemblyInfo.cs b/SearchWithSpacesFix/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..8ba0f9a --- /dev/null +++ b/SearchWithSpacesFix/Properties/AssemblyInfo.cs @@ -0,0 +1,32 @@ +using MelonLoader; +using NAK.SearchWithSpacesFix.Properties; +using System.Reflection; + +[assembly: AssemblyVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyTitle(nameof(NAK.SearchWithSpacesFix))] +[assembly: AssemblyCompany(AssemblyInfoParams.Author)] +[assembly: AssemblyProduct(nameof(NAK.SearchWithSpacesFix))] + +[assembly: MelonInfo( + typeof(NAK.SearchWithSpacesFix.SearchWithSpacesFixMod), + nameof(NAK.SearchWithSpacesFix), + AssemblyInfoParams.Version, + AssemblyInfoParams.Author, + downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/SearchWithSpacesFix" +)] + +[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")] +[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] +[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)] +[assembly: MelonColor(255, 246, 25, 99)] // red-pink +[assembly: MelonAuthorColor(255, 158, 21, 32)] // red +[assembly: HarmonyDontPatchAll] + +namespace NAK.SearchWithSpacesFix.Properties; +internal static class AssemblyInfoParams +{ + public const string Version = "1.0.0"; + public const string Author = "NotAKidoS"; +} \ No newline at end of file diff --git a/SearchWithSpacesFix/README.md b/SearchWithSpacesFix/README.md new file mode 100644 index 0000000..1c4c5bc --- /dev/null +++ b/SearchWithSpacesFix/README.md @@ -0,0 +1,14 @@ +# SearchWithSpacesFix + +Fixes search terms that use spaces. + +--- + +Here is the block of text where I tell you this mod is not affiliated with or endorsed by ABI. +https://documentation.abinteractive.net/official/legal/tos/#7-modding-our-games + +> This mod is an independent creation not affiliated with, supported by, or approved by Alpha Blend Interactive. + +> Use of this mod is done so at the user's own risk and the creator cannot be held responsible for any issues arising from its use. + +> To the best of my knowledge, I have adhered to the Modding Guidelines established by Alpha Blend Interactive. diff --git a/SearchWithSpacesFix/SearchWithSpacesFix.csproj b/SearchWithSpacesFix/SearchWithSpacesFix.csproj new file mode 100644 index 0000000..e94f9dc --- /dev/null +++ b/SearchWithSpacesFix/SearchWithSpacesFix.csproj @@ -0,0 +1,2 @@ + + diff --git a/SearchWithSpacesFix/format.json b/SearchWithSpacesFix/format.json new file mode 100644 index 0000000..f8950ca --- /dev/null +++ b/SearchWithSpacesFix/format.json @@ -0,0 +1,23 @@ +{ + "_id": -1, + "name": "SearchWithSpacesFix", + "modversion": "1.0.0", + "gameversion": "2024r177", + "loaderversion": "0.6.1", + "modtype": "Mod", + "author": "NotAKidoS", + "description": "Fixes search terms that include spaces.", + "searchtags": [ + "search", + "spaces", + "fix", + "meow" + ], + "requirements": [ + "None" + ], + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r42/SearchWithSpacesFix.dll", + "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/SearchWithSpacesFix/", + "changelog": "- Initial release", + "embedcolor": "#f61963" +} \ No newline at end of file From 04c4a4590dcf5c3e434232ef01b53650f3d6f242 Mon Sep 17 00:00:00 2001 From: SurprisinglySuspicious Date: Wed, 20 Nov 2024 16:07:52 -0500 Subject: [PATCH 003/142] Issue: If NStrip wasn't found in folder or PATH, it tries to show a message to user, but it doesn't wait for key input, so it just immediately closes. --- copy_and_nstrip_dll.ps1 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/copy_and_nstrip_dll.ps1 b/copy_and_nstrip_dll.ps1 index aa4cb97..d4b95db 100644 --- a/copy_and_nstrip_dll.ps1 +++ b/copy_and_nstrip_dll.ps1 @@ -139,7 +139,7 @@ if ($missingMods.Count -gt 0) { Write-Host "" Write-Host "Copied all libraries!" Write-Host "" -Write-Host "Press any key to strip the Dlls using NStrip" +Write-Host "Press any key to strip the DLLs using NStrip..." $HOST.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | OUT-NULL $HOST.UI.RawUI.Flushinputbuffer() @@ -156,6 +156,10 @@ else { # Display an error message if NStrip.exe could not be found Write-Host "Could not find NStrip.exe in the current directory nor in the PATH." -ForegroundColor Red Write-Host "Visit https://github.com/bbepis/NStrip/releases/latest to grab a copy." -ForegroundColor Red + Write-Host "" + Write-Host "Press any key to exit..." + $HOST.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | OUT-NULL + $HOST.UI.RawUI.Flushinputbuffer() return } } @@ -169,6 +173,6 @@ foreach($dllFile in $dllsToStrip) { Write-Host "" Write-Host "Copied all libraries and stripped the DLLs!" Write-Host "" -Write-Host "Press any key to exit" +Write-Host "Press any key to exit..." $HOST.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | OUT-NULL $HOST.UI.RawUI.Flushinputbuffer() From 70a54b632c819a75805851a44de4edd512fb3fab Mon Sep 17 00:00:00 2001 From: SurprisinglySuspicious <100347264+SurprisinglySuspicious@users.noreply.github.com> Date: Wed, 20 Nov 2024 16:59:19 -0500 Subject: [PATCH 004/142] Add it for PATH too --- copy_and_nstrip_dll.ps1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/copy_and_nstrip_dll.ps1 b/copy_and_nstrip_dll.ps1 index d4b95db..0f2c7f3 100644 --- a/copy_and_nstrip_dll.ps1 +++ b/copy_and_nstrip_dll.ps1 @@ -35,6 +35,10 @@ else { else { Write-Host "[ERROR] ChilloutVR.exe not found in CVRPATH or the default Steam location." Write-Host " Please define the Environment Variable CVRPATH pointing to the ChilloutVR folder!" + Write-Host "" + Write-Host "Press any key to exit..." + $HOST.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") | OUT-NULL + $HOST.UI.RawUI.Flushinputbuffer() return } } From db07d539716d76dfcaabd5cfd144aa9aec87be15 Mon Sep 17 00:00:00 2001 From: NotAKidoS <37721153+NotAKidoS@users.noreply.github.com> Date: Mon, 2 Dec 2024 20:04:47 -0600 Subject: [PATCH 005/142] ASTExtension: Fix for latest nightlies --- .../Extensions/PlayerSetupExtensions.cs | 5 +-- ASTExtension/Integrations/BTKUI/BtkUiAddon.cs | 31 +++++++++---------- .../Integrations/BTKUI/BtkUiAddon_Utils.cs | 15 +++++---- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/ASTExtension/Extensions/PlayerSetupExtensions.cs b/ASTExtension/Extensions/PlayerSetupExtensions.cs index 241cd1e..7fa9e5b 100644 --- a/ASTExtension/Extensions/PlayerSetupExtensions.cs +++ b/ASTExtension/Extensions/PlayerSetupExtensions.cs @@ -1,4 +1,5 @@ -using ABI_RC.Core.Player; +using ABI_RC.Core; +using ABI_RC.Core.Player; using UnityEngine; namespace NAK.ASTExtension.Extensions; @@ -17,7 +18,7 @@ public static class PlayerSetupExtensions Vector3 localScale = playerSetup._avatar.transform.localScale; Vector3 initialScale = playerSetup.initialScale; float initialHeight = playerSetup._initialAvatarHeight; - Vector3 scaleDifference = PlayerSetup.DivideVectors(localScale - initialScale, initialScale); + Vector3 scaleDifference = CVRTools.DivideVectors(localScale - initialScale, initialScale); return initialHeight + initialHeight * scaleDifference.y; } } \ No newline at end of file diff --git a/ASTExtension/Integrations/BTKUI/BtkUiAddon.cs b/ASTExtension/Integrations/BTKUI/BtkUiAddon.cs index 8c57cb7..cbb7534 100644 --- a/ASTExtension/Integrations/BTKUI/BtkUiAddon.cs +++ b/ASTExtension/Integrations/BTKUI/BtkUiAddon.cs @@ -3,41 +3,41 @@ using BTKUILib; using BTKUILib.UIObjects; using BTKUILib.UIObjects.Components; -namespace NAK.ASTExtension.Integrations +namespace NAK.ASTExtension.Integrations; + +public static partial class BtkUiAddon { - public static partial class BtkUiAddon + public static void Initialize() { - public static void Initialize() - { Prepare_Icons(); Setup_PlayerSelectPage(); } - private static void Prepare_Icons() - { + private static void Prepare_Icons() + { QuickMenuAPI.PrepareIcon(ASTExtensionMod.ModName, "ASM_Icon_AvatarHeightCopy", GetIconStream("ASM_Icon_AvatarHeightCopy.png")); } - #region Player Select Page + #region Player Select Page - private static string _selectedPlayer; + private static string _selectedPlayer; - private static void Setup_PlayerSelectPage() - { + private static void Setup_PlayerSelectPage() + { QuickMenuAPI.OnPlayerSelected += OnPlayerSelected; Category category = QuickMenuAPI.PlayerSelectPage.AddCategory(ASTExtensionMod.ModName, ASTExtensionMod.ModName); Button button = category.AddButton("Copy Height", "ASM_Icon_AvatarHeightCopy", "Copy selected players Eye Height."); button.OnPress += OnCopyPlayerHeight; } - private static void OnPlayerSelected(string _, string id) - { + private static void OnPlayerSelected(string _, string id) + { _selectedPlayer = id; } - private static void OnCopyPlayerHeight() - { + private static void OnCopyPlayerHeight() + { if (string.IsNullOrEmpty(_selectedPlayer)) return; @@ -51,6 +51,5 @@ namespace NAK.ASTExtension.Integrations ASTExtensionMod.Instance.SetAvatarHeight(height); } - #endregion Player Select Page - } + #endregion Player Select Page } \ No newline at end of file diff --git a/ASTExtension/Integrations/BTKUI/BtkUiAddon_Utils.cs b/ASTExtension/Integrations/BTKUI/BtkUiAddon_Utils.cs index 3dd1c30..7b3f569 100644 --- a/ASTExtension/Integrations/BTKUI/BtkUiAddon_Utils.cs +++ b/ASTExtension/Integrations/BTKUI/BtkUiAddon_Utils.cs @@ -1,18 +1,17 @@ using System.Reflection; -namespace NAK.ASTExtension.Integrations +namespace NAK.ASTExtension.Integrations; + +public static partial class BtkUiAddon { - public static partial class BtkUiAddon - { - #region Icon Utils + #region Icon Utils - private static Stream GetIconStream(string iconName) - { + private static Stream GetIconStream(string iconName) + { Assembly assembly = Assembly.GetExecutingAssembly(); string assemblyName = assembly.GetName().Name; return assembly.GetManifestResourceStream($"{assemblyName}.Resources.{iconName}"); } - #endregion Icon Utils - } + #endregion Icon Utils } \ No newline at end of file From fe768029eb5666340ad035066b1223e5e378f7d2 Mon Sep 17 00:00:00 2001 From: NotAKidoS <37721153+NotAKidoS@users.noreply.github.com> Date: Mon, 2 Dec 2024 20:05:34 -0600 Subject: [PATCH 006/142] LuaNetworkVariables: idk --- .../LuaNetworkVariables.csproj | 6 + LuaNetworkVariables/Main.cs | 58 ++++ LuaNetworkVariables/NetLuaModule.cs | 137 ++++++++ .../NetworkVariables/LuaEventContext.cs | 41 +++ .../NetworkVariables/LuaEventTracker.cs | 60 ++++ .../LuaNetVarController.Base.cs | 146 ++++++++ .../LuaNetVarController.Networking.cs | 214 ++++++++++++ .../LuaNetVarController.Registration.cs | 101 ++++++ .../LuaNetVarController.Serialization.cs | 62 ++++ .../LuaNetVarController.Utility.cs | 57 +++ LuaNetworkVariables/Patches.cs | 65 ++++ .../Properties/AssemblyInfo.cs | 32 ++ LuaNetworkVariables/README.md | 14 + .../SyncedBehaviour/MNSyncedBehaviour.cs | 326 ++++++++++++++++++ .../SyncedBehaviour/PickupableBehaviour.cs | 161 +++++++++ .../SyncedBehaviour/PickupableObject.cs | 72 ++++ .../SyncedBehaviour/TestSyncedBehaviour.cs | 63 ++++ .../SyncedBehaviour/TestSyncedObject.cs | 42 +++ LuaNetworkVariables/format.json | 23 ++ 19 files changed, 1680 insertions(+) create mode 100644 LuaNetworkVariables/LuaNetworkVariables.csproj create mode 100644 LuaNetworkVariables/Main.cs create mode 100644 LuaNetworkVariables/NetLuaModule.cs create mode 100644 LuaNetworkVariables/NetworkVariables/LuaEventContext.cs create mode 100644 LuaNetworkVariables/NetworkVariables/LuaEventTracker.cs create mode 100644 LuaNetworkVariables/NetworkVariables/LuaNetVarController.Base.cs create mode 100644 LuaNetworkVariables/NetworkVariables/LuaNetVarController.Networking.cs create mode 100644 LuaNetworkVariables/NetworkVariables/LuaNetVarController.Registration.cs create mode 100644 LuaNetworkVariables/NetworkVariables/LuaNetVarController.Serialization.cs create mode 100644 LuaNetworkVariables/NetworkVariables/LuaNetVarController.Utility.cs create mode 100644 LuaNetworkVariables/Patches.cs create mode 100644 LuaNetworkVariables/Properties/AssemblyInfo.cs create mode 100644 LuaNetworkVariables/README.md create mode 100644 LuaNetworkVariables/SyncedBehaviour/MNSyncedBehaviour.cs create mode 100644 LuaNetworkVariables/SyncedBehaviour/PickupableBehaviour.cs create mode 100644 LuaNetworkVariables/SyncedBehaviour/PickupableObject.cs create mode 100644 LuaNetworkVariables/SyncedBehaviour/TestSyncedBehaviour.cs create mode 100644 LuaNetworkVariables/SyncedBehaviour/TestSyncedObject.cs create mode 100644 LuaNetworkVariables/format.json diff --git a/LuaNetworkVariables/LuaNetworkVariables.csproj b/LuaNetworkVariables/LuaNetworkVariables.csproj new file mode 100644 index 0000000..728edb7 --- /dev/null +++ b/LuaNetworkVariables/LuaNetworkVariables.csproj @@ -0,0 +1,6 @@ + + + + net48 + + diff --git a/LuaNetworkVariables/Main.cs b/LuaNetworkVariables/Main.cs new file mode 100644 index 0000000..d8c5def --- /dev/null +++ b/LuaNetworkVariables/Main.cs @@ -0,0 +1,58 @@ +using ABI_RC.Core.Player; +using MelonLoader; +using UnityEngine; + +namespace NAK.LuaNetVars; + +public class LuaNetVarsMod : MelonMod +{ + internal static MelonLogger.Instance Logger; + + #region Melon Preferences + + + #endregion Melon Preferences + + #region Melon Events + + public override void OnInitializeMelon() + { + Logger = LoggerInstance; + + ApplyPatches(typeof(Patches.LuaScriptFactory_Patches)); + ApplyPatches(typeof(Patches.CVRSyncHelper_Patches)); + } + + public override void OnUpdate() + { + // if (Input.GetKeyDown(KeyCode.F1)) + // { + // PlayerSetup.Instance.DropProp("be0b5acc-a987-48dc-a28b-62bd912fe3a0"); + // } + // + // if (Input.GetKeyDown(KeyCode.F2)) + // { + // GameObject go = new("TestSyncedObject"); + // go.AddComponent(); + // } + } + + #endregion Melon Events + + #region Melon Mod Utilities + + private void ApplyPatches(Type type) + { + try + { + HarmonyInstance.PatchAll(type); + } + catch (Exception e) + { + LoggerInstance.Msg($"Failed while patching {type.Name}!"); + LoggerInstance.Error(e); + } + } + + #endregion Melon Mod Utilities +} \ No newline at end of file diff --git a/LuaNetworkVariables/NetLuaModule.cs b/LuaNetworkVariables/NetLuaModule.cs new file mode 100644 index 0000000..319fefa --- /dev/null +++ b/LuaNetworkVariables/NetLuaModule.cs @@ -0,0 +1,137 @@ +using ABI_RC.Core.Base; +using ABI.Scripting.CVRSTL.Common; +using JetBrains.Annotations; +using NAK.LuaNetVars; +using MoonSharp.Interpreter; + +namespace NAK.LuaNetVars.Modules; + +[PublicAPI] // Indicates that this class is used and should not be considered unused +public class LuaNetModule : BaseScriptedStaticWrapper +{ + public const string MODULE_ID = "NetworkModule"; + + private LuaNetVarController _controller; + + public LuaNetModule(CVRLuaContext context) : base(context) + { + _controller = context.behaviour.AddComponentIfMissing(); + } + + internal static object RegisterUserData(Script script, CVRLuaContext context) + { + // Register the LuaNetModule type with MoonSharp + UserData.RegisterType(InteropAccessMode.Default, MODULE_ID); + return new LuaNetModule(context); + } + + #region Module Instance Methods + + /// + /// Registers a network variable that can be synchronized over the network. + /// + /// The name of the variable to register. + public void RegisterNetworkVar(string varName) + { + CheckIfCanAccessMethod(nameof(RegisterNetworkVar), false, + CVRLuaEnvironmentContext.CLIENT, CVRLuaObjectContext.ALL_BUT_EVENTS, CVRLuaOwnerContext.ANY); + + if (_controller == null) + { + LuaNetVarsMod.Logger.Error("LuaNetVarController is null."); + return; + } + + _controller.RegisterNetworkVar(varName); + } + + /// + /// Registers a callback function to be called when the specified network variable changes. + /// + /// The name of the variable to watch. + /// The Lua function to call when the variable changes. + public void RegisterNotifyCallback(string varName, DynValue callback) + { + CheckIfCanAccessMethod(nameof(RegisterNotifyCallback), false, + CVRLuaEnvironmentContext.CLIENT, CVRLuaObjectContext.ALL_BUT_EVENTS, CVRLuaOwnerContext.ANY); + + if (_controller == null) + { + LuaNetVarsMod.Logger.Error("LuaNetVarController is null."); + return; + } + + _controller.RegisterNotifyCallback(varName, callback); + } + + /// + /// Registers a callback function to be called when the specified event is received. + /// + /// The name of the event to watch. + /// The Lua function to call when the event occurs. + public void RegisterEventCallback(string eventName, DynValue callback) + { + CheckIfCanAccessMethod(nameof(RegisterEventCallback), false, + CVRLuaEnvironmentContext.CLIENT, CVRLuaObjectContext.ALL_BUT_EVENTS, CVRLuaOwnerContext.ANY); + + if (_controller == null) + { + LuaNetVarsMod.Logger.Error("LuaNetVarController is null."); + return; + } + + _controller.RegisterEventCallback(eventName, callback); + } + + /// + /// Sends a Lua event to other clients. + /// + /// The name of the event to send. + /// Optional arguments to send with the event. + public void SendLuaEvent(string eventName, params DynValue[] args) + { + CheckIfCanAccessMethod(nameof(SendLuaEvent), false, + CVRLuaEnvironmentContext.CLIENT, CVRLuaObjectContext.ALL_BUT_EVENTS, CVRLuaOwnerContext.ANY); + + if (_controller == null) + { + LuaNetVarsMod.Logger.Error("LuaNetVarController is null."); + return; + } + + _controller.SendLuaEvent(eventName, args); + } + + /// + /// Checks if the current client is the owner of the synchronized object. + /// + public bool IsSyncOwner() + { + CheckIfCanAccessMethod(nameof(IsSyncOwner), false, + CVRLuaEnvironmentContext.CLIENT, CVRLuaObjectContext.ALL_BUT_EVENTS, CVRLuaOwnerContext.ANY); + + if (_controller == null) + { + LuaNetVarsMod.Logger.Error("LuaNetVarController is null."); + return false; + } + + return _controller.IsSyncOwner(); + } + + public string GetSyncOwner() + { + CheckIfCanAccessMethod(nameof(GetSyncOwner), false, + CVRLuaEnvironmentContext.CLIENT, CVRLuaObjectContext.ALL_BUT_EVENTS, CVRLuaOwnerContext.ANY); + + if (_controller == null) + { + LuaNetVarsMod.Logger.Error("LuaNetVarController is null."); + return string.Empty; + } + + return _controller.GetSyncOwner(); + } + + #endregion +} \ No newline at end of file diff --git a/LuaNetworkVariables/NetworkVariables/LuaEventContext.cs b/LuaNetworkVariables/NetworkVariables/LuaEventContext.cs new file mode 100644 index 0000000..01fbdc4 --- /dev/null +++ b/LuaNetworkVariables/NetworkVariables/LuaEventContext.cs @@ -0,0 +1,41 @@ +using MoonSharp.Interpreter; +using ABI_RC.Core.Player; +using ABI_RC.Core.Savior; + +namespace NAK.LuaNetVars; + +public struct LuaEventContext +{ + private string SenderId { get; set; } + public string SenderName { get; private set; } + private DateTime LastInvokeTime { get; set; } + private double TimeSinceLastInvoke { get; set; } + private bool IsLocal { get; set; } + + public static LuaEventContext Create(string senderId, DateTime lastInvokeTime) + { + var playerName = CVRPlayerManager.Instance.TryGetPlayerName(senderId); + + return new LuaEventContext + { + SenderId = senderId, + SenderName = playerName ?? "Unknown", + LastInvokeTime = lastInvokeTime, + TimeSinceLastInvoke = (DateTime.Now - lastInvokeTime).TotalSeconds, + IsLocal = senderId == MetaPort.Instance.ownerId + }; + } + + public Table ToLuaTable(Script script) + { + Table table = new(script) + { + ["senderId"] = SenderId, + ["senderName"] = SenderName, + ["lastInvokeTime"] = LastInvokeTime.ToUniversalTime().ToString("O"), + ["timeSinceLastInvoke"] = TimeSinceLastInvoke, + ["isLocal"] = IsLocal + }; + return table; + } +} \ No newline at end of file diff --git a/LuaNetworkVariables/NetworkVariables/LuaEventTracker.cs b/LuaNetworkVariables/NetworkVariables/LuaEventTracker.cs new file mode 100644 index 0000000..ed60cee --- /dev/null +++ b/LuaNetworkVariables/NetworkVariables/LuaEventTracker.cs @@ -0,0 +1,60 @@ +namespace NAK.LuaNetVars; + +internal class LuaEventTracker +{ + private class EventMetadata + { + public DateTime LastInvokeTime { get; set; } + public Dictionary LastInvokeTimePerSender { get; } = new(); + } + + private readonly Dictionary _eventMetadata = new(); + + public DateTime GetLastInvokeTime(string eventName) + { + if (!_eventMetadata.TryGetValue(eventName, out var metadata)) + { + metadata = new EventMetadata(); + _eventMetadata[eventName] = metadata; + } + return metadata.LastInvokeTime; + } + + public DateTime GetLastInvokeTimeForSender(string eventName, string senderId) + { + if (!_eventMetadata.TryGetValue(eventName, out var metadata)) + { + metadata = new EventMetadata(); + _eventMetadata[eventName] = metadata; + } + + if (!metadata.LastInvokeTimePerSender.TryGetValue(senderId, out DateTime time)) + { + return DateTime.MinValue; + } + return time; + } + + public void UpdateInvokeTime(string eventName, string senderId) + { + if (!_eventMetadata.TryGetValue(eventName, out EventMetadata metadata)) + { + metadata = new EventMetadata(); + _eventMetadata[eventName] = metadata; + } + + DateTime now = DateTime.Now; + metadata.LastInvokeTime = now; + metadata.LastInvokeTimePerSender[senderId] = now; + } + + public void Clear() + { + _eventMetadata.Clear(); + } + + public void ClearEvent(string eventName) + { + _eventMetadata.Remove(eventName); + } +} \ No newline at end of file diff --git a/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Base.cs b/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Base.cs new file mode 100644 index 0000000..589c405 --- /dev/null +++ b/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Base.cs @@ -0,0 +1,146 @@ +using ABI_RC.Core.Savior; +using ABI_RC.Core.Util; +using ABI_RC.Systems.ModNetwork; +using ABI.CCK.Components; +using ABI.Scripting.CVRSTL.Common; +using MoonSharp.Interpreter; +using UnityEngine; + +namespace NAK.LuaNetVars; + +public partial class LuaNetVarController : MonoBehaviour +{ + private static readonly HashSet _hashes = new(); + + private const string MODULE_ID = "NAK.LNV:"; + + private int _uniquePathHash; + private string ModNetworkID { get; set; } + private CVRLuaClientBehaviour _luaClientBehaviour; + + private readonly Dictionary _registeredNetworkVars = new(); + private readonly Dictionary _registeredNotifyCallbacks = new(); + private readonly Dictionary _registeredEventCallbacks = new(); + private readonly HashSet _dirtyVariables = new(); + + private bool _requestInitialSync; + private CVRSpawnable _spawnable; + private CVRObjectSync _objectSync; + + #region Unity Events + + private void Awake() + { + if (!Initialize()) + return; + + // TODO: a manager script should be in charge of this + // TODO: disabling object will kill coroutine + StartCoroutine(SendVariableUpdatesCoroutine()); + } + + private void OnDestroy() + => Cleanup(); + + #endregion Unity Events + + #region Private Methods + + private bool Initialize() + { + if (!TryGetComponent(out _luaClientBehaviour)) return false; + if (!TryGetUniqueNetworkID(out _uniquePathHash)) return false; + + ModNetworkID = MODULE_ID + _uniquePathHash.ToString("X8"); + + if (ModNetworkID.Length > ModNetworkManager.MaxMessageIdLength) + { + LuaNetVarsMod.Logger.Error($"ModNetworkID ({ModNetworkID}) exceeds max length of {ModNetworkManager.MaxMessageIdLength} characters!"); + return false; + } + + _hashes.Add(_uniquePathHash); + ModNetworkManager.Subscribe(ModNetworkID, OnMessageReceived); + LuaNetVarsMod.Logger.Msg($"Registered LuaNetVarController with ModNetworkID: {ModNetworkID}"); + + switch (_luaClientBehaviour.Context.objContext) + { + case CVRLuaObjectContext.AVATAR: + _requestInitialSync = !_luaClientBehaviour.Context.IsWornByMe; + break; + case CVRLuaObjectContext.PROP: + _spawnable = _luaClientBehaviour.Context.RootComponent as CVRSpawnable; + _requestInitialSync = !_luaClientBehaviour.Context.IsSpawnedByMe; + break; + case CVRLuaObjectContext.WORLD: + _objectSync = GetComponentInParent(); + _requestInitialSync = true; // idk probably works + break; + default: + _requestInitialSync = true; + break; + } + + return true; + } + + // TODO: evaluate if having dedicated globals is better behaviour (i think so) + // private void ConfigureLuaEnvironment() + // { + // _luaClientBehaviour.script.Globals["SendLuaEvent"] = DynValue.NewCallback(SendLuaEventCallback); + // } + + private void Cleanup() + { + _eventTracker.Clear(); + + if (_uniquePathHash == 0 || string.IsNullOrEmpty(ModNetworkID)) + return; + + ModNetworkManager.Unsubscribe(ModNetworkID); + LuaNetVarsMod.Logger.Msg($"Unsubscribed LuaNetVarController with ModNetworkID: {ModNetworkID}"); + _hashes.Remove(_uniquePathHash); + } + + private System.Collections.IEnumerator SendVariableUpdatesCoroutine() + { + while (true) + { + yield return new WaitForSeconds(0.1f); + if (IsSyncOwner()) SendVariableUpdates(); + if (!_requestInitialSync) continue; + _requestInitialSync = false; + RequestVariableSync(); + } + } + + #endregion Private Methods + + #region Ownership Methods + + public bool IsSyncOwner() + { + if (_objectSync) return _objectSync.SyncedByMe; // idk + if (_spawnable) + { + if (_spawnable.IsSyncedByMe()) return true; // is held / attached locally + CVRSyncHelper.PropData propData = CVRSyncHelper.Props.Find(x => x.InstanceId == _spawnable.instanceId); + if (propData != null) return propData.syncedBy == MetaPort.Instance.ownerId; // last updated by me + return false; // not held / attached locally and not last updated by me + } + return false; + } + + public string GetSyncOwner() + { + if (_objectSync) return _objectSync.syncedBy; + if (_spawnable) + { + CVRSyncHelper.PropData propData = CVRSyncHelper.Props.Find(x => x.InstanceId == _spawnable.instanceId); + return propData?.syncedBy ?? string.Empty; + } + return string.Empty; + } + + #endregion Ownership Methods +} \ No newline at end of file diff --git a/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Networking.cs b/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Networking.cs new file mode 100644 index 0000000..47f6415 --- /dev/null +++ b/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Networking.cs @@ -0,0 +1,214 @@ +using ABI_RC.Core.Savior; +using ABI_RC.Systems.ModNetwork; +using MoonSharp.Interpreter; +using Unity.Services.Authentication.Internal; + +namespace NAK.LuaNetVars +{ + public partial class LuaNetVarController + { + private enum MessageType : byte + { + LuaVariable = 0, + LuaEvent = 1, + SyncVariables = 2, + RequestSync = 3 + } + + private readonly LuaEventTracker _eventTracker = new(); + + #region Mod Network Events + + private void OnMessageReceived(ModNetworkMessage msg) + { + msg.Read(out byte msgTypeRaw); + if (!Enum.IsDefined(typeof(MessageType), msgTypeRaw)) return; + + MessageType msgType = (MessageType)msgTypeRaw; + switch (msgType) + { + case MessageType.LuaVariable: + HandleLuaVariableUpdate(msg); + break; + case MessageType.LuaEvent: + HandleLuaEvent(msg); + break; + case MessageType.SyncVariables: + HandleSyncVariables(msg); + break; + case MessageType.RequestSync: + HandleRequestSyncVariables(msg); + break; + } + } + + private void HandleLuaVariableUpdate(ModNetworkMessage msg) + { + msg.Read(out string varName); + DynValue newValue = DeserializeDynValue(msg); + + LuaNetVarsMod.Logger.Msg($"Received LuaVariable update: {varName} = {newValue}"); + + if (_registeredNetworkVars.TryGetValue(varName, out DynValue var)) + { + UpdateNetworkVariable(varName, var, newValue); + } + else + { + LuaNetVarsMod.Logger.Warning($"Received update for unregistered variable {varName}"); + } + } + + private void HandleLuaEvent(ModNetworkMessage msg) + { + string senderId = msg.Sender; + msg.Read(out string eventName); + msg.Read(out int argsCount); + + DateTime lastInvokeTime = _eventTracker.GetLastInvokeTimeForSender(eventName, senderId); + LuaEventContext context = LuaEventContext.Create(senderId, lastInvokeTime); + + // Update tracking + _eventTracker.UpdateInvokeTime(eventName, senderId); + + // Read event arguments + var args = new DynValue[argsCount + 1]; // +1 for context + args[0] = DynValue.FromObject(_luaClientBehaviour.script, context.ToLuaTable(_luaClientBehaviour.script)); + + for (int i = 0; i < argsCount; i++) + { + args[i + 1] = DeserializeDynValue(msg); + } + + LuaNetVarsMod.Logger.Msg($"Received LuaEvent: {eventName} from {context.SenderName} with {argsCount} args"); + + InvokeLuaEvent(eventName, args); + } + + private void HandleSyncVariables(ModNetworkMessage msg) + { + msg.Read(out int varCount); + for (int i = 0; i < varCount; i++) + { + msg.Read(out string varName); + DynValue newValue = DeserializeDynValue(msg); + + if (_registeredNetworkVars.TryGetValue(varName, out DynValue var)) + { + UpdateNetworkVariable(varName, var, newValue); + } + else + { + LuaNetVarsMod.Logger.Warning($"Received sync for unregistered variable {varName}"); + } + } + } + + private void HandleRequestSyncVariables(ModNetworkMessage msg) + { + if (!IsSyncOwner()) return; + SendVariableSyncToUser(msg.Sender); + } + + #endregion + + #region Event Invocation + + private void InvokeLuaEvent(string eventName, DynValue[] args) + { + if (_registeredEventCallbacks.TryGetValue(eventName, out DynValue callback)) + { + _luaClientBehaviour.script.Call(callback, args); + } + else + { + LuaNetVarsMod.Logger.Warning($"No registered callback for event {eventName}"); + } + } + + #endregion + + #region Sending Methods + + private void SendVariableUpdates() + { + if (_dirtyVariables.Count == 0) return; + + using ModNetworkMessage modMsg = new(ModNetworkID); // can pass target userids as params if needed + modMsg.Write((byte)MessageType.SyncVariables); + modMsg.Write(_dirtyVariables.Count); + modMsg.Send(); + + foreach (var varName in _dirtyVariables) + { + modMsg.Write(varName); + SerializeDynValue(modMsg, _registeredNetworkVars[varName]); + } + + _dirtyVariables.Clear(); + } + + private void SendVariableSyncToUser(string userId) + { + using ModNetworkMessage modMsg = new(ModNetworkID, userId); + modMsg.Write((byte)MessageType.SyncVariables); + modMsg.Write(_registeredNetworkVars.Count); + foreach (var kvp in _registeredNetworkVars) + { + modMsg.Write(kvp.Key); + SerializeDynValue(modMsg, kvp.Value); + } + modMsg.Send(); + + LuaNetVarsMod.Logger.Msg($"Sent variable sync to {userId}"); + } + + private void RequestVariableSync() + { + using ModNetworkMessage modMsg = new(ModNetworkID); + modMsg.Write((byte)MessageType.RequestSync); + modMsg.Send(); + LuaNetVarsMod.Logger.Msg("Requested variable sync"); + } + + // private DynValue SendLuaEventCallback(ScriptExecutionContext context, CallbackArguments args) + // { + // if (args.Count < 1) return DynValue.Nil; + // + // var eventName = args[0].CastToString(); + // var eventArgs = args.GetArray().Skip(1).ToArray(); + // + // SendLuaEvent(eventName, eventArgs); + // + // return DynValue.Nil; + // } + + internal void SendLuaEvent(string eventName, DynValue[] args) + { + string senderId = MetaPort.Instance.ownerId; + DateTime lastInvokeTime = _eventTracker.GetLastInvokeTimeForSender(eventName, senderId); + LuaEventContext context = LuaEventContext.Create(senderId, lastInvokeTime); + + // Update tracking + _eventTracker.UpdateInvokeTime(eventName, senderId); + + var argsWithContext = new DynValue[args.Length + 1]; + argsWithContext[0] = DynValue.FromObject(_luaClientBehaviour.script, context.ToLuaTable(_luaClientBehaviour.script)); + Array.Copy(args, 0, argsWithContext, 1, args.Length); + + InvokeLuaEvent(eventName, argsWithContext); + + using ModNetworkMessage modMsg = new(ModNetworkID); + modMsg.Write((byte)MessageType.LuaEvent); + modMsg.Write(eventName); + modMsg.Write(args.Length); + + foreach (DynValue arg in args) + SerializeDynValue(modMsg, arg); + + modMsg.Send(); + } + + #endregion + } +} \ No newline at end of file diff --git a/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Registration.cs b/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Registration.cs new file mode 100644 index 0000000..e68170f --- /dev/null +++ b/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Registration.cs @@ -0,0 +1,101 @@ +using MoonSharp.Interpreter; + +namespace NAK.LuaNetVars; + +public partial class LuaNetVarController +{ + internal void RegisterNetworkVar(string varName) + { + if (_registeredNetworkVars.ContainsKey(varName)) + { + LuaNetVarsMod.Logger.Warning($"Network variable {varName} already registered!"); + return; + } + + _registeredNetworkVars[varName] = DynValue.Nil; + _luaClientBehaviour.script.Globals[varName] = DynValue.Nil; + + RegisterGetterFunction(varName); + RegisterSetterFunction(varName); + + LuaNetVarsMod.Logger.Msg($"Registered network variable {varName}"); + } + + private void RegisterGetterFunction(string varName) + { + _luaClientBehaviour.script.Globals["Get" + varName] = DynValue.NewCallback((context, args) => + { + return _registeredNetworkVars.TryGetValue(varName, out var value) ? value : DynValue.Nil; + }); + } + + private void RegisterSetterFunction(string varName) + { + _luaClientBehaviour.script.Globals["Set" + varName] = DynValue.NewCallback((context, args) => + { + if (args.Count < 1) return DynValue.Nil; + + var newValue = args[0]; + if (!IsSupportedDynValue(newValue)) + { + LuaNetVarsMod.Logger.Error($"Unsupported DynValue type: {newValue.Type} for variable {varName}"); + return DynValue.Nil; + } + + if (_registeredNetworkVars.TryGetValue(varName, out var oldValue)) + { + UpdateNetworkVariable(varName, oldValue, newValue); + } + + return DynValue.Nil; + }); + } + + private void UpdateNetworkVariable(string varName, DynValue oldValue, DynValue newValue) + { + _registeredNetworkVars[varName] = newValue; + _luaClientBehaviour.script.Globals[varName] = newValue; + _dirtyVariables.Add(varName); + + if (_registeredNotifyCallbacks.TryGetValue(varName, out var callback)) + { + _luaClientBehaviour.script.Call(callback, DynValue.NewString(varName), oldValue, newValue); + } + } + + internal void RegisterNotifyCallback(string varName, DynValue callback) + { + if (!ValidateCallback(callback) || !ValidateNetworkVar(varName)) return; + + if (_registeredNotifyCallbacks.ContainsKey(varName)) + LuaNetVarsMod.Logger.Warning($"Overwriting notify callback for {varName}"); + + _registeredNotifyCallbacks[varName] = callback; + LuaNetVarsMod.Logger.Msg($"Registered notify callback for {varName}"); + } + + internal void RegisterEventCallback(string eventName, DynValue callback) + { + if (!ValidateCallback(callback)) return; + + if (_registeredEventCallbacks.ContainsKey(eventName)) + LuaNetVarsMod.Logger.Warning($"Overwriting event callback for {eventName}"); + + _registeredEventCallbacks[eventName] = callback; + LuaNetVarsMod.Logger.Msg($"Registered event callback for {eventName}"); + } + + private bool ValidateCallback(DynValue callback) + { + if (callback?.Function != null) return true; + LuaNetVarsMod.Logger.Error("Passed DynValue must be a function"); + return false; + } + + private bool ValidateNetworkVar(string varName) + { + if (_registeredNetworkVars.ContainsKey(varName)) return true; + LuaNetVarsMod.Logger.Error($"Attempted to register notify callback for non-registered variable {varName}."); + return false; + } +} \ No newline at end of file diff --git a/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Serialization.cs b/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Serialization.cs new file mode 100644 index 0000000..57221a7 --- /dev/null +++ b/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Serialization.cs @@ -0,0 +1,62 @@ +using ABI_RC.Systems.ModNetwork; +using MoonSharp.Interpreter; + +namespace NAK.LuaNetVars; + +public partial class LuaNetVarController +{ + private static DynValue DeserializeDynValue(ModNetworkMessage msg) + { + msg.Read(out byte dataTypeByte); + DataType dataType = (DataType)dataTypeByte; + + switch (dataType) + { + case DataType.Boolean: + msg.Read(out bool boolValue); + return DynValue.NewBoolean(boolValue); + case DataType.Number: + msg.Read(out double numberValue); + return DynValue.NewNumber(numberValue); + case DataType.String: + msg.Read(out string stringValue); + return DynValue.NewString(stringValue); + case DataType.Nil: + return DynValue.Nil; + default: + LuaNetVarsMod.Logger.Error($"Unsupported data type received: {dataType}"); + return DynValue.Nil; + } + } + + private static void SerializeDynValue(ModNetworkMessage msg, DynValue value) + { + switch (value.Type) + { + case DataType.Boolean: + msg.Write((byte)DataType.Boolean); + msg.Write(value.Boolean); + break; + case DataType.Number: + msg.Write((byte)DataType.Number); + msg.Write(value.Number); + break; + case DataType.String: + msg.Write((byte)DataType.String); + msg.Write(value.String); + break; + case DataType.Nil: + msg.Write((byte)DataType.Nil); + break; + default: + LuaNetVarsMod.Logger.Error($"Unsupported DynValue type: {value.Type}"); + msg.Write((byte)DataType.Nil); + break; + } + } + + private static bool IsSupportedDynValue(DynValue value) + { + return value.Type is DataType.Boolean or DataType.Number or DataType.String or DataType.Nil; + } +} \ No newline at end of file diff --git a/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Utility.cs b/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Utility.cs new file mode 100644 index 0000000..7712781 --- /dev/null +++ b/LuaNetworkVariables/NetworkVariables/LuaNetVarController.Utility.cs @@ -0,0 +1,57 @@ +using ABI_RC.Core.Savior; +using UnityEngine; + +namespace NAK.LuaNetVars; + +public partial class LuaNetVarController +{ + private bool TryGetUniqueNetworkID(out int hash) + { + string path = GetGameObjectPath(transform); + hash = path.GetHashCode(); + + // Check if it already exists (this **should** only matter in worlds) + if (_hashes.Contains(hash)) + { + LuaNetVarsMod.Logger.Warning($"Duplicate RelativeSyncMarker found at path {path}"); + if (!FindAvailableHash(ref hash)) // Super lazy fix idfc + { + LuaNetVarsMod.Logger.Error($"Failed to find available hash for RelativeSyncMarker after 16 tries! {path}"); + return false; + } + } + + _hashes.Add(hash); + + return true; + + static bool FindAvailableHash(ref int hash) + { + for (int i = 0; i < 16; i++) + { + hash += 1; + if (!_hashes.Contains(hash)) return true; + } + return false; // Failed to find a hash in 16 tries + } + } + + private static string GetGameObjectPath(Transform transform) + { + string path = transform.name; + while (transform.parent != null) + { + transform = transform.parent; + + // Only true at root of local player object + if (transform.CompareTag("Player")) + { + path = MetaPort.Instance.ownerId + "/" + path; + break; + } + + path = transform.name + "/" + path; + } + return path; + } +} \ No newline at end of file diff --git a/LuaNetworkVariables/Patches.cs b/LuaNetworkVariables/Patches.cs new file mode 100644 index 0000000..ce861fe --- /dev/null +++ b/LuaNetworkVariables/Patches.cs @@ -0,0 +1,65 @@ +using ABI_RC.Core.Base; +using ABI_RC.Core.Savior; +using ABI_RC.Core.Util; +using ABI.CCK.Components; +using ABI.Scripting.CVRSTL.Client; +using ABI.Scripting.CVRSTL.Common; +using HarmonyLib; +using MoonSharp.Interpreter; +using NAK.LuaNetVars.Modules; +using UnityEngine; + +namespace NAK.LuaNetVars.Patches; + +internal static class LuaScriptFactory_Patches +{ + [HarmonyPostfix] + [HarmonyPatch(typeof(LuaScriptFactory.CVRRequireModule), nameof(LuaScriptFactory.CVRRequireModule.Require))] + private static void Postfix_CVRRequireModule_require( + string moduleFriendlyName, + ref LuaScriptFactory.CVRRequireModule __instance, + ref object __result, + ref Script ____script, + ref CVRLuaContext ____context) + { + if (LuaNetModule.MODULE_ID != moduleFriendlyName) + return; // not our module + + __result = LuaNetModule.RegisterUserData(____script, ____context); + __instance.RegisteredModules[LuaNetModule.MODULE_ID] = __result; // add module to cache + } +} + +internal static class CVRSyncHelper_Patches +{ + [HarmonyPostfix] + [HarmonyPatch(typeof(CVRSyncHelper), nameof(CVRSyncHelper.UpdatePropValues))] + private static void Postfix_CVRSyncHelper_UpdatePropValues( + Vector3 position, Vector3 rotation, Vector3 scale, + float[] syncValues, string guid, string instanceId, + Span subSyncValues, int numSyncValues, int syncType = 0) + { + CVRSyncHelper.PropData propData = CVRSyncHelper.Props.Find(prop => prop.InstanceId == instanceId); + if (propData == null) return; + + // Update locally stored prop data with new values + // as GS does not reply with our own data... + + propData.PositionX = position.x; + propData.PositionY = position.y; + propData.PositionZ = position.z; + propData.RotationX = rotation.x; + propData.RotationY = rotation.y; + propData.RotationZ = rotation.z; + propData.ScaleX = scale.x; + propData.ScaleY = scale.y; + propData.ScaleZ = scale.z; + propData.CustomFloatsAmount = numSyncValues; + for (int i = 0; i < numSyncValues; i++) + propData.CustomFloats[i] = syncValues[i]; + + //propData.SpawnedBy + propData.syncedBy = MetaPort.Instance.ownerId; + propData.syncType = syncType; + } +} \ No newline at end of file diff --git a/LuaNetworkVariables/Properties/AssemblyInfo.cs b/LuaNetworkVariables/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..4cc49cf --- /dev/null +++ b/LuaNetworkVariables/Properties/AssemblyInfo.cs @@ -0,0 +1,32 @@ +using NAK.LuaNetVars.Properties; +using MelonLoader; +using System.Reflection; + +[assembly: AssemblyVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyTitle(nameof(NAK.LuaNetVars))] +[assembly: AssemblyCompany(AssemblyInfoParams.Author)] +[assembly: AssemblyProduct(nameof(NAK.LuaNetVars))] + +[assembly: MelonInfo( + typeof(NAK.LuaNetVars.LuaNetVarsMod), + nameof(NAK.LuaNetVars), + AssemblyInfoParams.Version, + AssemblyInfoParams.Author, + downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/WhereAmIPointing" +)] + +[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")] +[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] +[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)] +[assembly: MelonColor(255, 246, 25, 99)] // red-pink +[assembly: MelonAuthorColor(255, 158, 21, 32)] // red +[assembly: HarmonyDontPatchAll] + +namespace NAK.LuaNetVars.Properties; +internal static class AssemblyInfoParams +{ + public const string Version = "1.0.0"; + public const string Author = "NotAKidoS"; +} \ No newline at end of file diff --git a/LuaNetworkVariables/README.md b/LuaNetworkVariables/README.md new file mode 100644 index 0000000..c78a56a --- /dev/null +++ b/LuaNetworkVariables/README.md @@ -0,0 +1,14 @@ +# WhereAmIPointing + +Simple mod that makes your controller rays always visible when the menus are open. Useful for when you're trying to aim at something in the distance. Also visualizes which ray is being used for menu interaction. + +--- + +Here is the block of text where I tell you this mod is not affiliated with or endorsed by ABI. +https://documentation.abinteractive.net/official/legal/tos/#7-modding-our-games + +> This mod is an independent creation not affiliated with, supported by, or approved by Alpha Blend Interactive. + +> Use of this mod is done so at the user's own risk and the creator cannot be held responsible for any issues arising from its use. + +> To the best of my knowledge, I have adhered to the Modding Guidelines established by Alpha Blend Interactive. diff --git a/LuaNetworkVariables/SyncedBehaviour/MNSyncedBehaviour.cs b/LuaNetworkVariables/SyncedBehaviour/MNSyncedBehaviour.cs new file mode 100644 index 0000000..3576155 --- /dev/null +++ b/LuaNetworkVariables/SyncedBehaviour/MNSyncedBehaviour.cs @@ -0,0 +1,326 @@ +using UnityEngine; +using System; +using System.Collections.Generic; +using System.Threading; +using ABI_RC.Core.Savior; +using ABI_RC.Systems.ModNetwork; + +namespace NAK.LuaNetVars +{ + public abstract class MNSyncedBehaviour : IDisposable + { + // Add static property for clarity + protected static string LocalUserId => MetaPort.Instance.ownerId; + + protected enum MessageType : byte + { + OwnershipRequest, + OwnershipResponse, + OwnershipTransfer, + StateRequest, + StateUpdate, + CustomData + } + + protected enum OwnershipResponse : byte + { + Accepted, + Rejected + } + + protected readonly string networkId; + protected string currentOwnerId; + private readonly bool autoAcceptTransfers; + private readonly Dictionary> pendingRequests; + private bool isInitialized; + private bool disposedValue; + private bool isSoftOwner = false; + private Timer stateRequestTimer; + private const int StateRequestTimeout = 3000; // 3 seconds + + public string CurrentOwnerId => currentOwnerId; + public bool HasOwnership => currentOwnerId == LocalUserId; + + protected MNSyncedBehaviour(string networkId, string currentOwnerId = "", bool autoAcceptTransfers = false) + { + this.networkId = networkId; + this.currentOwnerId = currentOwnerId; + this.autoAcceptTransfers = autoAcceptTransfers; + this.pendingRequests = new Dictionary>(); + + ModNetworkManager.Subscribe(networkId, OnMessageReceived); + + if (!HasOwnership) + RequestInitialState(); + else + isInitialized = true; + } + + private void RequestInitialState() + { + using ModNetworkMessage msg = new(networkId); + msg.Write((byte)MessageType.StateRequest); + msg.Send(); + + stateRequestTimer = new Timer(StateRequestTimeoutCallback, null, StateRequestTimeout, Timeout.Infinite); + } + + private void StateRequestTimeoutCallback(object state) + { + // If isInitialized is still false, we assume soft ownership + if (!isInitialized) + { + currentOwnerId = LocalUserId; + isSoftOwner = true; + isInitialized = true; + OnOwnershipChanged(currentOwnerId); + } + + stateRequestTimer.Dispose(); + stateRequestTimer = null; + } + + public virtual void RequestOwnership(Action callback = null) + { + if (HasOwnership) + { + callback?.Invoke(true); + return; + } + + using (ModNetworkMessage msg = new(networkId)) + { + msg.Write((byte)MessageType.OwnershipRequest); + msg.Send(); + } + + if (callback != null) + { + pendingRequests[LocalUserId] = callback; + } + } + + protected void SendNetworkedData(Action writeData) + { + if (!HasOwnership) + { + Debug.LogWarning($"[MNSyncedBehaviour] Cannot send data without ownership. NetworkId: {networkId}"); + return; + } + + using (ModNetworkMessage msg = new(networkId)) + { + msg.Write((byte)MessageType.CustomData); + writeData(msg); + msg.Send(); + } + } + + protected virtual void OnMessageReceived(ModNetworkMessage message) + { + message.Read(out byte type); + MessageType messageType = (MessageType)type; + + if (!Enum.IsDefined(typeof(MessageType), messageType)) + return; + + switch (messageType) + { + case MessageType.OwnershipRequest: + if (!HasOwnership) break; + HandleOwnershipRequest(message); + break; + + case MessageType.OwnershipResponse: + if (message.Sender != currentOwnerId) break; + HandleOwnershipResponse(message); + break; + + case MessageType.OwnershipTransfer: + if (message.Sender != currentOwnerId) break; + currentOwnerId = message.Sender; + OnOwnershipChanged(currentOwnerId); + break; + + case MessageType.StateRequest: + if (!HasOwnership) break; // this is the only safeguard against ownership hijacking... idk how to prevent it + // TODO: only respond to a StateUpdate if expecting one + HandleStateRequest(message); + break; + + case MessageType.StateUpdate: + // Accept state updates from current owner or if we have soft ownership + if (message.Sender != currentOwnerId && !isSoftOwner) break; + HandleStateUpdate(message); + break; + + case MessageType.CustomData: + if (message.Sender != currentOwnerId) + { + // If we have soft ownership and receive data from real owner, accept it + if (isSoftOwner && message.Sender != LocalUserId) + { + currentOwnerId = message.Sender; + isSoftOwner = false; + OnOwnershipChanged(currentOwnerId); + } + else + { + // Ignore data from non-owner + break; + } + } + HandleCustomData(message); + break; + } + } + + protected virtual void HandleOwnershipRequest(ModNetworkMessage message) + { + if (!HasOwnership) + return; + + string requesterId = message.Sender; + var response = autoAcceptTransfers ? OwnershipResponse.Accepted : + OnOwnershipRequested(requesterId); + + using (ModNetworkMessage responseMsg = new(networkId)) + { + responseMsg.Write((byte)MessageType.OwnershipResponse); + responseMsg.Write((byte)response); + responseMsg.Send(); + } + + if (response == OwnershipResponse.Accepted) + { + TransferOwnership(requesterId); + } + } + + protected virtual void HandleOwnershipResponse(ModNetworkMessage message) + { + message.Read(out byte responseByte); + OwnershipResponse response = (OwnershipResponse)responseByte; + + if (pendingRequests.TryGetValue(LocalUserId, out var callback)) + { + bool accepted = response == OwnershipResponse.Accepted; + callback(accepted); + pendingRequests.Remove(LocalUserId); + + // Update ownership locally only if accepted + if (accepted) + { + currentOwnerId = LocalUserId; + OnOwnershipChanged(currentOwnerId); + } + } + } + + protected virtual void HandleStateRequest(ModNetworkMessage message) + { + if (!HasOwnership) + return; + + using ModNetworkMessage response = new(networkId, message.Sender); + response.Write((byte)MessageType.StateUpdate); + WriteState(response); + response.Send(); + } + + protected virtual void HandleStateUpdate(ModNetworkMessage message) + { + currentOwnerId = message.Sender; + isSoftOwner = false; + ReadState(message); + isInitialized = true; + + // Dispose of the state request timer if it's still running + if (stateRequestTimer != null) + { + stateRequestTimer.Dispose(); + stateRequestTimer = null; + } + } + + protected virtual void HandleCustomData(ModNetworkMessage message) + { + if (!isInitialized) + { + Debug.LogWarning($"[MNSyncedBehaviour] Received custom data before initialization. NetworkId: {networkId}"); + return; + } + + if (message.Sender != currentOwnerId) + { + // If we have soft ownership and receive data from real owner, accept it + if (isSoftOwner && message.Sender != LocalUserId) + { + currentOwnerId = message.Sender; + isSoftOwner = false; + OnOwnershipChanged(currentOwnerId); + } + else + { + // Ignore data from non-owner + return; + } + } + + ReadCustomData(message); + } + + protected virtual void TransferOwnership(string newOwnerId) + { + using (ModNetworkMessage msg = new(networkId)) + { + msg.Write((byte)MessageType.OwnershipTransfer); + msg.Write(newOwnerId); // Include the new owner ID in transfer message + msg.Send(); + } + + currentOwnerId = newOwnerId; + OnOwnershipChanged(newOwnerId); + } + + protected virtual OwnershipResponse OnOwnershipRequested(string requesterId) + { + return OwnershipResponse.Rejected; + } + + protected virtual void OnOwnershipChanged(string newOwnerId) + { + // Override to handle ownership changes + } + + protected virtual void WriteState(ModNetworkMessage message) { } + protected virtual void ReadState(ModNetworkMessage message) { } + protected virtual void ReadCustomData(ModNetworkMessage message) { } + + protected virtual void Dispose(bool disposing) + { + if (disposedValue) + return; + + if (disposing) + { + ModNetworkManager.Unsubscribe(networkId); + pendingRequests.Clear(); + + if (stateRequestTimer != null) + { + stateRequestTimer.Dispose(); + stateRequestTimer = null; + } + } + + disposedValue = true; + } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } +} \ No newline at end of file diff --git a/LuaNetworkVariables/SyncedBehaviour/PickupableBehaviour.cs b/LuaNetworkVariables/SyncedBehaviour/PickupableBehaviour.cs new file mode 100644 index 0000000..30fddc1 --- /dev/null +++ b/LuaNetworkVariables/SyncedBehaviour/PickupableBehaviour.cs @@ -0,0 +1,161 @@ +using UnityEngine; +using ABI_RC.Core.InteractionSystem; +using ABI_RC.Systems.ModNetwork; + +namespace NAK.LuaNetVars; + +public class PickupableBehaviour : MNSyncedBehaviour +{ + private enum PickupMessageType : byte + { + GrabState, + Transform + } + + private bool isHeld; + private string holderId; + private Vector3 lastPosition; + private Quaternion lastRotation; + + public PickupableObject Pickupable { get; private set; } + + public PickupableBehaviour(string networkId, PickupableObject pickupable) : base(networkId, autoAcceptTransfers: false) + { + Pickupable = pickupable; + isHeld = false; + holderId = string.Empty; + lastPosition = pickupable.transform.position; + lastRotation = pickupable.transform.rotation; + } + + public void OnGrabbed(InteractionContext context) + { + RequestOwnership(success => { + if (success) + { + isHeld = true; + holderId = LocalUserId; + SendNetworkedData(WriteGrabState); + } + else + { + // Ownership request failed, drop the object + Pickupable.ControllerRay = null; // Force drop + } + }); + } + + public void OnDropped() + { + if (!HasOwnership) return; + + isHeld = false; + holderId = string.Empty; + SendNetworkedData(WriteGrabState); + } + + public void UpdateTransform(Vector3 position, Quaternion rotation) + { + if (!HasOwnership || !isHeld) return; + + lastPosition = position; + lastRotation = rotation; + SendNetworkedData(WriteTransform); + } + + protected override OwnershipResponse OnOwnershipRequested(string requesterId) + { + // If the object is held by the current owner, reject the transfer + if (isHeld && holderId == LocalUserId) + return OwnershipResponse.Rejected; + + // If theft is disallowed and the object is held by someone, reject the transfer + if (Pickupable.DisallowTheft && !string.IsNullOrEmpty(holderId)) + return OwnershipResponse.Rejected; + + return OwnershipResponse.Accepted; + } + + protected override void WriteState(ModNetworkMessage message) + { + message.Write(isHeld); + message.Write(holderId); + message.Write(lastPosition); + message.Write(lastRotation); + } + + protected override void ReadState(ModNetworkMessage message) + { + message.Read(out isHeld); + message.Read(out holderId); + message.Read(out lastPosition); + message.Read(out lastRotation); + + UpdatePickupableState(); + } + + private void WriteGrabState(ModNetworkMessage message) + { + message.Write((byte)PickupMessageType.GrabState); + message.Write(isHeld); + message.Write(holderId); + } + + private void WriteTransform(ModNetworkMessage message) + { + message.Write((byte)PickupMessageType.Transform); + message.Write(lastPosition); + message.Write(lastRotation); + } + + protected override void ReadCustomData(ModNetworkMessage message) + { + message.Read(out byte messageType); + + switch ((PickupMessageType)messageType) + { + case PickupMessageType.GrabState: + message.Read(out isHeld); + message.Read(out holderId); + break; + + case PickupMessageType.Transform: + message.Read(out Vector3 position); + message.Read(out Quaternion rotation); + lastPosition = position; + lastRotation = rotation; + break; + } + + UpdatePickupableState(); + } + + private void UpdatePickupableState() + { + // Update transform if we're not the holder + if (!isHeld || holderId != LocalUserId) + { + Pickupable.transform.position = lastPosition; + Pickupable.transform.rotation = lastRotation; + } + + // Force drop if we were holding but someone else took ownership + if (isHeld && holderId != LocalUserId) + { + Pickupable.ControllerRay = null; // Force drop + } + } + + protected override void OnOwnershipChanged(string newOwnerId) + { + base.OnOwnershipChanged(newOwnerId); + + // If we lost ownership and were holding, force drop + if (!HasOwnership && holderId == LocalUserId) + { + isHeld = false; + holderId = string.Empty; + Pickupable.ControllerRay = null; // Force drop + } + } +} \ No newline at end of file diff --git a/LuaNetworkVariables/SyncedBehaviour/PickupableObject.cs b/LuaNetworkVariables/SyncedBehaviour/PickupableObject.cs new file mode 100644 index 0000000..76b495a --- /dev/null +++ b/LuaNetworkVariables/SyncedBehaviour/PickupableObject.cs @@ -0,0 +1,72 @@ +using ABI_RC.Core.InteractionSystem; +using ABI_RC.Core.InteractionSystem.Base; +using UnityEngine; + +namespace NAK.LuaNetVars; + +public class PickupableObject : Pickupable +{ + [SerializeField] private bool canPickup = true; + [SerializeField] private bool disallowTheft = false; + [SerializeField] private float maxGrabDistance = 2f; + [SerializeField] private float maxPushDistance = 2f; + [SerializeField] private bool isAutoHold = false; + [SerializeField] private bool allowRotation = true; + [SerializeField] private bool allowPushPull = true; + [SerializeField] private bool allowInteraction = true; + + private PickupableBehaviour behaviour; + private bool isInitialized; + + private void Awake() + { + // Generate a unique network ID based on the instance ID + string networkId = $"pickup_{gameObject.name}"; + behaviour = new PickupableBehaviour(networkId, this); + isInitialized = true; + } + + private void OnDestroy() + { + behaviour?.Dispose(); + } + + private void Update() + { + if (behaviour?.HasOwnership == true) + { + transform.SetPositionAndRotation(ControllerRay.pivotPoint.position, ControllerRay.pivotPoint.rotation); + behaviour.UpdateTransform(transform.position, transform.rotation); + } + } + + #region Pickupable Implementation + + public override void OnGrab(InteractionContext context, Vector3 grabPoint) + { + if (!isInitialized) return; + behaviour.OnGrabbed(context); + } + + public override void OnDrop(InteractionContext context) + { + if (!isInitialized) return; + behaviour.OnDropped(); + } + + public override void OnFlingTowardsTarget(Vector3 target) + { + // ignore + } + + public override bool CanPickup => canPickup; + public override bool DisallowTheft => disallowTheft; + public override float MaxGrabDistance => maxGrabDistance; + public override float MaxPushDistance => maxPushDistance; + public override bool IsAutoHold => isAutoHold; + public override bool IsObjectRotationAllowed => allowRotation; + public override bool IsObjectPushPullAllowed => allowPushPull; + public override bool IsObjectInteractionAllowed => allowInteraction; + + #endregion Pickupable Implementation +} \ No newline at end of file diff --git a/LuaNetworkVariables/SyncedBehaviour/TestSyncedBehaviour.cs b/LuaNetworkVariables/SyncedBehaviour/TestSyncedBehaviour.cs new file mode 100644 index 0000000..4941aaa --- /dev/null +++ b/LuaNetworkVariables/SyncedBehaviour/TestSyncedBehaviour.cs @@ -0,0 +1,63 @@ +using ABI_RC.Systems.ModNetwork; +using UnityEngine; + +namespace NAK.LuaNetVars; + + +// Test implementation +public class TestSyncedBehaviour : MNSyncedBehaviour +{ + private readonly System.Random random = new(); + private int testValue; + private int incrementValue; + + public TestSyncedBehaviour(string networkId) : base(networkId, autoAcceptTransfers: true) + { + Debug.Log($"[TestSyncedBehaviour] Initialized. NetworkId: {networkId}"); + } + + public void SendTestMessage() + { + if (!HasOwnership) return; + + SendNetworkedData(msg => { + testValue = random.Next(1000); + incrementValue++; + msg.Write(testValue); + msg.Write(incrementValue); + }); + } + + protected override void WriteState(ModNetworkMessage message) + { + message.Write(testValue); + message.Write(incrementValue); + } + + protected override void ReadState(ModNetworkMessage message) + { + message.Read(out testValue); + message.Read(out incrementValue); + Debug.Log($"[TestSyncedBehaviour] State synchronized. TestValue: {testValue}, IncrementValue: {incrementValue}"); + } + + protected override void ReadCustomData(ModNetworkMessage message) + { + message.Read(out int receivedValue); + message.Read(out int receivedIncrement); + testValue = receivedValue; + incrementValue = receivedIncrement; + Debug.Log($"[TestSyncedBehaviour] Received custom data: TestValue: {testValue}, IncrementValue: {incrementValue}"); + } + + protected override void OnOwnershipChanged(string newOwnerId) + { + Debug.Log($"[TestSyncedBehaviour] Ownership changed to: {newOwnerId}"); + } + + protected override OwnershipResponse OnOwnershipRequested(string requesterId) + { + Debug.Log($"[TestSyncedBehaviour] Ownership requested by: {requesterId}"); + return OwnershipResponse.Accepted; + } +} \ No newline at end of file diff --git a/LuaNetworkVariables/SyncedBehaviour/TestSyncedObject.cs b/LuaNetworkVariables/SyncedBehaviour/TestSyncedObject.cs new file mode 100644 index 0000000..76d6281 --- /dev/null +++ b/LuaNetworkVariables/SyncedBehaviour/TestSyncedObject.cs @@ -0,0 +1,42 @@ +using ABI_RC.Core.Savior; +using UnityEngine; + +namespace NAK.LuaNetVars; + +public class TestSyncedObject : MonoBehaviour +{ + private const string TEST_NETWORK_ID = "test.synced.object.1"; + private TestSyncedBehaviour syncBehaviour; + private float messageTimer = 0f; + private const float MESSAGE_INTERVAL = 2f; + + private void Start() + { + syncBehaviour = new TestSyncedBehaviour(TEST_NETWORK_ID); + Debug.Log($"TestSyncedObject started. Local Player ID: {MetaPort.Instance.ownerId}"); + } + + private void Update() + { + // Request ownership on Space key + if (Input.GetKeyDown(KeyCode.Space)) + { + Debug.Log("Requesting ownership..."); + syncBehaviour.RequestOwnership((success) => + { + Debug.Log($"Ownership request {(success ? "accepted" : "rejected")}"); + }); + } + + // If we have ownership, send custom data periodically + if (syncBehaviour.HasOwnership) + { + messageTimer += Time.deltaTime; + if (messageTimer >= MESSAGE_INTERVAL) + { + messageTimer = 0f; + syncBehaviour.SendTestMessage(); + } + } + } +} \ No newline at end of file diff --git a/LuaNetworkVariables/format.json b/LuaNetworkVariables/format.json new file mode 100644 index 0000000..654911a --- /dev/null +++ b/LuaNetworkVariables/format.json @@ -0,0 +1,23 @@ +{ + "_id": 234, + "name": "WhereAmIPointing", + "modversion": "1.0.1", + "gameversion": "2024r175", + "loaderversion": "0.6.1", + "modtype": "Mod", + "author": "NotAKidoS", + "description": "Simple mod that makes your controller rays always visible when the menus are open. Useful for when you're trying to aim at something in the distance. Also visualizes which ray is being used for menu interaction.", + "searchtags": [ + "controller", + "ray", + "line", + "tomato" + ], + "requirements": [ + "None" + ], + "downloadlink": "https://github.com/NotAKidoS/NAK_CVR_Mods/releases/download/r40/WhereAmIPointing.dll", + "sourcelink": "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/WhereAmIPointing/", + "changelog": "- Fixed line renderer alpha not being reset when the menu is closed.", + "embedcolor": "#f61963" +} \ No newline at end of file From 7b73452df6ae9e785875f5f2f270449cc881d6fb Mon Sep 17 00:00:00 2001 From: NotAKidoS <37721153+NotAKidoS@users.noreply.github.com> Date: Mon, 2 Dec 2024 20:06:26 -0600 Subject: [PATCH 007/142] ShareBubbles: initial commit --- ShareBubbles/Main.cs | 94 + ShareBubbles/ModSettings.cs | 51 + ShareBubbles/Patches.cs | 1731 +++++++++++++++++ ShareBubbles/Properties/AssemblyInfo.cs | 32 + ShareBubbles/README.md | 43 + ShareBubbles/Resources/sharingbubble.assets | Bin 0 -> 1293156 bytes ShareBubbles/ShareBubbles.csproj | 33 + .../API/Exceptions/ShareApiExceptions.cs | 66 + .../API/PedestalInfoBatchProcessor.cs | 112 ++ .../API/Responses/ActiveSharesResponse.cs | 22 + ...stalInfoResponseButWithPublicationState.cs | 13 + .../ShareBubbles/API/ShareApiHelper.cs | 236 +++ .../DataTypes/BubblePedestalInfo.cs | 10 + .../ShareBubbles/DataTypes/ShareBubbleData.cs | 80 + .../ShareBubbles/Enums/ShareAccess.cs | 8 + .../ShareBubbles/Enums/ShareLifetime.cs | 7 + ShareBubbles/ShareBubbles/Enums/ShareRule.cs | 7 + .../Implementation/AvatarBubbleImpl.cs | 103 + .../Implementation/IShareBubbleImpl.cs | 13 + .../Implementation/SpawnableBubbleImpl.cs | 122 ++ .../Implementation/TempShareManager.cs | 230 +++ .../Networking/ModNetwork.Constants.cs | 11 + .../Networking/ModNetwork.Enums.cs | 38 + .../Networking/ModNetwork.Helpers.cs | 27 + .../Networking/ModNetwork.Inbound.cs | 200 ++ .../Networking/ModNetwork.Logging.cs | 32 + .../Networking/ModNetwork.Main.cs | 36 + .../Networking/ModNetwork.Outbound.cs | 130 ++ ShareBubbles/ShareBubbles/ShareBubble.cs | 363 ++++ .../ShareBubbles/ShareBubbleManager.cs | 424 ++++ .../ShareBubbles/ShareBubbleRegistry.cs | 32 + .../ShareBubbles/UI/BubbleAnimController.cs | 230 +++ .../ShareBubbles/UI/BubbleInteract.cs | 51 + .../ShareBubbles/UI/FacePlayerCamDirection.cs | 21 + .../UI/FacePlayerDirectionAxis.cs | 22 + .../ShareBubbles/UI/HexagonSpinner.cs | 71 + .../ShareBubbles/UI/ReturnOnRelease.cs | 91 + .../ShareBubbles/UI/ShakeSoundController.cs | 85 + ShareBubbles/format.json | 24 + 39 files changed, 4901 insertions(+) create mode 100644 ShareBubbles/Main.cs create mode 100644 ShareBubbles/ModSettings.cs create mode 100644 ShareBubbles/Patches.cs create mode 100644 ShareBubbles/Properties/AssemblyInfo.cs create mode 100644 ShareBubbles/README.md create mode 100644 ShareBubbles/Resources/sharingbubble.assets create mode 100644 ShareBubbles/ShareBubbles.csproj create mode 100644 ShareBubbles/ShareBubbles/API/Exceptions/ShareApiExceptions.cs create mode 100644 ShareBubbles/ShareBubbles/API/PedestalInfoBatchProcessor.cs create mode 100644 ShareBubbles/ShareBubbles/API/Responses/ActiveSharesResponse.cs create mode 100644 ShareBubbles/ShareBubbles/API/Responses/PedestalInfoResponseButWithPublicationState.cs create mode 100644 ShareBubbles/ShareBubbles/API/ShareApiHelper.cs create mode 100644 ShareBubbles/ShareBubbles/DataTypes/BubblePedestalInfo.cs create mode 100644 ShareBubbles/ShareBubbles/DataTypes/ShareBubbleData.cs create mode 100644 ShareBubbles/ShareBubbles/Enums/ShareAccess.cs create mode 100644 ShareBubbles/ShareBubbles/Enums/ShareLifetime.cs create mode 100644 ShareBubbles/ShareBubbles/Enums/ShareRule.cs create mode 100644 ShareBubbles/ShareBubbles/Implementation/AvatarBubbleImpl.cs create mode 100644 ShareBubbles/ShareBubbles/Implementation/IShareBubbleImpl.cs create mode 100644 ShareBubbles/ShareBubbles/Implementation/SpawnableBubbleImpl.cs create mode 100644 ShareBubbles/ShareBubbles/Implementation/TempShareManager.cs create mode 100644 ShareBubbles/ShareBubbles/Networking/ModNetwork.Constants.cs create mode 100644 ShareBubbles/ShareBubbles/Networking/ModNetwork.Enums.cs create mode 100644 ShareBubbles/ShareBubbles/Networking/ModNetwork.Helpers.cs create mode 100644 ShareBubbles/ShareBubbles/Networking/ModNetwork.Inbound.cs create mode 100644 ShareBubbles/ShareBubbles/Networking/ModNetwork.Logging.cs create mode 100644 ShareBubbles/ShareBubbles/Networking/ModNetwork.Main.cs create mode 100644 ShareBubbles/ShareBubbles/Networking/ModNetwork.Outbound.cs create mode 100644 ShareBubbles/ShareBubbles/ShareBubble.cs create mode 100644 ShareBubbles/ShareBubbles/ShareBubbleManager.cs create mode 100644 ShareBubbles/ShareBubbles/ShareBubbleRegistry.cs create mode 100644 ShareBubbles/ShareBubbles/UI/BubbleAnimController.cs create mode 100644 ShareBubbles/ShareBubbles/UI/BubbleInteract.cs create mode 100644 ShareBubbles/ShareBubbles/UI/FacePlayerCamDirection.cs create mode 100644 ShareBubbles/ShareBubbles/UI/FacePlayerDirectionAxis.cs create mode 100644 ShareBubbles/ShareBubbles/UI/HexagonSpinner.cs create mode 100644 ShareBubbles/ShareBubbles/UI/ReturnOnRelease.cs create mode 100644 ShareBubbles/ShareBubbles/UI/ShakeSoundController.cs create mode 100644 ShareBubbles/format.json diff --git a/ShareBubbles/Main.cs b/ShareBubbles/Main.cs new file mode 100644 index 0000000..72f3c3d --- /dev/null +++ b/ShareBubbles/Main.cs @@ -0,0 +1,94 @@ +using MelonLoader; +using NAK.ShareBubbles.Networking; +using UnityEngine; + +namespace NAK.ShareBubbles; + +public class ShareBubblesMod : MelonMod +{ + internal static MelonLogger.Instance Logger; + + #region Melon Mod Overrides + + public override void OnInitializeMelon() + { + Logger = LoggerInstance; + + ShareBubbleManager.Initialize(); + TempShareManager.Initialize(); + + ModNetwork.Initialize(); + ModNetwork.Subscribe(); + + //ModSettings.Initialize(); + + ApplyPatches(typeof(Patches.PlayerSetup_Patches)); + ApplyPatches(typeof(Patches.ControllerRay_Patches)); + ApplyPatches(typeof(Patches.ViewManager_Patches)); + + LoadAssetBundle(); + } + + public override void OnApplicationQuit() + { + ModNetwork.Unsubscribe(); + } + + #endregion Melon Mod Overrides + + #region Melon Mod Utilities + + private void ApplyPatches(Type type) + { + try + { + HarmonyInstance.PatchAll(type); + } + catch (Exception e) + { + LoggerInstance.Msg($"Failed while patching {type.Name}!"); + LoggerInstance.Error(e); + } + } + + #endregion Melon Mod Utilities + + #region Asset Bundle Loading + + private const string SharingBubbleAssets = "ShareBubbles.Resources.sharingbubble.assets"; + private const string SharingBubblePrefabPath = "Assets/Mods/SharingBubble/SharingBubble.prefab"; + + internal static GameObject SharingBubblePrefab; + + private void LoadAssetBundle() + { + LoggerInstance.Msg($"Loading required asset bundle..."); + using Stream resourceStream = MelonAssembly.Assembly.GetManifestResourceStream(SharingBubbleAssets); + using MemoryStream memoryStream = new(); + if (resourceStream == null) { + LoggerInstance.Error($"Failed to load {SharingBubbleAssets}!"); + return; + } + + resourceStream.CopyTo(memoryStream); + AssetBundle assetBundle = AssetBundle.LoadFromMemory(memoryStream.ToArray()); + if (assetBundle == null) { + LoggerInstance.Error($"Failed to load {SharingBubbleAssets}! Asset bundle is null!"); + return; + } + + SharingBubblePrefab = assetBundle.LoadAsset(SharingBubblePrefabPath); + if (SharingBubblePrefab == null) { + LoggerInstance.Error($"Failed to load {SharingBubblePrefab}! Prefab is null!"); + return; + } + SharingBubblePrefab.hideFlags |= HideFlags.DontUnloadUnusedAsset; + LoggerInstance.Msg($"Loaded {SharingBubblePrefab}!"); + + // load + + LoggerInstance.Msg("Asset bundle successfully loaded!"); + } + + #endregion Asset Bundle Loading +} \ No newline at end of file diff --git a/ShareBubbles/ModSettings.cs b/ShareBubbles/ModSettings.cs new file mode 100644 index 0000000..22f708c --- /dev/null +++ b/ShareBubbles/ModSettings.cs @@ -0,0 +1,51 @@ +using MelonLoader; + +namespace NAK.ShareBubbles; + +// TODO: +// Setting for ShareBubbles scaling with player size +// Setting for receiving notification when a direct share is received +// Setting for ShareBubble being interactable outside of the UI buttons (Grab & Click) +// Store last Visibility, Lifetime, and Access Control settings for ShareBubble placement in hidden melon preferences + +public static class ModSettings +{ + #region Constants & Category + + internal const string ModName = nameof(ShareBubbles); // TODO idea: BTKUI player page button to remove player's ShareBubbles ? + + //internal const string SM_SettingsCategory = "Share Bubbles Mod"; + + private static readonly MelonPreferences_Category Category = + MelonPreferences.CreateCategory(ModName); + + #endregion Constants & Category + + #region Debug Settings + + internal static readonly MelonPreferences_Entry Debug_NetworkInbound = + Category.CreateEntry("debug_inbound", false, display_name: "Debug Inbound", description: "Log inbound Mod Network updates."); + + internal static readonly MelonPreferences_Entry Debug_NetworkOutbound = + Category.CreateEntry("debug_outbound", false, display_name: "Debug Outbound", description: "Log outbound Mod Network updates."); + + #endregion Debug Settings + + #region Initialization + + internal static void Initialize() + { + + } + + #endregion Initialization + + #region Setting Changed Callbacks + + // private static void OnPlayerUpAlignmentThresholdChanged(float oldValue, float newValue) + // { + // Entry_PlayerUpAlignmentThreshold.Value = Mathf.Clamp(newValue, 0f, 180f); + // } + + #endregion Setting Changed Callbacks +} \ No newline at end of file diff --git a/ShareBubbles/Patches.cs b/ShareBubbles/Patches.cs new file mode 100644 index 0000000..4f705e3 --- /dev/null +++ b/ShareBubbles/Patches.cs @@ -0,0 +1,1731 @@ +using ABI_RC.Core.InteractionSystem; +using ABI_RC.Core.Networking.API.Responses; +using ABI_RC.Core.Networking.API.UserWebsocket; +using ABI_RC.Core.Player; +using ABI_RC.Core.Savior; +using HarmonyLib; +using MTJobSystem; +using NAK.ShareBubbles.API; +using NAK.ShareBubbles.API.Exceptions; +using NAK.ShareBubbles.API.Responses; +using Newtonsoft.Json; + +namespace NAK.ShareBubbles.Patches; + +internal static class PlayerSetup_Patches +{ + [HarmonyPostfix] + [HarmonyPatch(typeof(PlayerSetup), nameof(PlayerSetup.SetSafeScale))] + public static void Postfix_PlayerSetup_SetSafeScale() + { + // I wish there was a callback to listen for player scale changes + ShareBubbleManager.Instance.OnPlayerScaleChanged(); + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(PlayerSetup), nameof(PlayerSetup.GetCurrentPropSelectionMode))] + private static void Postfix_PlayerSetup_GetCurrentPropSelectionMode(ref PlayerSetup.PropSelectionMode __result) + { + // Stickers mod uses invalid enum value 4, so we use 5 + // https://github.com/NotAKidoS/NAK_CVR_Mods/blob/d0c8298074c4dcfc089ccb34ed8b8bd7e0b9cedf/Stickers/Patches.cs#L17 + if (ShareBubbleManager.Instance.IsPlacingBubbleMode) __result = (PlayerSetup.PropSelectionMode)5; + } +} + +internal static class ControllerRay_Patches +{ + [HarmonyPostfix] + [HarmonyPatch(typeof(ControllerRay), nameof(ControllerRay.DeleteSpawnable))] + public static void Postfix_ControllerRay_DeleteSpawnable(ref ControllerRay __instance) + { + if (!__instance._interactDown) + return; // not interacted, no need to check + + if (PlayerSetup.Instance.GetCurrentPropSelectionMode() + != PlayerSetup.PropSelectionMode.Delete) + return; // not in delete mode, no need to check + + ShareBubble shareBubble = __instance.hitTransform.GetComponentInParent(); + if (shareBubble == null) return; + + ShareBubbleManager.Instance.DestroyBubble(shareBubble); + } + + [HarmonyPrefix] + [HarmonyPatch(typeof(ControllerRay), nameof(ControllerRay.HandlePropSpawn))] + private static void Prefix_ControllerRay_HandlePropSpawn(ref ControllerRay __instance) + { + if (!ShareBubbleManager.Instance.IsPlacingBubbleMode) + return; + + if (__instance._gripDown) ShareBubbleManager.Instance.IsPlacingBubbleMode = false; + if (__instance._hitUIInternal || !__instance._interactDown) + return; + + ShareBubbleManager.Instance.PlaceSelectedBubbleFromControllerRay(__instance.rayDirectionTransform); + } +} + +internal static class ViewManager_Patches +{ + +private const string DETAILS_TOOLBAR_PATCHES = """ + +const ContentShareMod = { + debugMode: false, + currentContentData: null, + themeColors: null, + + /* Theme Handling */ + + getThemeColors: function() { + if (this.themeColors) return this.themeColors; + + // Default fallback colors + const defaultColors = { + background: '#373021', + border: '#59885d' + }; + + // Try to get colors from favorite category element + const favoriteCategoryElement = document.querySelector('.favorite-category-selection'); + if (!favoriteCategoryElement) return defaultColors; + + const computedStyle = window.getComputedStyle(favoriteCategoryElement); + this.themeColors = { + background: computedStyle.backgroundColor || defaultColors.background, + border: computedStyle.borderColor || defaultColors.border + }; + + return this.themeColors; + }, + + applyThemeToDialog: function(dialog) { + const colors = this.getThemeColors(); + dialog.style.backgroundColor = colors.background; + dialog.style.borderColor = colors.border; + + // Update any close or page buttons to match theme + const buttons = dialog.querySelectorAll('.close-btn, .page-btn'); + buttons.forEach(button => { + button.style.borderColor = colors.border; + }); + + return colors; + }, + + /* Core Initialization */ + + init: function() { + const styles = [ + this.getSharedStyles(), + this.ShareBubble.initStyles(), + this.ShareSelect.initStyles(), + this.DirectShare.initStyles(), + this.Unshare.initStyles() + ].join('\n'); + + const styleElement = document.createElement('style'); + styleElement.type = 'text/css'; + styleElement.innerHTML = styles; + document.head.appendChild(styleElement); + + this.shareBubbleDialog = this.ShareBubble.createDialog(); + this.shareSelectDialog = this.ShareSelect.createDialog(); + this.directShareDialog = this.DirectShare.createDialog(); + this.unshareDialog = this.Unshare.createDialog(); + + this.initializeToolbars(); + this.bindEvents(); + }, + + getSharedStyles: function() { + return ` + .content-sharing-base-dialog { + position: fixed; + background-color: #373021; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 800px; + min-width: 500px; + border: 3px solid #59885d; + padding: 20px; + z-index: 100000; + opacity: 0; + transition: opacity 0.2s linear; + } + + .content-sharing-base-dialog.in { + opacity: 1; + } + + .content-sharing-base-dialog.out { + opacity: 0; + } + + .content-sharing-base-dialog.hidden { + display: none; + } + + .content-sharing-base-dialog h2, + .content-sharing-base-dialog h3 { + margin-top: 0; + margin-bottom: 0.5em; + text-align: left; + } + + .content-sharing-base-dialog .description { + margin-bottom: 1em; + text-align: left; + font-size: 0.9em; + color: #aaa; + } + + .content-sharing-base-dialog .close-btn { + position: absolute; + top: 1%; + right: 1%; + border-radius: 0.25em; + border: 3px solid #59885d; + padding: 0.5em; + width: 8em; + text-align: center; + } + + .page-btn { + border-radius: 0.25em; + border: 3px solid #59885d; + padding: 0.5em; + width: 8em; + text-align: center; + } + + .page-btn:disabled { + opacity: 0.5; + cursor: not-allowed; + } + + .inp-hidden { + display: none; + } + `; + }, + + /* Feature Modules */ + + ShareBubble: { + initStyles: function() { + return ` + .share-bubble-dialog { + max-width: 800px; + transform: translate(-50%, -60%); + } + + .share-bubble-dialog .content-btn { + position: relative; + margin-bottom: 0.5em; + } + + .share-bubble-dialog .btn-group { + display: flex; + margin-bottom: 1em; + } + + .share-bubble-dialog .option-select { + flex: 1 1 0; + min-width: 0; + background-color: inherit; + text-align: center; + padding: 0.5em; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + border: 1px solid inherit; + } + + .share-bubble-dialog .option-select + .option-select { + margin-left: -1px; + } + + .share-bubble-dialog .option-select.active, + .share-bubble-dialog .option-select:hover { + background-color: rgba(27, 80, 55, 1); + } + + .share-bubble-dialog .action-buttons { + margin-top: 1em; + display: flex; + justify-content: space-between; + } + + .share-bubble-dialog .action-btn { + flex: 1; + text-align: center; + padding: 0.5em; + border-radius: 0.25em; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + margin-right: 0.5em; + } + + .share-bubble-dialog .action-btn:last-child { + margin-right: 0; + } + `; + }, + + createDialog: function() { + const dialog = document.createElement('div'); + dialog.id = 'content-share-bubble-dialog'; + dialog.className = 'content-sharing-base-dialog share-bubble-dialog hidden'; + dialog.innerHTML = ` + Share Bubble + Close + + Visibility + Choose who can see your Sharing Bubble. + + Everyone + Friends Only + + + Lifetime + How long the Sharing Bubble lasts. You can delete it at any time. + + 2 Minutes + For Session + + + + Access Control + + + Users will keep access to this Private Content. You can manage permissions later on the Hub. + + + Users can only access this Private Content while in the same instance. + + + Users cannot access your Private Content through this share. + + + + Keep + Instance Only + None + + + + + Access Control + You cannot control access to this content because it is Public or you do not own it. + + + + + + + + Drop + Select + + `; + document.body.appendChild(dialog); + return dialog; + }, + + show: function(contentDetails) { + const dialog = ContentShareMod.shareBubbleDialog; + const colors = ContentShareMod.applyThemeToDialog(dialog); + + // Additional ShareBubble-specific theming + const optionSelects = dialog.querySelectorAll('.option-select'); + optionSelects.forEach(element => { + element.style.borderColor = colors.border; + }); + + const grantAccessSection = dialog.querySelector('.grant-access-section'); + const noAccessControlMessage = dialog.querySelector('.no-access-control-message'); + const showGrantAccess = contentDetails.IsMine && !contentDetails.IsPublic; + + if (grantAccessSection && noAccessControlMessage) { + grantAccessSection.style.display = showGrantAccess ? '' : 'none'; + noAccessControlMessage.style.display = showGrantAccess ? 'none' : ''; + } + + dialog.classList.remove('hidden', 'out'); + setTimeout(() => dialog.classList.add('in'), 50); + + ContentShareMod.currentContentData = contentDetails; + }, + + hide: function() { + const dialog = ContentShareMod.shareBubbleDialog; + dialog.classList.remove('in'); + dialog.classList.add('out'); + setTimeout(() => { + dialog.classList.add('hidden'); + dialog.classList.remove('out'); + }, 200); + }, + + changeVisibility: function(element) { + document.getElementById('share-visibility').value = element.dataset.visibilityValue; + const buttons = ContentShareMod.shareBubbleDialog.querySelectorAll('.visibility-btn'); + buttons.forEach(btn => btn.classList.remove('active')); + element.classList.add('active'); + }, + + changeDuration: function(element) { + document.getElementById('share-duration').value = element.dataset.durationValue; + const buttons = ContentShareMod.shareBubbleDialog.querySelectorAll('.duration-btn'); + buttons.forEach(btn => btn.classList.remove('active')); + element.classList.add('active'); + }, + + changeAccess: function(element) { + document.getElementById('share-access').value = element.dataset.accessValue; + const buttons = ContentShareMod.shareBubbleDialog.querySelectorAll('.access-btn'); + buttons.forEach(btn => btn.classList.remove('active')); + element.classList.add('active'); + + const descriptions = ContentShareMod.shareBubbleDialog.querySelectorAll('.access-desc'); + descriptions.forEach(desc => { + desc.style.display = desc.dataset.accessType === element.dataset.accessValue ? '' : 'none'; + }); + }, + + submit: function(action) { + const contentDetails = ContentShareMod.currentContentData; + let bubbleImpl, bubbleContent, contentImage, contentName; + + if (contentDetails.AvatarId) { + bubbleImpl = 'Avatar'; + bubbleContent = contentDetails.AvatarId; + contentImage = contentDetails.AvatarImageCoui; + contentName = contentDetails.AvatarName; + } else if (contentDetails.SpawnableId) { + bubbleImpl = 'Spawnable'; + bubbleContent = contentDetails.SpawnableId; + contentImage = contentDetails.SpawnableImageCoui; + contentName = contentDetails.SpawnableName; + } else if (contentDetails.WorldId) { + bubbleImpl = 'World'; + bubbleContent = contentDetails.WorldId; + contentImage = contentDetails.WorldImageCoui; + contentName = contentDetails.WorldName; + } else if (contentDetails.UserId) { + bubbleImpl = 'User'; + bubbleContent = contentDetails.UserId; + contentImage = contentDetails.UserImageCoui; + contentName = contentDetails.UserName; + } else { + console.error('No valid content ID found'); + return; + } + + const visibility = document.getElementById('share-visibility').value; + const duration = document.getElementById('share-duration').value; + const access = document.getElementById('share-access').value; + + const shareRule = visibility === 'FriendsOnly' ? 'FriendsOnly' : 'Everyone'; + const shareLifetime = duration === 'Session' ? 'Session' : 'TwoMinutes'; + const shareAccess = access === 'Permanent' ? 'Permanent' : + access === 'Session' ? 'Session' : 'NoAccess'; + + if (ContentShareMod.debugMode) { + console.log('Sharing content:', { + action, bubbleImpl, bubbleContent, + shareRule, shareLifetime, shareAccess + }); + } + + engine.call('NAKCallShareContent', action, bubbleImpl, bubbleContent, + shareRule, shareLifetime, shareAccess, contentImage, contentName); + + this.hide(); + } + }, + + Unshare: { + currentPage: 1, + totalPages: 1, + sharesPerPage: 5, + sharesList: null, + + initStyles: function() { + return ` + .unshare-dialog { + width: 800px; + height: 1000px; + transform: translate(-50%, -60%); + display: flex; + flex-direction: column; + } + + .unshare-dialog .shares-container { + flex: 1; + overflow-y: auto; + margin: 20px 0; + min-height: 0; + } + + .unshare-dialog #shares-loading, + .unshare-dialog #shares-error, + .unshare-dialog #shares-empty { + text-align: center; + padding: 2em; + font-size: 1.1em; + } + + .unshare-dialog #shares-error { + color: #ff6b6b; + } + + .unshare-dialog .share-item { + display: flex; + align-items: center; + padding: 15px; + border: 1px solid rgba(255, 255, 255, 0.1); + margin-bottom: 10px; + background: rgba(0, 0, 0, 0.2); + border-radius: 4px; + min-height: 120px; + } + + .unshare-dialog .share-item img { + width: 96px; + height: 96px; + border-radius: 4px; + margin-right: 15px; + cursor: pointer; + } + + .unshare-dialog .share-item .user-name { + flex: 1; + font-size: 1.3em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-right: 15px; + cursor: pointer; + } + + .unshare-dialog .action-btn { + width: 180px; + height: 60px; + font-size: 1.2em; + color: white; + border-radius: 4px; + display: inline-flex; + align-items: center; + justify-content: center; + margin-left: 10px; + padding: 0 20px; + text-align: center; + } + + .unshare-dialog .revoke-btn { + background-color: #ff6b6b; + } + + .unshare-dialog .undo-btn { + background-color: #4a9eff; + } + + .unshare-dialog .action-btn:disabled { + opacity: 0.5; + cursor: not-allowed; + background-color: #666; + } + + .unshare-dialog .pagination { + border-top: 1px solid rgba(255, 255, 255, 0.1); + padding-top: 20px; + } + + .unshare-dialog .page-info { + font-size: 1.1em; + opacity: 0.8; + margin-bottom: 15px; + text-align: center; + } + + .unshare-dialog .page-buttons { + display: flex; + justify-content: center; + gap: 20px; + } + + .unshare-dialog .share-item.revoked { + opacity: 0.7; + } + `; + }, + + createDialog: function() { + const dialog = document.createElement('div'); + dialog.id = 'content-unshare-dialog'; + dialog.className = 'content-sharing-base-dialog unshare-dialog hidden'; + dialog.innerHTML = ` + Manage Shares + Close + + + Loading shares... + Failed to load shares. Please try again. + No active shares found. + + + + + 1/1 + + Previous + Next + + + `; + document.body.appendChild(dialog); + return dialog; + }, + + show: function(contentDetails) { + const dialog = ContentShareMod.unshareDialog; + ContentShareMod.applyThemeToDialog(dialog); + + dialog.classList.remove('hidden', 'out'); + setTimeout(() => dialog.classList.add('in'), 50); + + ContentShareMod.currentContentData = contentDetails; + this.currentPage = 1; + this.totalPages = 1; + this.sharesList = null; + this.requestShares(); + }, + + hide: function() { + const dialog = ContentShareMod.unshareDialog; + dialog.classList.remove('in'); + dialog.classList.add('out'); + setTimeout(() => { + dialog.classList.add('hidden'); + dialog.classList.remove('out'); + }, 200); + }, + + requestShares: function() { + const dialog = ContentShareMod.unshareDialog; + const sharesContainer = dialog.querySelector('.shares-container'); + + sharesContainer.querySelector('#shares-loading').style.display = ''; + sharesContainer.querySelector('#shares-error').style.display = 'none'; + sharesContainer.querySelector('#shares-empty').style.display = 'none'; + sharesContainer.querySelector('#shares-list').style.display = 'none'; + + const contentDetails = ContentShareMod.currentContentData; + const contentType = contentDetails.AvatarId ? 'Avatar' : 'Spawnable'; + const contentId = contentDetails.AvatarId || contentDetails.SpawnableId; + + engine.call('NAKGetContentShares', contentType, contentId); + }, + + handleSharesResponse: function(success, shares) { + const dialog = ContentShareMod.unshareDialog; + const sharesContainer = dialog.querySelector('.shares-container'); + const loadingElement = sharesContainer.querySelector('#shares-loading'); + const errorElement = sharesContainer.querySelector('#shares-error'); + const emptyElement = sharesContainer.querySelector('#shares-empty'); + const sharesListElement = sharesContainer.querySelector('#shares-list'); + + loadingElement.style.display = 'none'; + + if (!success) { + errorElement.style.display = ''; + return; + } + + try { + const response = JSON.parse(shares); + this.sharesList = response.Data.value; + + if (!this.sharesList || this.sharesList.length === 0) { + emptyElement.style.display = ''; + const pagination = dialog.querySelector('.pagination'); + const [prevButton, nextButton] = pagination.querySelectorAll('.page-btn'); + prevButton.disabled = true; + nextButton.disabled = true; + pagination.querySelector('.page-info').textContent = '1/1'; + return; + } + + this.totalPages = Math.ceil(this.sharesList.length / this.sharesPerPage); + this.updatePageContent(); + } catch (error) { + console.error('Error parsing shares:', error); + errorElement.style.display = ''; + } + }, + + updatePageContent: function() { + const dialog = ContentShareMod.unshareDialog; + const sharesListElement = dialog.querySelector('#shares-list'); + + const startIndex = (this.currentPage - 1) * this.sharesPerPage; + const endIndex = startIndex + this.sharesPerPage; + const currentShares = this.sharesList.slice(startIndex, endIndex); + + sharesListElement.innerHTML = currentShares.map(share => ` + + + ${share.name} + + Revoke + + + `).join(''); + + sharesListElement.style.display = ''; + + const pagination = dialog.querySelector('.pagination'); + pagination.querySelector('.page-info').textContent = `${this.currentPage}/${this.totalPages}`; + const [prevButton, nextButton] = pagination.querySelectorAll('.page-btn'); + prevButton.disabled = this.currentPage === 1; + nextButton.disabled = this.currentPage === this.totalPages; + }, + + previousPage: function() { + if (this.currentPage > 1) { + this.currentPage--; + this.updatePageContent(); + } + }, + + nextPage: function() { + if (this.currentPage < this.totalPages) { + this.currentPage++; + this.updatePageContent(); + } + }, + + viewUserProfile: function(userId) { + this.hide(); + getUserDetails(userId); + }, + + revokeShare: function(userId, buttonElement) { + const contentDetails = ContentShareMod.currentContentData; + const contentType = contentDetails.AvatarId ? 'Avatar' : 'Spawnable'; + const contentId = contentDetails.AvatarId || contentDetails.SpawnableId; + + buttonElement.disabled = true; + buttonElement.textContent = 'Revoking...'; + + engine.call('NAKRevokeContentShare', contentType, contentId, userId); + }, + + handleRevokeResponse: function(success, userId, error) { + const dialog = ContentShareMod.unshareDialog; + const shareItem = dialog.querySelector(`[data-user-id="${userId}"]`); + if (!shareItem) return; + + const actionButton = shareItem.querySelector('button'); + + if (success) { + shareItem.classList.add('revoked'); + actionButton.className = 'action-btn undo-btn button'; + actionButton.textContent = 'Undo'; + actionButton.onclick = () => { + actionButton.disabled = true; + actionButton.textContent = 'Restoring...'; + + const contentDetails = ContentShareMod.currentContentData; + const contentType = contentDetails.AvatarId ? 'Avatar' : 'Spawnable'; + const contentId = contentDetails.AvatarId || contentDetails.SpawnableId; + + engine.call('NAKCallShareContentDirect', contentType, contentId, userId); + }; + uiPushShow("Share revoked successfully", 3); + } else { + actionButton.textContent = 'Failed'; + actionButton.classList.add('failed'); + uiPushShow(error || "Failed to revoke share", 3); + + // Reset button after a moment + setTimeout(() => { + actionButton.disabled = false; + actionButton.textContent = 'Revoke'; + actionButton.classList.remove('failed'); + }, 1000); + } + }, + + handleShareResponse: function(success, userId, error) { + const dialog = ContentShareMod.unshareDialog; + const shareItem = dialog.querySelector(`[data-user-id="${userId}"]`); + if (!shareItem) return; + + const actionButton = shareItem.querySelector('button'); + + if (success) { + this.requestShares(); + uiPushShow("Share restored successfully", 3); + } else { + actionButton.textContent = 'Failed'; + actionButton.classList.add('failed'); + uiPushShow(error || "Failed to restore share", 3); + + // Reset button after a moment + setTimeout(() => { + actionButton.disabled = false; + actionButton.textContent = 'Undo'; + actionButton.classList.remove('failed'); + }, 1000); + } + } + }, + + DirectShare: { + currentPage: 1, + totalPages: 1, + usersPerPage: 5, + usersList: null, + isInstanceUsers: true, + + initStyles: function() { + return ` + .direct-share-dialog { + width: 800px; + height: 1000px; + transform: translate(-50%, -60%); + display: flex; + flex-direction: column; + } + + .direct-share-dialog .search-container { + margin: 20px 0; + padding: 10px; + background: rgba(0, 0, 0, 0.2); + border-radius: 4px; + } + + .direct-share-dialog .search-input { + width: 100%; + padding: 10px; + border: none; + background: transparent; + color: inherit; + font-size: 1.1em; + } + + .direct-share-dialog .source-indicator { + padding: 10px; + text-align: center; + opacity: 0.8; + background: rgba(0, 0, 0, 0.1); + border-radius: 4px; + margin-bottom: 20px; + } + + .direct-share-dialog .users-container { + flex: 1; + overflow-y: auto; + margin-bottom: 20px; + min-height: 0; + } + + .direct-share-dialog .user-item { + display: flex; + align-items: center; + padding: 15px; + border: 1px solid rgba(255, 255, 255, 0.1); + margin-bottom: 10px; + background: rgba(0, 0, 0, 0.2); + border-radius: 4px; + min-height: 96px; + } + + .direct-share-dialog .user-item img { + width: 96px; + height: 96px; + margin-right: 15px; + cursor: pointer; + border-radius: 4px; + } + + .direct-share-dialog .user-item .user-name { + flex: 1; + font-size: 1.3em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-right: 15px; + cursor: pointer; + } + + .direct-share-dialog .user-item .action-btn { + width: 140px; + height: 50px; + font-size: 1.2em; + color: white; + background-color: rgba(27, 80, 55, 1); + border-radius: 4px; + display: inline-flex; + align-items: center; + justify-content: center; + } + + .direct-share-dialog .user-item .action-btn:disabled { + opacity: 0.5; + cursor: not-allowed; + background-color: #4a9eff; + } + + .direct-share-dialog .pagination { + border-top: 1px solid rgba(255, 255, 255, 0.1); + padding-top: 20px; + } + + .direct-share-dialog .page-info { + font-size: 1.1em; + opacity: 0.8; + margin-bottom: 15px; + text-align: center; + } + + .direct-share-dialog .page-buttons { + display: flex; + justify-content: center; + gap: 20px; + } + + .direct-share-dialog .user-item .action-btn { + width: 180px; + height: 60px; + font-size: 1.2em; + color: white; + border-radius: 4px; + display: inline-flex; + align-items: center; + justify-content: center; + margin-left: 10px; + padding: 0 20px; + text-align: center; + background-color: rgba(27, 80, 55, 1); + } + + .direct-share-dialog .user-item .action-btn:disabled { + opacity: 0.5; + cursor: not-allowed; + } + + .direct-share-dialog .user-item .action-btn.shared { + background-color: #4a9eff; + } + + .direct-share-dialog .user-item .action-btn.failed { + background-color: #ff6b6b; + } + `; + }, + + createDialog: function() { + const dialog = document.createElement('div'); + dialog.id = 'content-direct-share-dialog'; + dialog.className = 'content-sharing-base-dialog direct-share-dialog hidden'; + dialog.innerHTML = ` + Direct Share + Close + + + Loading users... + Failed to load users. Please try again. + No users found. + + + + + Page 1/1 + + Previous + Next + + + `; + document.body.appendChild(dialog); + return dialog; + }, + + show: function(contentDetails) { + const dialog = ContentShareMod.directShareDialog; + ContentShareMod.applyThemeToDialog(dialog); + + dialog.classList.remove('hidden', 'out'); + setTimeout(() => dialog.classList.add('in'), 50); + + ContentShareMod.currentContentData = contentDetails; + this.currentPage = 1; + this.totalPages = 1; + this.usersList = null; + this.requestUsers(true); + }, + + hide: function() { + const dialog = ContentShareMod.directShareDialog; + dialog.classList.remove('in'); + dialog.classList.add('out'); + setTimeout(() => { + dialog.classList.add('hidden'); + dialog.classList.remove('out'); + }, 200); + }, + + handleUsersResponse: function(success, users, isInstanceUsers) { + const dialog = ContentShareMod.directShareDialog; + const usersContainer = dialog.querySelector('.users-container'); + // const sourceIndicator = dialog.querySelector('.source-indicator'); + const loadingElement = usersContainer.querySelector('#users-loading'); + const errorElement = usersContainer.querySelector('#users-error'); + const emptyElement = usersContainer.querySelector('#users-empty'); + const usersListElement = usersContainer.querySelector('#users-list'); + + loadingElement.style.display = 'none'; + // sourceIndicator.textContent = isInstanceUsers ? + // 'Showing users in current instance' : + // 'Showing search results'; + + // TODO: Add source indicator to html: + // + // Showing users in current instance + // + + if (!success) { + errorElement.style.display = ''; + return; + } + + try { + const response = JSON.parse(users); + this.usersList = response.entries; + this.isInstanceUsers = isInstanceUsers; + + if (!this.usersList || this.usersList.length === 0) { + emptyElement.style.display = ''; + this.updatePagination(); + return; + } + + this.totalPages = Math.ceil(this.usersList.length / this.usersPerPage); + this.updatePageContent(); + } catch (error) { + console.error('Error parsing users:', error); + errorElement.style.display = ''; + } + }, + + handleSearch: function(event) { + if (event.key === 'Enter') { + const searchValue = event.target.value.trim(); + // Pass true for instance users when empty search, false for search results + this.requestUsers(searchValue === '', searchValue); + } + }, + + requestUsers: function(isInstanceUsers, searchQuery = '') { + const dialog = ContentShareMod.directShareDialog; + const usersContainer = dialog.querySelector('.users-container'); + + usersContainer.querySelector('#users-loading').style.display = ''; + usersContainer.querySelector('#users-error').style.display = 'none'; + usersContainer.querySelector('#users-empty').style.display = 'none'; + usersContainer.querySelector('#users-list').style.display = 'none'; + + engine.call('NAKGetUsersForSharing', searchQuery); + }, + + updatePageContent: function() { + const dialog = ContentShareMod.directShareDialog; + const usersListElement = dialog.querySelector('#users-list'); + + const startIndex = (this.currentPage - 1) * this.usersPerPage; + const endIndex = startIndex + this.usersPerPage; + const currentUsers = this.usersList.slice(startIndex, endIndex); + + usersListElement.innerHTML = currentUsers.map(user => ` + + + ${user.name} + + Share + + + `).join(''); + + usersListElement.style.display = ''; + this.updatePagination(); + }, + + updatePagination: function() { + const dialog = ContentShareMod.directShareDialog; + const pagination = dialog.querySelector('.pagination'); + const [prevButton, nextButton] = pagination.querySelectorAll('.page-btn'); + + pagination.querySelector('.page-info').textContent = `Page ${this.currentPage}/${this.totalPages}`; + prevButton.disabled = this.currentPage === 1; + nextButton.disabled = this.currentPage === this.totalPages; + }, + + previousPage: function() { + if (this.currentPage > 1) { + this.currentPage--; + this.updatePageContent(); + } + }, + + nextPage: function() { + if (this.currentPage < this.totalPages) { + this.currentPage++; + this.updatePageContent(); + } + }, + + viewUserProfile: function(userId) { + this.hide(); + getUserDetails(userId); + }, + + shareWithUser: function(userId, buttonElement) { + const contentDetails = ContentShareMod.currentContentData; + const contentType = contentDetails.AvatarId ? 'Avatar' : 'Spawnable'; + const contentId = contentDetails.AvatarId || contentDetails.SpawnableId; + const contentName = contentDetails.AvatarName || contentDetails.SpawnableName; + const contentImage = contentDetails.AvatarImageURL || contentDetails.SpawnableImageURL; + + buttonElement.disabled = true; + buttonElement.textContent = 'Sharing...'; + + engine.call('NAKCallShareContentDirect', contentType, contentId, userId, contentName, contentImage); + }, + + handleShareResponse: function(success, userId, error) { + const dialog = ContentShareMod.directShareDialog; + const userItem = dialog.querySelector(`[data-user-id="${userId}"]`); + if (!userItem) return; + + const actionButton = userItem.querySelector('button'); + + if (success) { + actionButton.textContent = 'Shared'; + actionButton.disabled = true; + actionButton.classList.add('shared'); + uiPushShow("Content shared successfully", 3, "shareresponse"); + } else { + actionButton.disabled = false; + actionButton.textContent = 'Failed'; + actionButton.classList.add('failed'); + uiPushShow(error || "Failed to share content", 3, "shareresponse"); + + // Reset button after a moment + setTimeout(() => { + actionButton.disabled = false; + actionButton.textContent = 'Share'; + actionButton.classList.remove('failed', 'shared'); + }, 1000); + } + } + }, + + ShareSelect: { + initStyles: function() { + return ` + .share-select-dialog { + width: 650px; + height: 480px; + transform: translate(-50%, -80%); + } + + .share-select-dialog .share-options { + display: flex; + flex-direction: column; + gap: 15px; + margin-top: 20px; + } + + .share-select-dialog .share-option { + padding: 20px; + text-align: left; + cursor: pointer; + background: rgba(0, 0, 0, 0.2); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 4px; + transition: background-color 0.2s ease; + } + + .share-select-dialog .share-option:hover { + background-color: rgba(27, 80, 55, 1); + border-color: rgba(255, 255, 255, 0.2); + } + + .share-select-dialog h3 { + margin: 0 0 8px 0; + font-size: 1.2em; + } + + .share-select-dialog p { + margin: 0; + opacity: 0.8; + font-size: 0.95em; + line-height: 1.4; + } + `; + }, + + createDialog: function() { + const dialog = document.createElement('div'); + dialog.id = 'content-share-select-dialog'; + dialog.className = 'content-sharing-base-dialog share-select-dialog hidden'; + dialog.innerHTML = ` + Share Content + Close + + + + Share Bubble + Drop or place a bubble in the world that others can interact with + + + + Direct Share + Share directly with specific users + + + `; + document.body.appendChild(dialog); + return dialog; + }, + + show: function(contentDetails) { + const dialog = ContentShareMod.shareSelectDialog; + ContentShareMod.applyThemeToDialog(dialog); + + dialog.classList.remove('hidden', 'out'); + setTimeout(() => dialog.classList.add('in'), 50); + + ContentShareMod.currentContentData = contentDetails; + }, + + hide: function() { + const dialog = ContentShareMod.shareSelectDialog; + dialog.classList.remove('in'); + dialog.classList.add('out'); + setTimeout(() => { + dialog.classList.add('hidden'); + dialog.classList.remove('out'); + }, 200); + }, + + openShareBubble: function() { + this.hide(); + ContentShareMod.ShareBubble.show(ContentShareMod.currentContentData); + }, + + openDirectShare: function() { + this.hide(); + ContentShareMod.DirectShare.show(ContentShareMod.currentContentData); + } + }, + + // Toolbar initialization and event bindings + initializeToolbars: function() { + const findEmptyButtons = (toolbar) => { + return Array.from(toolbar.querySelectorAll('.toolbar-btn')).filter( + btn => btn.textContent.trim() === "" + ); + }; + + const setupToolbar = (selector) => { + const toolbar = document.querySelector(selector); + if (!toolbar) return; + + const emptyButtons = findEmptyButtons(toolbar); + if (emptyButtons.length >= 2) { + emptyButtons[0].classList.add('content-share-btn'); + emptyButtons[0].textContent = 'Share'; + + emptyButtons[1].classList.add('content-unshare-btn'); + emptyButtons[1].textContent = 'Unshare'; + } + }; + + setupToolbar('#avatar-detail .avatar-toolbar'); + setupToolbar('#prop-detail .avatar-toolbar'); + }, + + bindEvents: function() { + // Avatar events + engine.on("LoadAvatarDetails", (avatarDetails) => { + const shareBtn = document.querySelector('#avatar-detail .content-share-btn'); + const unshareBtn = document.querySelector('#avatar-detail .content-unshare-btn'); + const canShareDirectly = avatarDetails.IsMine; + const canUnshare = avatarDetails.IsMine || avatarDetails.IsSharedWithMe; + + if (shareBtn) { + shareBtn.classList.remove('disabled'); + + if (canShareDirectly) { + shareBtn.onclick = () => ContentShareMod.ShareSelect.show(avatarDetails); + } else { + shareBtn.onclick = () => ContentShareMod.ShareBubble.show(avatarDetails); + } + } + + if (unshareBtn) { + if (canUnshare) { + unshareBtn.classList.remove('disabled'); + unshareBtn.onclick = () => { + if (avatarDetails.IsMine) { + ContentShareMod.Unshare.show(avatarDetails); + } else { + uiConfirmShow("Unshare Avatar", + "Are you sure you want to unshare this avatar?", + "unshare_avatar_confirmation", + avatarDetails.AvatarId); + } + }; + } else { + unshareBtn.classList.add('disabled'); + unshareBtn.onclick = null; + } + } + + ContentShareMod.currentContentData = avatarDetails; + }); + + // Prop events + engine.on("LoadPropDetails", (propDetails) => { + const shareBtn = document.querySelector('#prop-detail .content-share-btn'); + const unshareBtn = document.querySelector('#prop-detail .content-unshare-btn'); + const canShareDirectly = propDetails.IsMine; + const canUnshare = propDetails.IsMine || propDetails.IsSharedWithMe; + + if (shareBtn) { + shareBtn.classList.remove('disabled'); + + if (canShareDirectly) { + shareBtn.onclick = () => ContentShareMod.ShareSelect.show(propDetails); + } else { + shareBtn.onclick = () => ContentShareMod.ShareBubble.show(propDetails); + } + } + + if (unshareBtn) { + if (canUnshare) { + unshareBtn.classList.remove('disabled'); + unshareBtn.onclick = () => { + if (propDetails.IsMine) { + ContentShareMod.Unshare.show(propDetails); + } else { + uiConfirmShow("Unshare Prop", + "Are you sure you want to unshare this prop?", + "unshare_prop_confirmation", + propDetails.SpawnableId); + } + }; + } else { + unshareBtn.classList.add('disabled'); + unshareBtn.onclick = null; + } + } + + ContentShareMod.currentContentData = propDetails; + }); + + // Share response handlers + engine.on("OnHandleSharesResponse", (success, shares) => { + if (ContentShareMod.debugMode) { + console.log('Shares response:', success, shares); + } + ContentShareMod.Unshare.handleSharesResponse(success, shares); + }); + + engine.on("OnHandleRevokeResponse", (success, userId, error) => { + ContentShareMod.Unshare.handleRevokeResponse(success, userId, error); + }); + + engine.on("OnHandleShareResponse", function(success, userId, error) { + // Pass event to Unshare and DirectShare modules depending on which dialog is open + if (ContentShareMod.unshareDialog && !ContentShareMod.unshareDialog.classList.contains('hidden')) { + ContentShareMod.Unshare.handleShareResponse(success, userId, error); + } else if (ContentShareMod.directShareDialog && !ContentShareMod.directShareDialog.classList.contains('hidden')) { + ContentShareMod.DirectShare.handleShareResponse(success, userId, error); + } + }); + + // Share release handlers + engine.on("OnReleasedAvatarShare", (contentId) => { + if (!ContentShareMod.currentContentData || + ContentShareMod.currentContentData.AvatarId !== contentId) return; + + const unshareBtn = document.querySelector('#avatar-detail .content-unshare-btn'); + ContentShareMod.currentContentData.IsSharedWithMe = false; + + if (unshareBtn) { + unshareBtn.classList.add('disabled'); + unshareBtn.onclick = null; + } + + const contentIsAccessible = ContentShareMod.currentContentData.IsMine || + ContentShareMod.currentContentData.IsPublic; + + if (!contentIsAccessible) { + const detail = document.querySelector('#avatar-detail'); + if (detail) { + ['drop-btn', 'select-btn', 'fav-btn'].forEach(className => { + const button = detail.querySelector('.' + className); + if (button) { + button.classList.add('disabled'); + button.removeAttribute('onclick'); + } + }); + } + } + }); + + engine.on("OnReleasedPropShare", (contentId) => { + if (!ContentShareMod.currentContentData || + ContentShareMod.currentContentData.SpawnableId !== contentId) return; + + const unshareBtn = document.querySelector('#prop-detail .content-unshare-btn'); + ContentShareMod.currentContentData.IsSharedWithMe = false; + + if (unshareBtn) { + unshareBtn.classList.add('disabled'); + unshareBtn.onclick = null; + } + + const contentIsAccessible = ContentShareMod.currentContentData.IsMine || + ContentShareMod.currentContentData.IsPublic; + + if (!contentIsAccessible) { + const detail = document.querySelector('#prop-detail'); + if (detail) { + ['drop-btn', 'select-btn', 'fav-btn'].forEach(className => { + const button = detail.querySelector('.' + className); + if (button) { + button.classList.add('disabled'); + button.removeAttribute('onclick'); + } + }); + } + } + }); + + engine.on("OnHandleUsersResponse", (success, users, isInstanceUsers) => { + if (ContentShareMod.debugMode) { + console.log('Users response:', success, isInstanceUsers, users); + } + ContentShareMod.DirectShare.handleUsersResponse(success, users, isInstanceUsers); + }); + } +}; + +ContentShareMod.init(); + +"""; + + private const string UiConfirmId_ReleaseAvatarShareWarning = "unshare_avatar_confirmation"; + private const string UiConfirmId_ReleasePropShareWarning = "unshare_prop_confirmation"; + + [HarmonyPostfix] + [HarmonyPatch(typeof(ViewManager), nameof(ViewManager.Start))] + public static void Postfix_ViewManager_Start(ViewManager __instance) + { + // Inject the details toolbar patches when the game menu view is loaded + __instance.gameMenuView.Listener.FinishLoad += _ => { + __instance.gameMenuView.View._view.ExecuteScript(DETAILS_TOOLBAR_PATCHES); + __instance.gameMenuView.View.BindCall("NAKCallShareContent", OnShareContent); + __instance.gameMenuView.View.BindCall("NAKGetContentShares", OnGetContentShares); + __instance.gameMenuView.View.BindCall("NAKRevokeContentShare", OnRevokeContentShare); + __instance.gameMenuView.View.BindCall("NAKCallShareContentDirect", OnShareContentDirect); + __instance.gameMenuView.View.BindCall("NAKGetUsersForSharing", OnGetUsersForSharing); + }; + + // Add the event listener for the unshare confirmation dialog + __instance.OnUiConfirm.AddListener(OnReleaseContentShareConfirmation); + + return; + + void OnShareContent( + string action, + string bubbleImpl, + string bubbleContent, + string shareRule, + string shareLifetime, + string shareAccess, + string contentImage, + string contentName) + { + // Action: drop, select + // BubbleImpl: Avatar, Prop, World, User + // BubbleContent: AvatarId, PropId, WorldId, UserId + // ShareRule: Public, FriendsOnly + // ShareLifetime: TwoMinutes, Session + // ShareAccess: PermanentAccess, SessionAccess, NoAccess + + ShareRule rule = shareRule switch + { + "Everyone" => ShareRule.Everyone, + "FriendsOnly" => ShareRule.FriendsOnly, + _ => ShareRule.Everyone + }; + + ShareLifetime lifetime = shareLifetime switch + { + "Session" => ShareLifetime.Session, + "TwoMinutes" => ShareLifetime.TwoMinutes, + _ => ShareLifetime.TwoMinutes + }; + + ShareAccess access = shareAccess switch + { + "Permanent" => ShareAccess.Permanent, + "Session" => ShareAccess.Session, + "None" => ShareAccess.None, + _ => ShareAccess.None + }; + + uint implTypeHash = ShareBubbleManager.GetMaskedHash(bubbleImpl); + ShareBubbleData bubbleData = new() + { + BubbleId = ShareBubbleManager.GenerateBubbleId(bubbleContent, implTypeHash), + ImplTypeHash = implTypeHash, + ContentId = bubbleContent, + Rule = rule, + Lifetime = lifetime, + Access = access, + CreatedAt = DateTime.UtcNow + }; + + switch (action) + { + case "drop": + ShareBubbleManager.Instance.DropBubbleInFront(bubbleData); + break; + case "select": + ShareBubbleManager.Instance.SelectBubbleForPlace(contentImage, contentName, bubbleData); + break; + } + + // Close menu + ViewManager.Instance.UiStateToggle(false); + } + + void OnReleaseContentShareConfirmation(string id, string value, string contentId) + { + // Check if the confirmation event is for unsharing content + if (id != UiConfirmId_ReleaseAvatarShareWarning + && id != UiConfirmId_ReleasePropShareWarning) + return; + + //ShareBubblesMod.Logger.Msg($"Unshare confirmation received: {id}, {value}"); + + // Check if the user confirmed the unshare action + if (value != "true") + { + //ShareBubblesMod.Logger.Msg("Unshare action cancelled by user"); + return; + } + + //ShareBubblesMod.Logger.Msg("Releasing share..."); + + // Determine the content type based on the confirmation ID + ShareApiHelper.ShareContentType contentType = id == UiConfirmId_ReleaseAvatarShareWarning + ? ShareApiHelper.ShareContentType.Avatar + : ShareApiHelper.ShareContentType.Spawnable; + + Task.Run(async () => { + try + { + await ShareApiHelper.ReleaseShareAsync(contentType, contentId); + MTJobManager.RunOnMainThread("release_share_response", () => + { + // Cannot display a success message as opening details page pushes itself to top + // after talking to api, so success message would need to be timed to show after + // if (contentType == ApiShareHelper.ShareContentType.Avatar) + // ViewManager.Instance.RequestAvatarDetailsPage(contentId); + // else + // ViewManager.Instance.GetPropDetails(contentId); + + ViewManager.Instance.gameMenuView.View._view.TriggerEvent( + contentType == ShareApiHelper.ShareContentType.Avatar + ? "OnReleasedAvatarShare" : "OnReleasedPropShare", + contentId); + + ViewManager.Instance.TriggerPushNotification("Content unshared successfully", 3f); + }); + } + catch (ShareApiException ex) + { + ShareBubblesMod.Logger.Error($"Share API error: {ex.Message}"); + MTJobManager.RunOnMainThread("release_share_error", () => { + ViewManager.Instance.TriggerAlert("Release Share Error", ex.UserFriendlyMessage, -1, true); + }); + } + catch (Exception ex) + { + ShareBubblesMod.Logger.Error($"Unexpected error releasing share: {ex.Message}"); + MTJobManager.RunOnMainThread("release_share_error", () => { + ViewManager.Instance.TriggerAlert("Release Share Error", "An unexpected error occurred", -1, true); + }); + } + }); + } + + async void OnGetContentShares(string contentType, string contentId) + { + try + { + var response = await ShareApiHelper.GetSharesAsync>( + contentType == "Avatar" ? ShareApiHelper.ShareContentType.Avatar : ShareApiHelper.ShareContentType.Spawnable, + contentId + ); + + // TODO: somethign better than this cause this is ass and i need to replace the image urls with ImageCache coui ones + // FUICJK< + string json = JsonConvert.SerializeObject(response.Data); + + // log the json to console + //ShareBubblesMod.Logger.Msg($"Shares response: {json}"); + + __instance.gameMenuView.View.TriggerEvent("OnHandleSharesResponse", true, json); + } + catch (Exception ex) + { + ShareBubblesMod.Logger.Error($"Failed to get content shares: {ex.Message}"); + __instance.gameMenuView.View.TriggerEvent("OnHandleSharesResponse", false); + } + } + + async void OnRevokeContentShare(string contentType, string contentId, string userId) + { + try + { + await ShareApiHelper.ReleaseShareAsync( + contentType == "Avatar" ? ShareApiHelper.ShareContentType.Avatar : ShareApiHelper.ShareContentType.Spawnable, + contentId, + userId + ); + + __instance.gameMenuView.View.TriggerEvent("OnHandleRevokeResponse", true, userId); + } + catch (ShareApiException ex) + { + ShareBubblesMod.Logger.Error($"Share API error revoking share: {ex.Message}"); + __instance.gameMenuView.View.TriggerEvent("OnHandleRevokeResponse", false, userId, ex.UserFriendlyMessage); + } + catch (Exception ex) + { + ShareBubblesMod.Logger.Error($"Unexpected error revoking share: {ex.Message}"); + __instance.gameMenuView.View.TriggerEvent("OnHandleRevokeResponse", false, userId, "An unexpected error occurred"); + } + } + + async void OnShareContentDirect(string contentType, string contentId, string userId, string contentName = "", string contentImage = "") + { + try + { + ShareApiHelper.ShareContentType shareContentType = contentType == "Avatar" + ? ShareApiHelper.ShareContentType.Avatar + : ShareApiHelper.ShareContentType.Spawnable; + + await ShareApiHelper.ShareContentAsync( + shareContentType, + contentId, + userId + ); + + // Alert the user that the share occurred + //ModNetwork.SendDirectShareNotification(userId, shareContentType, contentId); + + __instance.gameMenuView.View._view.TriggerEvent("OnHandleShareResponse", true, userId); + } + catch (ShareApiException ex) + { + ShareBubblesMod.Logger.Error($"Share API error: {ex.Message}"); + __instance.gameMenuView.View._view.TriggerEvent("OnHandleShareResponse", false, userId, ex.UserFriendlyMessage); + } + catch (Exception ex) + { + ShareBubblesMod.Logger.Error($"Unexpected error sharing content: {ex.Message}"); + __instance.gameMenuView.View._view.TriggerEvent("OnHandleShareResponse", false, userId, "An unexpected error occurred"); + } + } + void OnGetUsersForSharing(string searchTerm = "") + { + try + { + if (!string.IsNullOrEmpty(searchTerm)) + { + // TODO: Search users implementation will go here + // For now just return an empty list + var response = new { entries = new List() }; + string json = JsonConvert.SerializeObject(response); + __instance.gameMenuView.View.TriggerEvent("OnHandleUsersResponse", true, json, false); + } + else + { + // Get instance users + CVRPlayerManager playerManager = CVRPlayerManager.Instance; + if (playerManager == null) + { + __instance.gameMenuView.View.TriggerEvent("OnHandleUsersResponse", false, false, true); + return; + } + + var response = new + { + entries = playerManager.NetworkPlayers + .Where(p => p != null && !string.IsNullOrEmpty(p.Uuid) + && !MetaPort.Instance.blockedUserIds.Contains(p.Uuid)) // You SHOULDNT HAVE TO DO THIS, but GS dumb + .Select(p => new + { + id = p.Uuid, + name = p.Username, + image = p.ApiProfileImageUrl + }) + .ToList() + }; + + string json = JsonConvert.SerializeObject(response); + __instance.gameMenuView.View.TriggerEvent("OnHandleUsersResponse", true, json, true); + } + } + catch (Exception ex) + { + ShareBubblesMod.Logger.Error($"Failed to get users: {ex.Message}"); + __instance.gameMenuView.View.TriggerEvent("OnHandleUsersResponse", false, false, string.IsNullOrEmpty(searchTerm)); + } + } + } +} \ No newline at end of file diff --git a/ShareBubbles/Properties/AssemblyInfo.cs b/ShareBubbles/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..06006a8 --- /dev/null +++ b/ShareBubbles/Properties/AssemblyInfo.cs @@ -0,0 +1,32 @@ +using MelonLoader; +using System.Reflection; +using NAK.ShareBubbles.Properties; + +[assembly: AssemblyVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyTitle(nameof(NAK.ShareBubbles))] +[assembly: AssemblyCompany(AssemblyInfoParams.Author)] +[assembly: AssemblyProduct(nameof(NAK.ShareBubbles))] + +[assembly: MelonInfo( + typeof(NAK.ShareBubbles.ShareBubblesMod), + nameof(NAK.ShareBubbles), + AssemblyInfoParams.Version, + AssemblyInfoParams.Author, + downloadLink: "https://github.com/NotAKidoS/NAK_CVR_Mods/tree/main/ShareBubbles" +)] + +[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")] +[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] +[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)] +[assembly: MelonColor(255, 246, 25, 99)] // red-pink +[assembly: MelonAuthorColor(255, 158, 21, 32)] // red +[assembly: HarmonyDontPatchAll] + +namespace NAK.ShareBubbles.Properties; +internal static class AssemblyInfoParams +{ + public const string Version = "1.0.2"; + public const string Author = "NotAKidoS, Exterrata, Noachi, RaidShadowLily, Tejler, Luc"; +} \ No newline at end of file diff --git a/ShareBubbles/README.md b/ShareBubbles/README.md new file mode 100644 index 0000000..27e11cf --- /dev/null +++ b/ShareBubbles/README.md @@ -0,0 +1,43 @@ +# Share Bubbles + +Share Bubbles! Allows you to drop down bubbles containing Avatars & Props. Requires both users to have the mod installed. Synced over Mod Network. + +### How to use +Open a Content Details page and click the Share button. You can choose between placing a Share Bubble or Sharing directly with users in the same instance. When placing a Share Bubble, you will be prompted to configure the bubble. Once you're ready, you can then Drop or Select the bubble for placement. + +While viewing content details of an item shared *to you* by another player, you can also click **Unshare** to revoke access to the content. + +#### Visibility +- **Everyone** - All players with the mod installed can see the bubble. +- **Friends Only** - Only friends with the mod installed can see the bubble. + +#### Lifetime +- **2 Minutes** - The bubble will disappear after 2 minutes. +- **Session** - The bubble will only disappear once you delete it with Delete mode or leave the instance. + +#### Access Control +- **Keep Access** - Users who claim the bubble get to keep the shared content. +- **Session Access** - Users who claim the bubble only get to keep the shared content for however long they are in the instance with you. +- **No Access** - Users will not be able to claim the contents of the bubble and will only be able to view it. + +Access Control is only available for **Private Content** and serves as a way to share content in-game. + +**Note:** Session Access requires the game to be running to revoke access once you or the claimant leaves the instance. If the game is closed unexpectedly, the claimant will keep the content until you next launch the game and connect to an online instance. + +## Credits +- Noachi - the bubble +- RaidShadowLily - the particles +- Tejler - the bell sound +- Luc - the fixing pedestal api endpoint +- Exterrata - the loading hexagon model from the hit mod PropLoadingHexagon + +--- + +Here is the block of text where I tell you this mod is not affiliated with or endorsed by ABI. +https://documentation.abinteractive.net/official/legal/tos/#7-modding-our-games + +> This mod is an independent creation not affiliated with, supported by, or approved by Alpha Blend Interactive. + +> Use of this mod is done so at the user's own risk and the creator cannot be held responsible for any issues arising from its use. + +> To the best of my knowledge, I have adhered to the Modding Guidelines established by Alpha Blend Interactive. diff --git a/ShareBubbles/Resources/sharingbubble.assets b/ShareBubbles/Resources/sharingbubble.assets new file mode 100644 index 0000000000000000000000000000000000000000..339e6295b2715b05b3e24ed6ca24f1734cf77191 GIT binary patch literal 1293156 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⁡8 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
Choose who can see your Sharing Bubble.
How long the Sharing Bubble lasts. You can delete it at any time.
+ Users will keep access to this Private Content. You can manage permissions later on the Hub. +
+ Users can only access this Private Content while in the same instance. +
+ Users cannot access your Private Content through this share. +
You cannot control access to this content because it is Public or you do not own it.
Drop or place a bubble in the world that others can interact with
Share directly with specific users
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⁡8 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
w#ADHAfrj!-fS+D(ZXfOh<2nByapnyxM z!pa~R?1#Gl(=59}l`O;=NT6}*rr@Ts%XJNxI9sxW_vi`Ur8rgMDZ~ z)^)rCj^bGrREwVWd_=W#|WhbZ3ZdiOtW@e{b+N0FPgmuSp zH&IiqJ>^4Ph!$S4J;O)#niQYy+ixTMVHo%!KOH}1=uTIF#eUG1v{2LD8o5y*#mp#< zS!)+4R_pbzS4~{|8&9zX?g258pgI5BXn3j_#s2KhyHIq^mRMthf)@z%@7IRvz64(9 zy8c+{E^y#i(`Gy^@F8q91{9VK)kxG0*Pe{=2z%r<3gj%kp|o_*;5xp_{$w8i0D@ko zbOM_%Crw4(Y^m(vY7u(jiR_}5P#1}a)`YrhAw2f}=!l}?OUuLu!oK8Gv4SP{H`<@| zWjmd4J<{mu6)6{3@VCubIh#T)DU`=02rFgjv|nw(nsP|=m(k@4RKZjDPH}$iJs*1O zjIf&C(4&*9nZKzD<{bq2)zS$LThrn>K4eqM9o1g?Q35ug>WI`=M*e%T@}QlbqY8j1 zrh*pRsG0r=rvjT5L|Q^3a%3mM88WlTrV!|Pgau%*U^$&g3{4w=v~WL2D_*hT;Zl@o z`2Fcfe*WI9%yY3d(lpDMU(C8fIuzJNB|b|J;RYwryvOshnpQ}xnB$*}4bs`qDm0b* zb)>!drfM}`HOWkA1oIaZepLVXaDVMX&s9p*>QewP^&cg8SfR^fU z^QY1dNDKLp*egMbR9dwwK&by(?KsuE-?ihoDD4BBMyy`g^JS#l;cf1p=r zcMdeRt~Y8jlB^%gQpUS6oGj$Rzch|uHc?3UPv@HZ7 z@}ZfhTV|C9ygf$j?Y1jw93nWXZ8`awZ|;c9?!eYaopO)6Y-ps6yBHBjCXyREFwXfA z1*uNMf`|*+kIkq!R&7a5;&?udRR-GX$BGc=@Syy$S`53DCGW_&BaMu9D(*Ltr+IrF zc}M{wrdot!gP`$kgF8Axb_PO6SiMY9kPgnu19IbQb^1SQS2C|~U7~OvCdeKaT-(UN z+30SjnDAUkCy{wwA#c@W!*!`4XAd)7#K(QlO0|%O0uj6KgW*ff<`~yZSofO9jAnw= znXgt;c`f``Rg7@o-@bp7TRxnIJ%Ou611AdA<*Mu@ub3QKlfRAvdo&rX#`60EfOnF4 z?By1*HrHxoZ*aAsW2Yp5D&oSex3Qceg~k7wW70_6sZ7T($&;*9eD5^whqqW^R!)o6OMl^4b`?2hwZo+O<3{H z1H)gdFXI8o0uno6l=BziuLey7Nnc#=#y!PcqMuF>h-G48sr;eEzay5x_HomqH|#mO zNF$SwFKX=lvMSndiYO0$Who1a)0iz==yq_pQ`Sm|Sm+B_D;0^LqbXOOgs8b%WeGi) zm-#a|wN4uT6QMQyi+0{&VM3Afa}MNAfU zs0i}1lMF@V2(q=>j^CCtco*lX@mX`z0Pg_g*zEO^Uo6I34zH<=Y0e3U1Huna{Q2as zR!ZUh1huN4BKlJ&hYEub8UOL$#SsKIf3~O1QXo5cr_X2BY{GI6rbe*w#F|%f(b|Vn zD@Ccu3!^uF5iJV?5b`wr-^KgOfV8dipSjX6JJ08>l4z%}eAZu!hwJ$5E+F^u?_w*i zBF(d_-cS;7ayy2sGe)!zCsrQj{zk)Ka~Ah$VR<%70rak#Q`2GIC-mjzmJ&b_>T288 z-(YnxM5z%xE}Lxc2xANX>abOmS4J=c$pnd=i#-t+bKjC`IVii|{shD7PdGZx*W}1mmy!F!gf%v~%KC`|qp+ zC(-5F0P5_VIdi8!B3ICha}-^=xuhxL&~UWp1dhA`k01_~F>!Iv`3;mWfhL$p-*4-${v0UhxGt>xWCRu?g; zN5tg?@`>Ed-n(0f!{+gJvw;5e$j{cag3a@SKD>6s&bfcXvIw{*d3we`p4pKGY}dwk zUnF$Ku$IuV#UJ4GPj*)v?vx<*3ccTq?zmu{oWEsYf|;LXZ05?WXB6|T%X@_8^~6g!_yn?z=UO#i9P@#6!ifzd?q$3FB9K|xyi zGQc<>5&_g7r2}$%OMU&2q}lX*EkE1vcYjsVN#_0SRGI(Hb}nAY6| zwS^+lU=)5@A4UkS-Gw=*MXn0d!UGk!il^Ew(ZZKxxrBropSdKGWuZdd7-a}JWBy`< znq_+RZ2v^E6X5v}(>Yei553SPbjhD!s|tgT8F_2p$DTbCZ+f$ZI+JPV!uRlS5opcd ztjyw$lh2~0yAgyVuE;u?;7<9chSPHg4*8Hjr?#3|(IuA2z|IG`@YDxRY94?kDZ#|m zCIv1^x!yu!de~!J?$Owd+eDi>z0HezZg~*zomiG> zcBc~pO7l#<(;C;8Ja{oGi`8if2(4uBx_rq>k_A0x35X5?3&?ir-p%0x$DqMXU%Nln zk7Q8RhSL38e2?}5J#%?!5f=pz+~mBqFFISsfO;q^|HG*CfJ}ls5dteM5%P#OR>vfQ zBtU-W_W4o^X5*eZgI$&lEt^Nlhw3c>s)cRC%xnI;EuGUS0sVGxs2Ua(4VP-a9gJaR24dh2wXe`H;c6zql7r zgjkke;2Il!B1S*`#S$aQu>WVI3`3uV*1FbRt&es7$*N4?0IFDch~4p)oN>}#7;h;vHUNRXx>VB4gDJRWjl@O=sND(P;nNAgS)4c!r6^+yzxDEi@zCr8C)$AO&V9)u>&vUg`x%GJPER|a+Wmn`SD5&m4 zH=07=_lgtfx@3W96I{W$E_#K@A{WbJq`BHC>71W_IT9RfWdz6hcgczt4m{C zA?Fil_&+$*W!CxmxL7!0LJ{-zmSgyF47UN{!B@vlDM22M4vv*duR;Z_BgMj5fkJ$@ z06HvY@|fjWkuv!JIue&Hi$C7_l>uRJz9rM~YYuqnNWKZ6?$W#Wiu^E1 z9I;9cQg2>hm8AuRwfT>b0Y-e#VAJM@2{eOAAUSQMd*Bv=HA`z9ip@@HNi&NaaRphC z-J5vip`5z%Jr@8gTtk{rOnXcs6r=_uk5q8yF{EyY(MLB%XV7~Wy%ew-z3l}=5c)rt zqrpnc0(GnnALNBbnzcxhTIo{JJW^ci&VmH<8b%4#PSMQbU#6-tcH!=`UH~FUsY{BR z!#A~Z)TPINaxK<>hN3L3!*JH;hU7?`-2z)eE4-i2LA3zR;Xdt=s2eAu4r^AE&u{g= zDYS19ceTS%zy^$o6{gIleVV2Mq_9A=Y9z*73Q)X9YKJ8_u36yRgM4u{g8tK~lq=DdxXiypWzn1S=&Ib~?H#OdQ7Hu+@spT?`t$8E=&^Dn6 zVoc!z9RY4>LAOSi0-D>Fi)%xnZ+o^nWL@EKW=QO38S?W&kzUx1LYH2W(Q*bxa-*g! zgX?y_v2Pp|KyJ8WQKqqa=x9l7TpJ7@;o2`zZt`S;RQ1*Yo zTwkF9HNwp{(T{1tR%`LBABVc6{Pn~bE|79=aOXvuq!ZBXY9S51xJ1APK!cO_MCP5O zW?zz00RLGi-@0qIV)ag>54*ZN%FNQGt%J&WN{U|3OtQFIr7mGS;R3xuoVy_jA>R9>H}(p6ce|#;9H77d;JY z3fZTnfXKOvLg*%v(cDtokeYvs7(+V|RWq9yeNnWb?b4A-F#&zF7_qG_*=I&&8Iq2h zgFEr&$G{R-y7&LjM1|>r$|A2uh!!oTB^A}oJsbAxnH$j)Cp|(`kYn0)lF7sPc6&-n z2-5{<08>5iE2Bz=VsX6Fss)uhH8SMDxp@=Oz{#n<{YP84JW1(77ZM>O3nnGZv_Zq_ zX!gF=muP_jVTzk>xCvEX8?!e<*}dLdo7?qH43Z|P9ct0Ggvm+AD)e@^yk*EpuAFDc zS5yr9DYSl^&li4T_iA0he7oscn^p_q5n;-4(>iQy#&+<{W||)i_cR=u1U|8 z8%$R*)b;)+EUW}(KG!wNlhv(T;T!$Dcf&LE1rMg2IScf=LgRc*K%e~G6%B%f#9FHj z$oD1U7UDBq4N4)eey`ChZt%*d_zKqr+W4ruPBIk-aP~^%Cb>$`&MD3-SAc@WMy#5v zwkpH9NZXC5tmS%Di5NlEGvmI?9o{5Essz}V9@vnnjQ~PGORN}4>67vim6PK_F*+wt zR@2XJ5@|e|Pt+n(y51b?QFSI6I6ZElDT<5Sj3@tOJFsIjZ=gnyP7*3blS$^G|mp$KG9w^hSE%$D$>S>jXW52Uw#)ugFjineE zuWu9HxRKkaIhwY)R3&v7<>C;@*R&&WEEp`6(;6F%)PE%S4E= z{*F}q)Fc8fZ;QT|dvvUq87|c-g8zw;LM#4>60oHrAFP=| zX{KAWVZ*qp30Pi2b+7*)o!v?{_e?K`W%AlNAy9@7UMB|ZnpGwKbm)om zV6}u?_ABipfL^lt<*?UTzgRt}1DOHXqX)h&-*mq3E$-u@pVZ(?R&;v)%{K0+S?Dn= zeRh>T7-^s8T7@l?IEmNrJN&(du4kERj~c<0*R=Il+@Fn*^>(G3hB+fw}D^uA+8f)ly1hRq^S-Mf=g2(=>ca4Py}#ZuB3U;)1pH`$1gHksT*^Lht*1 z-ZXz!zK;l->WG-^kpii(?}co3S@gX}pV!Ye4=v#O-3%$vuC$3(6=Vj1Y` z{Lm0Qs{Z2r$M)8T^JFx|E@cN;ny#~A3k*v>?SR8)sFbCx3rijqqK=K&(_hZHhh%ax zznU7*H{g-IH5VhIOW{rWfCJ!wjhz44$kA}Jg!$Aq>fF=5y&N2$XniRya zFOODl3yGnb4c59%qD=_@^fgPzvi?KO5oq_94>myrAjEvZMNhv@R>`!q+3$W^Db{=b zIsirQu(%2AHzk9X4@hl1Rq<_#?)G}&Gpo2aOKQFbJXB8_S<^HpA0tO?=`=BfeqHSu z?fF!BQ-p2g`=p8iD&x~A*eOE@w(-xNCIW_O5E=A3dwbc2qEq1aJci3)@pU>kf&-$z5Ux?O>YJ4dXhv3!xXw&Z_*@1b zy`;bH8z+^Am>*1|kaGZO7wfu$ki)?d6_UHcChC#P#FC_1^tiOltfN0&ziw0RlH0-r zD;p$%czBd)_C&dGOP#vsdDcUX^2r&QAvVZ4%EiM^)i=a_EVpC_%AMIA#RQSa0_7JR z4;AnDt7SD+21rI5cLoK~IyLU`5}#=wp3O;jsxXb%oF;I2;Nmr?ox=t{?@ZNRqyqA! zb|ZlG!dYW6u~mE4n?3|tj(G1?%z|M);4p2{T_JTlQ~$jwS@}@3nL2;lX@tsGRxVsuY^*P;6?*6fIGF z@EfAw{ua(aWdf?x^^{s_kT1hu0h~51ZQ|JpUU@ErFZMe?iKgu4AXyFUn1-=nUYWPr@8 zB^@hnpNE2%SA!_fArdw&som44NENg#S+NGH2seL3ILiX}(SRKjrOSl+FE%^xz;g!g zO_jiSd8MJ%Ua#=6{N)XBi0yJUWLaTq^6<9UQHrVuxvjuvpMJwYBUA%k>9G3Zq-*NrPr$4nYUEoYMe#)C|_%B0N$Ox_DW3we= zHnVotot=g{&o`_A%G?L2w@azCBCMs;3Gbo6_vsVr`L`Z3PMs?eE?6QJwp-; ziGgbB2t(&BjMA1_EN(3+5J<-h78r0Of#Yk+-@;q3D35-=l(xH9U(`(@XL4d&4bFFJ zk2rcmL|wGeu7mFBU$4PbaP}JwR3OEZNXi#MTfNMqy+LR6>#q}7z4NOngD5f$(V2`F ztJx|0c0PLxS!DIw*8l(#e7}Y=4QSRAEB)x~5bc>zR#_n#O|Iq>q68zs?&i5({_Ghm zV$O$lVX~vLv0#~4ub3c_(5{z`>m2SrEjTa;^z?bcF3G#qOOGYf1IWTfX10sXetFmr zX(7gH{BnlZa+b&hlF#mwoJ>B&kw91qcV+urB@oWe=X=cmB}TUFyV5&w3h2IQ*vF-;y^Bf#$achbd5H&3 zI96=%e9L0@zBPHsgs zu9-^#DqKR$OTYQXd1Xm>&n*~YNnJL08wVJc!w^(LjEfl>*sPU~u>d`{D!D|}lVh$h%y~$nIzfSVX|I0*&t@i`6!)3=M04ZNN zs}+SB)aBjU)tUY#qoO(3czCxkK;V!wl98#%bm?E=p+6A?F{lAP>2?QZG0B2pVzxgm%uPkh zCWiEfYv^aNs1UeHmrf4}3XkVKjnx0@76N1y?A2H@NJQ>|W+LTGr#pS-f4UBy3({X! z{)&}d1J1kB{F>#11vAO{-KPX8k_aVSW}}E18-9njRHb8z?~bp#Vk92|ou1ITJ9hgQ zM+fvrh0}CHS9y85sjn>5p_G@M_3W+rE{I^z`GDPu)n}^)Pz4IRm5}x%%6y;OCOZ zC@HSmpxBzH?5b3Pz)TXq^l9ZjUp2Tdz<8(XJEEThpcOa_SHLLdrhWIuV=$7uD;5=}Yd7x&XCcpY?4&zNUeAk3CiEQ1z%iFeLnp4goDdx#_B90F$K zK_qJe$+nH9VbXT&z5oBi+=4o~mkds!coYFUAnk&-%9~Ny42YFbPE;C5UIFh*+3nDTph zkC%@x;l-$M;HwE?w*G)N5;D_tmu&Jb(BRr>`n0Z}bQ%@vYWI)^#m!QVz-w(@`fqXj zK!}kdu%;@qX>d{oeWGI+E71cht#BJtVt)BmU*>Ea3C2gPS|n`j+~=QC2(gB81vKY% z@uJ7*F7fY$j`#dQ0v3+%b1s&@7nerVR#KU$9@9wdgY8s{>OvPRYjD)LsD6bIp+oRN z;q7z(wClvXXpVX2ih3-6=~K5m!p}9V2Z%@Yoa*lHlAYOiBxw#wcL^2G=1fn`X`7n8 z64&Is?FTP`9tSgy(J|mitnggq5DZ9#$<}?>IF*_~K*04Ymx&v#q ziN6#3c-4JQy=WGB>>|ED_xWh2_2Uub)5JW>C>pkY#f?hdL?(<;g|v-po2cq|=y=Z? z5CerA%d=YQBW{fUV4yeWWq;vclhV#g^^HQ416ETH9Z?CXJ^D8W;pn@H`Cyx;3fHSC zTCd}DO1oX97-N5a-%z+=N`O=ma*$DT=Ga+Md+IJA8&{EH0VZAbaGlEeEuz_m(Q&q` zIg?Z6`k8l0Cg;QY-G&h$+wmcy9Y;S41P`%41Br=P17md&^t?EIQhXp|jS@F0R^Z+` zBdWhY$I$9+Pk#W5(Rf{@TdSuc$|+=Ss5AGQI0U*Hu-#jWy_an0M+KZessNzl%Tde$ z5tR||6GAT$$SOs$ehR0k>U%Fi4HkGyZ>#D1I&@Blm}QKk*Fvvx-47~NVRS?~⁡8 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⁡8 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
Y` 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⁡8 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
yGl5e4P_%p*59) ztkMh1=UN@_j4snK%6(MFBh1_OQGap+Pj4~r+mT&7_}wnH?74{C|Am+Aw-weDNP_M= zjTRe8JVbX4