From 127e48a582f7d5c6cdd835408f91596ef843e9d2 Mon Sep 17 00:00:00 2001 From: DrakiaXYZ Date: Sat, 26 Oct 2024 08:15:12 +0000 Subject: [PATCH] Redirect "PlayerPrefs" registry reads/writes to a JSON file (Memory-cached) to avoid cross contamination with live data (!172) Co-authored-by: DrakiaXYZ <565558+TheDgtl@users.noreply.github.com> Reviewed-on: https://dev.sp-tarkov.com/SPT/Modules/pulls/172 Co-authored-by: DrakiaXYZ Co-committed-by: DrakiaXYZ --- .../Patches/SaveRegistryToSptFolderPatches.cs | 237 ++++++++++++++++++ project/SPT.Custom/SPTCustomPlugin.cs | 1 + 2 files changed, 238 insertions(+) create mode 100644 project/SPT.Custom/Patches/SaveRegistryToSptFolderPatches.cs diff --git a/project/SPT.Custom/Patches/SaveRegistryToSptFolderPatches.cs b/project/SPT.Custom/Patches/SaveRegistryToSptFolderPatches.cs new file mode 100644 index 0000000..88cf3c3 --- /dev/null +++ b/project/SPT.Custom/Patches/SaveRegistryToSptFolderPatches.cs @@ -0,0 +1,237 @@ +using HarmonyLib; +using Newtonsoft.Json.Linq; +using SPT.Reflection.Patching; +using System; +using System.IO; +using System.Reflection; +using UnityEngine; + +namespace SPT.Custom.Patches +{ + /// + /// Redirect registry reads/writes to a folder in the SPT directory, instead of sharing + /// registry entries with live. + /// + /// Note this is a multi-patch to keep these patches grouped together, as we need to patch + /// many methods to properly implement this + /// + public class SaveRegistryToSptFolderPatches + { + private static readonly string _sptRegistryPath = Path.Combine(Environment.CurrentDirectory, "user", "sptRegistry"); + private static readonly string _registryFilePath = Path.Combine(_sptRegistryPath, "registry.json"); + private static JObject _sptRegistry = new JObject(); + + public void Enable() + { + Init(); + + new PatchPlayerPrefsSetInt().Enable(); + new PatchPlayerPrefsSetFloat().Enable(); + new PatchPlayerPrefsSetString().Enable(); + new PatchPlayerPrefsGetInt().Enable(); + new PatchPlayerPrefsGetFloat().Enable(); + new PatchPlayerPrefsGetString().Enable(); + new PatchPlayerPrefsHasKey().Enable(); + new PatchPlayerPrefsDeleteKey().Enable(); + new PatchPlayerPrefsDeleteAll().Enable(); + new PatchPlayerPrefsSave().Enable(); + } + + public void Init() + { + // Make sure the registry directory exists + if (!Directory.Exists(_sptRegistryPath)) + { + Directory.CreateDirectory(_sptRegistryPath); + } + + // Load the existing registry if found + if (File.Exists(_registryFilePath)) + { + _sptRegistry = JObject.Parse(File.ReadAllText(_registryFilePath)); + } + } + + public class PatchPlayerPrefsSetInt : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + MethodInfo method = AccessTools.Method(typeof(PlayerPrefs), nameof(PlayerPrefs.SetInt)); + return method; + } + + [PatchPrefix] + private static bool PatchPrefix(string key, int value) + { + _sptRegistry[key] = value; + return false; + } + } + + public class PatchPlayerPrefsSetFloat : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + MethodInfo method = AccessTools.Method(typeof(PlayerPrefs), nameof(PlayerPrefs.SetFloat)); + return method; + } + + [PatchPrefix] + private static bool PatchPrefix(string key, float value) + { + _sptRegistry[key] = value; + return false; + } + } + + public class PatchPlayerPrefsSetString : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + MethodInfo method = AccessTools.Method(typeof(PlayerPrefs), nameof(PlayerPrefs.SetString)); + return method; + } + + [PatchPrefix] + private static bool PatchPrefix(string key, string value) + { + _sptRegistry[key] = value; + return false; + } + } + + public class PatchPlayerPrefsGetInt : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + MethodInfo method = AccessTools.Method(typeof(PlayerPrefs), nameof(PlayerPrefs.GetInt), [typeof(string), typeof(int)]); + return method; + } + + [PatchPrefix] + private static bool PatchPrefix(ref int __result, string key, int defaultValue) + { + if (_sptRegistry.TryGetValue(key, out var value)) + { + __result = value.Value(); + } + else + { + __result = defaultValue; + } + return false; + } + } + + public class PatchPlayerPrefsGetFloat : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + MethodInfo method = AccessTools.Method(typeof(PlayerPrefs), nameof(PlayerPrefs.GetFloat), [typeof(string), typeof(float)]); + return method; + } + + [PatchPrefix] + private static bool PatchPrefix(ref float __result, string key, float defaultValue) + { + if (_sptRegistry.TryGetValue(key, out var value)) + { + __result = value.Value(); + } + else + { + __result = defaultValue; + } + return false; + } + } + + public class PatchPlayerPrefsGetString : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + MethodInfo method = AccessTools.Method(typeof(PlayerPrefs), nameof(PlayerPrefs.GetString), [typeof(string), typeof(string)]); + return method; + } + + [PatchPrefix] + private static bool PatchPrefix(ref string __result, string key, string defaultValue) + { + if (_sptRegistry.TryGetValue(key, out var value)) + { + __result = value.Value(); + } + else + { + __result = defaultValue; + } + return false; + } + } + + public class PatchPlayerPrefsHasKey : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + MethodInfo method = AccessTools.Method(typeof(PlayerPrefs), nameof(PlayerPrefs.HasKey)); + return method; + } + + [PatchPrefix] + private static bool PatchPrefix(ref bool __result, string key) + { + __result = _sptRegistry.ContainsKey(key); + return false; + } + } + + public class PatchPlayerPrefsDeleteKey : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + MethodInfo method = AccessTools.Method(typeof(PlayerPrefs), nameof(PlayerPrefs.DeleteKey)); + return method; + } + + [PatchPrefix] + private static bool PatchPrefix(string key) + { + _sptRegistry.Remove(key); + return false; + } + } + + public class PatchPlayerPrefsDeleteAll : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + MethodInfo method = AccessTools.Method(typeof(PlayerPrefs), nameof(PlayerPrefs.DeleteAll)); + return method; + } + + [PatchPrefix] + private static bool PatchPrefix() + { + _sptRegistry.RemoveAll(); + return false; + } + } + + public class PatchPlayerPrefsSave : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + MethodInfo method = AccessTools.Method(typeof(PlayerPrefs), nameof(PlayerPrefs.Save)); + return method; + } + + [PatchPrefix] + private static bool PatchPrefix() + { + File.WriteAllText(_registryFilePath, _sptRegistry.ToString()); + return false; + } + } + + } +} diff --git a/project/SPT.Custom/SPTCustomPlugin.cs b/project/SPT.Custom/SPTCustomPlugin.cs index b2c81df..7160a4d 100644 --- a/project/SPT.Custom/SPTCustomPlugin.cs +++ b/project/SPT.Custom/SPTCustomPlugin.cs @@ -27,6 +27,7 @@ namespace SPT.Custom new AddTraitorScavsPatch().Enable(); new CustomAiPatch().Enable(); new SaveSettingsToSptFolderPatch().Enable(); + new SaveRegistryToSptFolderPatches().Enable(); new QTEPatch().Enable(); new RedirectClientImageRequestsPatch().Enable(); new DisableGameModeAdjustButtonPatch().Enable();