Merge pull request #549 from misternebula/better-profile-manager

Better profile manager
This commit is contained in:
_nebula 2022-08-09 20:27:20 +01:00 committed by GitHub
commit 121a83c29d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 2143 additions and 1118 deletions

View File

@ -1,9 +1,9 @@
using System;
namespace QSB.Patches;
namespace QSB;
[Flags]
public enum PatchVendor
public enum GameVendor
{
None = 0,
Epic = 1,

View File

@ -134,7 +134,7 @@ internal class MenuManager : MonoBehaviour, IAddComponentOnStart
var text = QSBCore.DebugSettings.UseKcpTransport ? QSBLocalization.Current.PublicIPAddress : QSBLocalization.Current.ProductUserID;
ConnectPopup.SetUpPopup(text, InputLibrary.menuConfirm, InputLibrary.cancel, new ScreenPrompt(QSBLocalization.Current.Connect), new ScreenPrompt(QSBLocalization.Current.Cancel), false);
ConnectPopup.SetInputFieldPlaceholderText(text);
ExistingNewCopyPopup.SetUpPopup(QSBLocalization.Current.HostExistingOrNew,
ExistingNewCopyPopup.SetUpPopup(QSBLocalization.Current.HostExistingOrNewOrCopy,
InputLibrary.menuConfirm,
InputLibrary.confirm2,
InputLibrary.signalscope,
@ -370,7 +370,7 @@ internal class MenuManager : MonoBehaviour, IAddComponentOnStart
TwoButtonInfoPopup.OnPopupConfirm += () => OnCloseInfoPopup(true);
TwoButtonInfoPopup.OnPopupCancel += () => OnCloseInfoPopup(false);
ExistingNewCopyPopup = CreateFourChoicePopup(QSBLocalization.Current.HostExistingOrNew,
ExistingNewCopyPopup = CreateFourChoicePopup(QSBLocalization.Current.HostExistingOrNewOrCopy,
QSBLocalization.Current.ExistingSave,
QSBLocalization.Current.NewSave,
QSBLocalization.Current.CopySave,
@ -381,7 +381,18 @@ internal class MenuManager : MonoBehaviour, IAddComponentOnStart
{
DebugLog.DebugWrite("Replacing multiplayer save with singleplayer save");
QSBCore.IsInMultiplayer = true;
StandaloneProfileManager.SharedInstance.SaveGame(QSBProfileManager._currentProfile.gameSave, null, null, null);
if (QSBCore.IsStandalone)
{
var currentProfile = QSBStandaloneProfileManager.SharedInstance.currentProfile;
QSBStandaloneProfileManager.SharedInstance.SaveGame(currentProfile.gameSave, null, null, null);
}
else
{
var gameSave = QSBMSStoreProfileManager.SharedInstance.currentProfileGameSave;
QSBMSStoreProfileManager.SharedInstance.SaveGame(gameSave, null, null, null);
}
Host(false);
};
@ -394,7 +405,18 @@ internal class MenuManager : MonoBehaviour, IAddComponentOnStart
{
DebugLog.DebugWrite("Replacing multiplayer save with singleplayer save");
QSBCore.IsInMultiplayer = true;
StandaloneProfileManager.SharedInstance.SaveGame(QSBProfileManager._currentProfile.gameSave, null, null, null);
if (QSBCore.IsStandalone)
{
var currentProfile = QSBStandaloneProfileManager.SharedInstance.currentProfile;
QSBStandaloneProfileManager.SharedInstance.SaveGame(currentProfile.gameSave, null, null, null);
}
else
{
var gameSave = QSBMSStoreProfileManager.SharedInstance.currentProfileGameSave;
QSBMSStoreProfileManager.SharedInstance.SaveGame(gameSave, null, null, null);
}
Host(false);
};
@ -544,9 +566,20 @@ internal class MenuManager : MonoBehaviour, IAddComponentOnStart
private void PreHost()
{
var profile = QSBProfileManager._currentProfile;
var doesSingleplayerSaveExist = profile.gameSave.loopCount > 1;
var doesMultiplayerSaveExist = profile.multiplayerGameSave.loopCount > 1;
bool doesSingleplayerSaveExist = false;
bool doesMultiplayerSaveExist = false;
if (!QSBCore.IsStandalone)
{
var manager = QSBMSStoreProfileManager.SharedInstance;
doesSingleplayerSaveExist = manager.currentProfileGameSave.loopCount > 1;
doesMultiplayerSaveExist = manager.currentProfileMultiplayerGameSave.loopCount > 1;
}
else
{
var profile = QSBStandaloneProfileManager.SharedInstance.currentProfile;
doesSingleplayerSaveExist = profile.gameSave.loopCount > 1;
doesMultiplayerSaveExist = profile.multiplayerGameSave.loopCount > 1;
}
if (doesSingleplayerSaveExist && doesMultiplayerSaveExist)
{
@ -587,8 +620,16 @@ internal class MenuManager : MonoBehaviour, IAddComponentOnStart
else
{
DebugLog.DebugWrite("Loading multiplayer game...");
var profile = QSBProfileManager._currentProfile;
PlayerData.Init(profile.multiplayerGameSave, profile.settingsSave, profile.graphicsSettings, profile.inputJSON);
if (QSBCore.IsStandalone)
{
var profile = QSBStandaloneProfileManager.SharedInstance.currentProfile;
PlayerData.Init(profile.multiplayerGameSave, profile.settingsSave, profile.graphicsSettings, profile.inputJSON);
}
else
{
var manager = QSBMSStoreProfileManager.SharedInstance;
PlayerData.Init(manager.currentProfileMultiplayerGameSave, manager.currentProfileGameSettings, manager.currentProfileGraphicsSettings, manager.currentProfileInputJSON);
}
}
_intentionalDisconnect = false;
@ -629,8 +670,16 @@ internal class MenuManager : MonoBehaviour, IAddComponentOnStart
QSBCore.IsInMultiplayer = true;
_intentionalDisconnect = false;
var profile = QSBProfileManager._currentProfile;
PlayerData.Init(profile.multiplayerGameSave, profile.settingsSave, profile.graphicsSettings, profile.inputJSON);
if (QSBCore.IsStandalone)
{
var profile = QSBStandaloneProfileManager.SharedInstance.currentProfile;
PlayerData.Init(profile.multiplayerGameSave, profile.settingsSave, profile.graphicsSettings, profile.inputJSON);
}
else
{
var manager = QSBMSStoreProfileManager.SharedInstance;
PlayerData.Init(manager.currentProfileMultiplayerGameSave, manager.currentProfileGameSettings, manager.currentProfileGraphicsSettings, manager.currentProfileInputJSON);
}
var address = ConnectPopup.GetInputText();
if (address == string.Empty)

View File

@ -8,7 +8,7 @@ public abstract class QSBPatch
{
public abstract QSBPatchTypes Type { get; }
public virtual PatchVendor PatchVendor => PatchVendor.Epic | PatchVendor.Steam | PatchVendor.Gamepass;
public virtual GameVendor PatchVendor => GameVendor.Epic | GameVendor.Steam | GameVendor.Gamepass;
public void DoPatches(Harmony instance) => instance.PatchAll(GetType());

View File

@ -14,7 +14,6 @@ public static class QSBPatchManager
private static readonly List<QSBPatch> _patchList = new();
private static readonly List<QSBPatchTypes> _patchedTypes = new();
private static PatchVendor _vendor = PatchVendor.None;
public static Dictionary<QSBPatchTypes, Harmony> TypeToInstance = new();
@ -30,30 +29,6 @@ public static class QSBPatchManager
TypeToInstance.Add(type, new Harmony(type.ToString()));
}
var gameAssemblyTypes = typeof(AstroObject).Assembly.GetTypes();
var isEpic = gameAssemblyTypes.Any(x => x.Name == "EpicEntitlementRetriever");
var isSteam = gameAssemblyTypes.Any(x => x.Name == "SteamEntitlementRetriever");
var isUWP = gameAssemblyTypes.Any(x => x.Name == "MSStoreEntitlementRetriever");
if (isEpic && !isSteam && !isUWP)
{
_vendor = PatchVendor.Epic;
}
else if (!isEpic && isSteam && !isUWP)
{
_vendor = PatchVendor.Steam;
}
else if (!isEpic && !isSteam && isUWP)
{
_vendor = PatchVendor.Gamepass;
}
else
{
// ???
DebugLog.ToConsole($"FATAL - Could not determine game vendor.", MessageType.Fatal);
}
DebugLog.DebugWrite($"Determined game vendor as {_vendor}", MessageType.Info);
DebugLog.DebugWrite("Patch Manager ready.", MessageType.Success);
}
@ -67,7 +42,7 @@ public static class QSBPatchManager
OnPatchType?.SafeInvoke(type);
//DebugLog.DebugWrite($"Patch block {Enum.GetName(typeof(QSBPatchTypes), type)}", MessageType.Info);
foreach (var patch in _patchList.Where(x => x.Type == type && x.PatchVendor.HasFlag(_vendor)))
foreach (var patch in _patchList.Where(x => x.Type == type && x.PatchVendor.HasFlag(QSBCore.GameVendor)))
{
//DebugLog.DebugWrite($" - Patching in {patch.GetType().Name}", MessageType.Info);
try

View File

@ -90,7 +90,7 @@
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="OuterWildsGameLibs" Version="1.1.12.168" IncludeAssets="compile" />
<PackageReference Include="OuterWildsGameLibs" Version="1.1.12.201" IncludeAssets="compile" />
<Reference Include="..\Mirror\*.dll" />
<Reference Include="..\UniTask\*.dll" />
<ProjectReference Include="..\EpicOnlineTransport\EpicOnlineTransport.csproj" />

View File

@ -6,6 +6,7 @@ using QSB.Localization;
using QSB.Menus;
using QSB.Patches;
using QSB.QuantumSync;
using QSB.SaveSync;
using QSB.Utility;
using QSB.WorldSync;
using System;
@ -55,6 +56,11 @@ public class QSBCore : ModBehaviour
Application.version.Split('.').Take(3).Join(delimiter: ".");
public static bool DLCInstalled => EntitlementsManager.IsDlcOwned() == EntitlementsManager.AsyncOwnershipStatus.Owned;
public static bool IncompatibleModsAllowed { get; private set; }
public static GameVendor GameVendor { get; private set; } = GameVendor.None;
public static bool IsStandalone => GameVendor is GameVendor.Epic or GameVendor.Steam;
public static IProfileManager ProfileManager => IsStandalone
? QSBStandaloneProfileManager.SharedInstance
: QSBMSStoreProfileManager.SharedInstance;
public static IMenuAPI MenuApi { get; private set; }
public static DebugSettings DebugSettings { get; private set; } = new();
public static Storage Storage { get; private set; } = new();
@ -70,6 +76,34 @@ public class QSBCore : ModBehaviour
"Vesper.AutoResume"
};
private static void DetermineGameVendor()
{
var gameAssemblyTypes = typeof(AstroObject).Assembly.GetTypes();
var isEpic = gameAssemblyTypes.Any(x => x.Name == "EpicEntitlementRetriever");
var isSteam = gameAssemblyTypes.Any(x => x.Name == "SteamEntitlementRetriever");
var isUWP = gameAssemblyTypes.Any(x => x.Name == "MSStoreEntitlementRetriever");
if (isEpic && !isSteam && !isUWP)
{
GameVendor = GameVendor.Epic;
}
else if (!isEpic && isSteam && !isUWP)
{
GameVendor = GameVendor.Steam;
}
else if (!isEpic && !isSteam && isUWP)
{
GameVendor = GameVendor.Gamepass;
}
else
{
// ???
DebugLog.ToConsole($"FATAL - Could not determine game vendor.", MessageType.Fatal);
}
DebugLog.DebugWrite($"Determined game vendor as {GameVendor}", MessageType.Info);
}
public void Awake()
{
EpicRerouter.ModSide.Interop.Go();
@ -77,6 +111,11 @@ public class QSBCore : ModBehaviour
// no, we cant localize this - languages are loaded after the splash screen
UIHelper.ReplaceUI(UITextType.PleaseUseController,
"<color=orange>Quantum Space Buddies</color> is best experienced with friends...");
DetermineGameVendor();
QSBPatchManager.Init();
QSBPatchManager.DoPatchType(QSBPatchTypes.OnModStart);
}
public void Start()
@ -133,7 +172,6 @@ public class QSBCore : ModBehaviour
return;
}
QSBPatchManager.Init();
DeterministicManager.Init();
QSBLocalization.Init();
@ -144,8 +182,6 @@ public class QSBCore : ModBehaviour
QSBWorldSync.Managers = components.OfType<WorldObjectManager>().ToArray();
QSBPatchManager.OnPatchType += OnPatchType;
QSBPatchManager.OnUnpatchType += OnUnpatchType;
QSBPatchManager.DoPatchType(QSBPatchTypes.OnModStart);
}
private static void OnPatchType(QSBPatchTypes type)

View File

@ -102,7 +102,7 @@ public class QSBNetworkManager : NetworkManager, IAddComponentOnStart
base.Awake();
InitPlayerName();
StandaloneProfileManager.SharedInstance.OnProfileSignInComplete += _ => InitPlayerName();
QSBCore.ProfileManager.OnProfileSignInComplete += _ => InitPlayerName();
playerPrefab = QSBCore.NetworkAssetBundle.LoadAsset<GameObject>("Assets/Prefabs/NETWORK_Player_Body.prefab");
playerPrefab.GetRequiredComponent<NetworkIdentity>().SetValue("m_AssetId", 1.ToGuid().ToString("N"));
@ -157,15 +157,22 @@ public class QSBNetworkManager : NetworkManager, IAddComponentOnStart
{
try
{
var titleScreenManager = FindObjectOfType<TitleScreenManager>();
var profileManager = titleScreenManager._profileManager;
if (profileManager.GetType().Name == "MSStoreProfileManager")
if (!QSBCore.IsStandalone)
{
PlayerName = (string)profileManager.GetType().GetProperty("userDisplayName").GetValue(profileManager);
PlayerName = QSBMSStoreProfileManager.SharedInstance.userDisplayName;
}
else
{
PlayerName = QSBProfileManager._currentProfile.profileName;
var currentProfile = QSBStandaloneProfileManager.SharedInstance.currentProfile;
if (currentProfile == null)
{
// probably havent created a profile yet
Delay.RunWhen(() => QSBStandaloneProfileManager.SharedInstance.currentProfile != null, () => InitPlayerName());
return;
}
PlayerName = currentProfile.profileName;
}
}
catch (Exception ex)

View File

@ -0,0 +1,33 @@
using HarmonyLib;
using QSB.Patches;
namespace QSB.SaveSync.Patches;
[HarmonyPatch(typeof(InGameProfileMenuManager))]
internal class InGameProfileMenuManagerPatches : QSBPatch
{
public override QSBPatchTypes Type => QSBPatchTypes.OnModStart;
[HarmonyPrefix]
[HarmonyPatch(nameof(InGameProfileMenuManager.InitializeOnAwake))]
public static bool InitializeOnAwake(InGameProfileMenuManager __instance)
{
if (!__instance._initialized)
{
TextTranslation.Get().OnLanguageChanged += __instance.UpdateLanguage;
__instance.UpdateLanguage();
__instance._profileManager = QSBCore.ProfileManager;
__instance._profileManager.OnProfileSignInComplete += __instance.OnProfileSignInComplete;
__instance._profileManager.OnProfileSignOutComplete += __instance.OnProfileSignOutComplete;
__instance._profileManager.OnProfileReadDone += __instance.OnProfileReadDone;
__instance._returnToGameSubmitAction.OnSubmitAction += __instance.OnResumeGameBtnSubmit;
__instance._returnToTitleSubmitAction.OnSubmitAction += __instance.OnTitleSubmitAction;
LoadManager.OnStartSceneLoad += __instance.OnStartSceneLoad;
LoadManager.OnCompleteSceneLoad += __instance.OnCompleteSceneLoad;
GlobalMessenger.AddListener("PlayerResurrection", new Callback(__instance.OnPlayerResurrection));
__instance._initialized = true;
}
return false;
}
}

View File

@ -0,0 +1,54 @@
using HarmonyLib;
using QSB.Patches;
using UnityEngine;
namespace QSB.SaveSync.Patches;
[HarmonyPatch(typeof(PlayerData))]
internal class PlayerDataPatches : QSBPatch
{
public override QSBPatchTypes Type => QSBPatchTypes.OnModStart;
[HarmonyPrefix]
[HarmonyPatch(nameof(PlayerData.ResetGame))]
public static bool ResetGame()
{
PlayerData._currentGameSave = new GameSave();
QSBCore.ProfileManager.SaveGame(PlayerData._currentGameSave, null, null, null);
return false;
}
[HarmonyPrefix]
[HarmonyPatch(nameof(PlayerData.SaveCurrentGame))]
public static bool SaveCurrentGame()
{
PlayerData._currentGameSave.version = Application.version;
QSBCore.ProfileManager.SaveGame(PlayerData._currentGameSave, null, null, null);
return false;
}
[HarmonyPrefix]
[HarmonyPatch(nameof(PlayerData.SaveInputSettings))]
public static bool SaveInputSettings()
{
QSBCore.ProfileManager.SaveGame(null, null, null, PlayerData.inputJSON);
return false;
}
[HarmonyPrefix]
[HarmonyPatch(nameof(PlayerData.SaveSettings))]
public static bool SaveSettings()
{
QSBCore.ProfileManager.SaveGame(null, PlayerData._settingsSave, PlayerData._graphicsSettings, PlayerData.inputJSON);
return false;
}
// this is actually still StandaloneProfileManager in the gamepass dll. game bug?
[HarmonyPrefix]
[HarmonyPatch(nameof(PlayerData.IsBusy))]
public static bool IsBusy(ref bool __result)
{
__result = QSBCore.ProfileManager.isBusyWithFileOps;
return false;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,19 @@
using HarmonyLib;
using QSB.Patches;
namespace QSB.SaveSync.Patches;
[HarmonyPatch(typeof(ProfileManagerUpdater))]
internal class ProfileManagerUpdaterPatches : QSBPatch
{
public override QSBPatchTypes Type => QSBPatchTypes.OnModStart;
[HarmonyPrefix]
[HarmonyPatch(nameof(ProfileManagerUpdater.Start))]
public static bool Start(ProfileManagerUpdater __instance)
{
__instance._profileManager = QSBCore.ProfileManager;
__instance.enabled = true;
return false;
}
}

View File

@ -0,0 +1,212 @@
using HarmonyLib;
using QSB.Patches;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace QSB.SaveSync.Patches;
[HarmonyPatch(typeof(ProfileMenuManager))]
internal class ProfileMenuManagerPatches : QSBPatch
{
public override QSBPatchTypes Type => QSBPatchTypes.OnModStart;
public override GameVendor PatchVendor => GameVendor.Epic | GameVendor.Steam;
[HarmonyPrefix]
[HarmonyPatch(nameof(ProfileMenuManager.OnCreateProfileConfirm))]
public static bool OnCreateProfileConfirm(ProfileMenuManager __instance)
{
__instance._inputPopupActivated = false;
var inputPopup = __instance._createProfileAction.GetInputPopup();
inputPopup.OnPopupValidate -= __instance.OnCreateProfileValidate;
inputPopup.OnInputPopupValidateChar -= __instance.OnValidateChar;
__instance._createProfileAction.OnSubmitAction -= __instance.OnCreateProfileConfirm;
QSBStandaloneProfileManager.SharedInstance.TryCreateProfile(__instance._createProfileAction.GetInputString());
inputPopup.CloseMenuOnOk(true);
__instance.PopulateProfiles();
__instance.SetCurrentProfileLabel();
inputPopup.EnableMenu(false);
if (__instance._firstTimeProfileCreation)
{
__instance._firstTimeProfileCreation = false;
__instance.UpdatePopupPrompts();
}
return false;
}
[HarmonyPrefix]
[HarmonyPatch(nameof(ProfileMenuManager.OnCreateProfileValidate))]
public static bool OnCreateProfileValidate(ProfileMenuManager __instance, ref bool __result)
{
var inputPopup = __instance._createProfileAction.GetInputPopup();
__result = QSBStandaloneProfileManager.SharedInstance.ValidateProfileName(inputPopup.GetInputText());
return false;
}
[HarmonyPrefix]
[HarmonyPatch(nameof(ProfileMenuManager.OnDeleteProfile))]
public static bool OnDeleteProfile(ProfileMenuManager __instance)
{
if (__instance._lastSelectedProfileAction != null)
{
__instance._deleteProfileConfirmPopup = null;
QSBStandaloneProfileManager.SharedInstance.DeleteProfile(__instance._lastSelectedProfileAction.GetLabelText());
__instance.PopulateProfiles();
__instance._lastSelectedProfileAction = null;
Locator.GetMenuInputModule().SelectOnNextUpdate(__instance._createProfileButton);
}
return false;
}
[HarmonyPrefix]
[HarmonyPatch(nameof(ProfileMenuManager.OnSwitchProfile))]
public static bool OnSwitchProfile(ProfileMenuManager __instance)
{
if (__instance._lastSelectedProfileAction != null)
{
__instance._switchProfileConfirmPopup = null;
if (QSBStandaloneProfileManager.SharedInstance.SwitchProfile(__instance._lastSelectedProfileAction.GetLabelText()))
{
__instance.PopulateProfiles();
__instance.SetCurrentProfileLabel();
__instance._lastSelectedProfileAction = null;
Locator.GetMenuInputModule().SelectOnNextUpdate(__instance._createProfileButton);
return false;
}
QSBStandaloneProfileManager.SharedInstance.OnBackupDataRestored += __instance.OnSwitchProfileDataRecoveryCompleted;
}
return false;
}
[HarmonyPrefix]
[HarmonyPatch(nameof(ProfileMenuManager.OnSwitchProfileDataRecoveryCompleted))]
public static bool OnSwitchProfileDataRecoveryCompleted(ProfileMenuManager __instance)
{
QSBStandaloneProfileManager.SharedInstance.OnBackupDataRestored -= __instance.OnSwitchProfileDataRecoveryCompleted;
__instance.PopulateProfiles();
__instance.SetCurrentProfileLabel();
__instance._lastSelectedProfileAction = null;
Locator.GetMenuInputModule().SelectOnNextUpdate(__instance._createProfileButton);
return false;
}
[HarmonyPrefix]
[HarmonyPatch(nameof(ProfileMenuManager.OnValidateChar))]
public static bool OnValidateChar(ProfileMenuManager __instance, char c, ref bool __result)
{
__result = __instance._createProfileAction.GetInputPopup().GetInputText().Length < QSBStandaloneProfileManager.SharedInstance.profileNameCharacterLimit
&& QSBStandaloneProfileManager.SharedInstance.IsValidCharacterForProfileName(c);
return false;
}
[HarmonyPrefix]
[HarmonyPatch(nameof(ProfileMenuManager.PopulateProfiles))]
public static bool PopulateProfiles(ProfileMenuManager __instance)
{
if (__instance._listProfileElements == null)
{
__instance._listProfileElements = new List<GameObject>();
}
else
{
for (int i = 0; i < __instance._listProfileElements.Count; i++)
{
TwoButtonActionElement requiredComponent = __instance._listProfileElements[i].GetRequiredComponent<TwoButtonActionElement>();
__instance.ClearProfileElementListeners(requiredComponent);
Object.Destroy(__instance._listProfileElements[i]);
}
__instance._listProfileElements.Clear();
}
if (__instance._listProfileUIElementLookup == null)
{
__instance._listProfileUIElementLookup = new List<ProfileMenuManager.ProfileElementLookup>();
}
else
{
__instance._listProfileUIElementLookup.Clear();
}
var array = QSBStandaloneProfileManager.SharedInstance.profiles.ToArray();
var profileName = QSBStandaloneProfileManager.SharedInstance.currentProfile.profileName;
var num = 0;
Selectable selectable = null;
for (int j = 0; j < array.Length; j++)
{
if (!(array[j].profileName == profileName))
{
GameObject gameObject = Object.Instantiate<GameObject>(__instance._profileItemTemplate);
gameObject.gameObject.SetActive(true);
gameObject.transform.SetParent(__instance._profileListRoot.transform);
gameObject.transform.localScale = new Vector3(1f, 1f, 1f);
Text[] componentsInChildren = gameObject.gameObject.GetComponentsInChildren<Text>();
for (int k = 0; k < componentsInChildren.Length; k++)
{
__instance._fontController.AddTextElement(componentsInChildren[k], true, true, false);
}
num++;
TwoButtonActionElement requiredComponent2 = gameObject.GetRequiredComponent<TwoButtonActionElement>();
Selectable requiredComponent3 = requiredComponent2.GetRequiredComponent<Selectable>();
__instance.SetUpProfileElementListeners(requiredComponent2);
requiredComponent2.SetLabelText(array[j].profileName);
Text component = requiredComponent2.GetButtonOne().GetComponent<Text>();
if (component != null)
{
__instance._fontController.AddTextElement(component, true, true, false);
}
component = requiredComponent2.GetButtonTwo().GetComponent<Text>();
if (component != null)
{
__instance._fontController.AddTextElement(component, true, true, false);
}
if (num == 1)
{
Navigation navigation = __instance._createProfileButton.navigation;
navigation.selectOnDown = gameObject.GetRequiredComponent<Selectable>();
__instance._createProfileButton.navigation = navigation;
Navigation navigation2 = requiredComponent3.navigation;
navigation2.selectOnUp = __instance._createProfileButton;
requiredComponent3.navigation = navigation2;
}
else
{
Navigation navigation3 = requiredComponent3.navigation;
Navigation navigation4 = selectable.navigation;
navigation3.selectOnUp = selectable;
navigation3.selectOnDown = null;
navigation4.selectOnDown = requiredComponent3;
requiredComponent3.navigation = navigation3;
selectable.navigation = navigation4;
}
__instance._listProfileElements.Add(gameObject);
selectable = requiredComponent3;
ProfileMenuManager.ProfileElementLookup profileElementLookup = new ProfileMenuManager.ProfileElementLookup();
profileElementLookup.profileName = array[j].profileName;
profileElementLookup.lastModifiedTime = array[j].lastModifiedTime;
profileElementLookup.confirmSwitchAction = requiredComponent2.GetSubmitActionOne() as SubmitActionConfirm;
profileElementLookup.confirmDeleteAction = requiredComponent2.GetSubmitActionTwo() as SubmitActionConfirm;
__instance._listProfileUIElementLookup.Add(profileElementLookup);
}
}
return false;
}
[HarmonyPrefix]
[HarmonyPatch(nameof(ProfileMenuManager.SetCurrentProfileLabel))]
public static bool SetCurrentProfileLabel(ProfileMenuManager __instance)
{
__instance._currenProfileLabel.text = UITextLibrary.GetString(UITextType.MenuProfile)
+ " "
+ QSBStandaloneProfileManager.SharedInstance.currentProfile.profileName;
return false;
}
}

View File

@ -0,0 +1,90 @@
using HarmonyLib;
using QSB.Patches;
using QSB.Utility;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace QSB.SaveSync.Patches;
[HarmonyPatch(typeof(TitleScreenManager))]
internal class TitleScreenManagerPatchesCommon : QSBPatch
{
public override QSBPatchTypes Type => QSBPatchTypes.OnModStart;
[HarmonyPrefix]
[HarmonyPatch(nameof(TitleScreenManager.Awake))]
public static bool Awake(TitleScreenManager __instance)
{
__instance._profileManager = QSBCore.ProfileManager;
__instance._profileManager.PreInitialize();
LoadManager.OnStartSceneLoad += __instance.OnStartSceneLoad;
LoadManager.OnCompleteSceneLoad += __instance.OnCompleteSceneLoad;
MenuStackManager.SharedInstance.OnMenuPush += __instance.OnMenuPush;
MenuStackManager.SharedInstance.OnMenuPop += __instance.OnMenuPop;
__instance._resumeGameTextSetter = __instance._resumeGameObject.GetComponentInChildren<ResumeGameLocalizedText>();
__instance.InitializePopupPrompts();
return false;
}
[HarmonyPrefix]
[HarmonyPatch(nameof(TitleScreenManager.InitializeProfileManagerCallbacks))]
public static bool InitializeProfileManagerCallbacks(TitleScreenManager __instance)
{
if (QSBCore.IsStandalone)
{
QSBStandaloneProfileManager.SharedInstance.OnNoProfilesExist += __instance.OnNoStandaloneProfilesExist;
QSBStandaloneProfileManager.SharedInstance.OnUpdatePlayerProfiles += __instance.OnUpdatePlayerProfiles;
QSBStandaloneProfileManager.SharedInstance.OnBrokenDataExists += __instance.OnBrokenDataExists;
}
else
{
QSBMSStoreProfileManager.SharedInstance.OnBrokenDataExists += __instance.OnBrokenDataExists;
}
__instance._profileManager.OnProfileSignInStart += __instance.OnProfileSignInStart;
__instance._profileManager.OnProfileSignInComplete += __instance.OnProfileSignInComplete;
__instance._profileManager.OnProfileSignOutStart += __instance.OnProfileSignOutStart;
__instance._profileManager.OnProfileSignOutComplete += __instance.OnProfileSignOutComplete;
__instance._profileManager.OnProfileReadDone += __instance.OnProfileManagerReadDone;
__instance._profileManager.Initialize();
return false;
}
[HarmonyPrefix]
[HarmonyPatch(nameof(TitleScreenManager.OnDestroy))]
public static bool OnDestroy(TitleScreenManager __instance)
{
if (QSBCore.IsStandalone)
{
QSBStandaloneProfileManager.SharedInstance.OnNoProfilesExist -= __instance.OnNoStandaloneProfilesExist;
QSBStandaloneProfileManager.SharedInstance.OnUpdatePlayerProfiles -= __instance.OnUpdatePlayerProfiles;
QSBStandaloneProfileManager.SharedInstance.OnBrokenDataExists -= __instance.OnBrokenDataExists;
}
else
{
QSBMSStoreProfileManager.SharedInstance.OnBrokenDataExists -= __instance.OnBrokenDataExists;
}
__instance._profileManager.OnProfileSignInStart -= __instance.OnProfileSignInStart;
__instance._profileManager.OnProfileSignInComplete -= __instance.OnProfileSignInComplete;
__instance._profileManager.OnProfileSignOutStart -= __instance.OnProfileSignOutStart;
__instance._profileManager.OnProfileSignOutComplete -= __instance.OnProfileSignOutComplete;
__instance._profileManager.OnProfileReadDone -= __instance.OnProfileManagerReadDone;
LoadManager.OnStartSceneLoad -= __instance.OnStartSceneLoad;
LoadManager.OnCompleteSceneLoad -= __instance.OnCompleteSceneLoad;
TextTranslation.Get().OnLanguageChanged -= __instance.OnLanguageChanged;
__instance._newGameAction.OnSubmitAction -= __instance.OnNewGameSubmit;
__instance._newGameAction.OnPostSetupPopup -= __instance.OnNewGameSetupPopup;
__instance._resetGameAction.OnSubmitAction -= __instance.OnResetGameSubmit;
__instance._accountPickerSubmitAction.OnAccountPickerSubmitEvent -= __instance.OnAccountPickerSubmitEvent;
MenuStackManager.SharedInstance.OnMenuPush -= __instance.OnMenuPush;
MenuStackManager.SharedInstance.OnMenuPop -= __instance.OnMenuPop;
return false;
}
}

View File

@ -0,0 +1,23 @@
using HarmonyLib;
using QSB.Patches;
using System.Reflection;
using UnityEngine.UI;
namespace QSB.SaveSync.Patches;
[HarmonyPatch(typeof(TitleScreenManager))]
internal class TitleScreenManagerPatchesGamepass : QSBPatch
{
public override QSBPatchTypes Type => QSBPatchTypes.OnModStart;
public override GameVendor PatchVendor => GameVendor.Gamepass;
[HarmonyPrefix]
[HarmonyPatch("SetUserAccountDisplayInfo")]
public static bool SetUserAccountDisplayInfo(TitleScreenManager __instance)
{
var text = (Text)__instance.GetType().GetField("_gamertagDisplay", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance);
text.text = ""; // no idea why, mobius be like
text.text = QSBMSStoreProfileManager.SharedInstance.userDisplayName;
return false;
}
}

View File

@ -0,0 +1,45 @@
using HarmonyLib;
using QSB.Patches;
namespace QSB.SaveSync.Patches;
[HarmonyPatch(typeof(TitleScreenManager))]
internal class TitleScreenManagerPatchesStandalone : QSBPatch
{
public override QSBPatchTypes Type => QSBPatchTypes.OnModStart;
public override GameVendor PatchVendor => GameVendor.Epic | GameVendor.Steam;
[HarmonyPrefix]
[HarmonyPatch(nameof(TitleScreenManager.OnBrokenDataExists))]
public static bool OnBrokenDataExists(TitleScreenManager __instance)
{
__instance._titleMenuRaycastBlocker.blocksRaycasts = false;
__instance._inputModule.EnableInputs();
__instance._waitingOnBrokenDataResponse = true;
var flag = QSBStandaloneProfileManager.SharedInstance.BackupExistsForBrokenData();
var text = UITextLibrary.GetString(UITextType.SaveRestore_CorruptedMsg);
if (flag)
{
text = text + " " + UITextLibrary.GetString(UITextType.SaveRestore_LoadPreviousMsg);
}
__instance._okCancelPopup.ResetPopup();
__instance._okCancelPopup.SetUpPopup(text, InputLibrary.confirm, InputLibrary.cancel, __instance._confirmActionPrompt, __instance._cancelActionPrompt, true, flag);
__instance._okCancelPopup.OnPopupConfirm += __instance.OnUserConfirmRestoreData;
__instance._okCancelPopup.OnPopupCancel += __instance.OnUserCancelRestoreData;
__instance._okCancelPopup.EnableMenu(true);
return false;
}
[HarmonyPrefix]
[HarmonyPatch(nameof(TitleScreenManager.OnUserConfirmRestoreData))]
public static bool OnUserConfirmRestoreData(TitleScreenManager __instance)
{
__instance._waitingOnBrokenDataResponse = false;
QSBStandaloneProfileManager.SharedInstance.RestoreCurrentProfileBackup();
__instance.OnProfileManagerReadDone();
__instance._okCancelPopup.OnPopupConfirm -= __instance.OnUserConfirmRestoreData;
__instance._okCancelPopup.OnPopupCancel -= __instance.OnUserCancelRestoreData;
return false;
}
}

View File

@ -0,0 +1,429 @@
using Microsoft.Xbox;
using Newtonsoft.Json;
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Xml.Serialization;
using UnityEngine;
using UnityEngine.InputSystem;
namespace QSB.SaveSync;
internal class QSBMSStoreProfileManager : IProfileManager
{
private const string OW_SAVE_CONTAINER_NAME = "GameSave";
private const string OW_GAME_SAVE_BLOB_NAME = "Outer Wilds Converted";
private const string OW_GAME_SETTINGS_BLOB_NAME = "PCGameSettings";
private static QSBMSStoreProfileManager _sharedInstance;
private QSBX1SaveData _saveData;
private const string c_containerName = "OuterWildsConnectedStorage";
private GameSave _pendingGameSave;
private SettingsSave _pendingSettingsSave;
private GraphicSettings _pendingGfxSettingsSave;
private string _pendingInputActionsSave = "";
private JsonSerializer _jsonSerializer;
private bool _initialized;
private int _fileOpsBusyLocks;
private bool _preInitialized;
private bool _isLoadingGameBlob;
private bool _isLoadingSettingsBlob;
public static QSBMSStoreProfileManager SharedInstance
{
get
{
if (_sharedInstance == null)
{
_sharedInstance = new QSBMSStoreProfileManager();
}
return _sharedInstance;
}
}
public GameSave currentProfileGameSave => _saveData.gameSave;
public GameSave currentProfileMultiplayerGameSave => _saveData.gameMultSave;
public SettingsSave currentProfileGameSettings => _saveData.settings;
public GraphicSettings currentProfileGraphicsSettings => _saveData.gfxSettings;
public string currentProfileInputJSON => _saveData.inputActionsJson;
public bool isInitialized { get; }
public bool isBusyWithFileOps => _fileOpsBusyLocks > 0;
public bool hasPendingSaveOperation => _pendingGameSave != null || _pendingSettingsSave != null || _pendingGfxSettingsSave != null || _pendingInputActionsSave != null;
public bool saveSystemAvailable { get; private set; }
public string userDisplayName => Gdk.Helpers.currentGamertag;
public delegate void BrokenDataExistsEvent();
public event BrokenDataExistsEvent OnBrokenDataExists;
public event ProfileDataSavedEvent OnProfileDataSaved;
public event ProfileReadDoneEvent OnProfileReadDone;
public event ProfileSignInCompleteEvent OnProfileSignInComplete;
public event ProfileSignInStartEvent OnProfileSignInStart;
public event ProfileSignOutCompleteEvent OnProfileSignOutComplete;
public event ProfileSignOutStartEvent OnProfileSignOutStart;
public void Initialize()
{
if (!_initialized)
{
Gdk.Helpers.SignIn();
SpinnerUI.Show();
Debug.Log("MSStoreProfileManager.Initialize");
Gdk.Helpers.OnGameSaveSucceeded += OnGameSaveComplete;
Gdk.Helpers.OnGameSaveFailed += OnGameSaveFailed;
Gdk.Helpers.OnGameSaveLoaded += OnGameSaveLoaded;
Gdk.Helpers.OnGameSaveLoadFailed += OnGameSaveLoadFailed;
Achievements.Init();
var serializationBinder = new VersionDeserializationBinder();
_jsonSerializer = new JsonSerializer
{
SerializationBinder = serializationBinder
};
_initialized = true;
return;
}
OnProfileSignInComplete?.Invoke(ProfileManagerSignInResult.COMPLETE);
var onProfileReadDone = OnProfileReadDone;
if (onProfileReadDone == null)
{
return;
}
onProfileReadDone();
}
public void PreInitialize()
{
if (_preInitialized)
{
return;
}
_fileOpsBusyLocks = 0;
_pendingGameSave = null;
_pendingSettingsSave = null;
_pendingGfxSettingsSave = null;
_pendingInputActionsSave = null;
_preInitialized = true;
}
public void InvokeProfileSignInComplete()
{
var onProfileSignInComplete = OnProfileSignInComplete;
if (onProfileSignInComplete == null)
{
return;
}
onProfileSignInComplete(ProfileManagerSignInResult.COMPLETE);
}
public void InvokeSaveSetupComplete()
{
saveSystemAvailable = true;
_isLoadingGameBlob = true;
_saveData = new QSBX1SaveData();
LoadGame(OW_GAME_SAVE_BLOB_NAME);
}
public void InitializeForEditor()
{
}
public void SaveGame(GameSave gameSave, SettingsSave settSave, GraphicSettings gfxSettings, string inputJSON)
{
Debug.Log("MSStoreProfileManager.SaveGame");
if (isBusyWithFileOps || LoadManager.IsBusy())
{
_pendingGameSave = gameSave;
_pendingSettingsSave = settSave;
_pendingGfxSettingsSave = gfxSettings;
_pendingInputActionsSave = inputJSON;
return;
}
var gameSaveData = new QSBX1SaveData();
var settingsSaveData = new QSBX1SaveData();
var saveGameSave = false;
if (gameSave != null)
{
saveGameSave = true;
if (QSBCore.IsInMultiplayer)
{
_saveData.gameMultSave = gameSave;
gameSaveData.gameMultSave = gameSave;
}
else
{
_saveData.gameSave = gameSave;
gameSaveData.gameSave = gameSave;
}
}
var saveGameSettings = false;
if (settSave != null)
{
saveGameSettings = true;
_saveData.settings = settSave;
settingsSaveData.settings = settSave;
}
else
{
settingsSaveData.settings = _saveData.settings;
}
if (gfxSettings != null)
{
saveGameSettings = true;
_saveData.gfxSettings = gfxSettings;
settingsSaveData.gfxSettings = gfxSettings;
}
else
{
settingsSaveData.gfxSettings = _saveData.gfxSettings;
}
if (!string.IsNullOrEmpty(inputJSON))
{
saveGameSettings = true;
_saveData.inputActionsJson = inputJSON;
settingsSaveData.inputActionsJson = inputJSON;
}
else if (!string.IsNullOrEmpty(_saveData.inputActionsJson))
{
settingsSaveData.inputActionsJson = _saveData.inputActionsJson;
}
else
{
settingsSaveData.inputActionsJson = ((InputManager)OWInput.SharedInputManager).commandManager.DefaultInputActions.ToJson();
}
if (saveGameSave)
{
WriteSaveToStorage(gameSaveData, OW_GAME_SAVE_BLOB_NAME);
}
if (saveGameSettings)
{
WriteSaveToStorage(settingsSaveData, OW_GAME_SETTINGS_BLOB_NAME);
}
}
private void LoadGame(string blobName)
{
_fileOpsBusyLocks++;
Gdk.Helpers.LoadSaveData(blobName);
}
private void WriteSaveToStorage(QSBX1SaveData saveData, string blobName)
{
Debug.Log("Saving to storage: " + blobName);
_fileOpsBusyLocks++;
var memoryStream = new MemoryStream();
using (JsonWriter jsonWriter = new JsonTextWriter(new StreamWriter(memoryStream)))
{
_jsonSerializer.Serialize(jsonWriter, saveData);
}
var buffer = memoryStream.GetBuffer();
Gdk.Helpers.Save(buffer, blobName);
}
public void PerformPendingSaveOperation()
{
if (!isBusyWithFileOps && !LoadManager.IsBusy())
{
SaveGame(_pendingGameSave, _pendingSettingsSave, _pendingGfxSettingsSave, _pendingInputActionsSave);
_pendingGameSave = null;
_pendingSettingsSave = null;
_pendingGfxSettingsSave = null;
_pendingInputActionsSave = null;
}
}
private void OnGameSaveComplete(object sender, string blobName)
{
_fileOpsBusyLocks--;
Debug.Log("[GDK] save to blob " + blobName + " complete");
}
private void OnGameSaveFailed(object sender, string blobName)
{
_fileOpsBusyLocks--;
Debug.Log("[GDK] save to blob " + blobName + " failed");
}
private void OnGameSaveLoaded(object sender, string blobName, GameSaveLoadedArgs saveData)
{
_fileOpsBusyLocks--;
Debug.Log("[GDK] save file load complete! blob name: " + blobName);
var memoryStream = new MemoryStream(saveData.Data);
memoryStream.Seek(0L, SeekOrigin.Begin);
using (var jsonTextReader = new JsonTextReader(new StreamReader(memoryStream)))
{
var x1SaveData = _jsonSerializer.Deserialize<QSBX1SaveData>(jsonTextReader);
if (_isLoadingGameBlob)
{
if (x1SaveData != null)
{
if (x1SaveData.gameSave == null)
{
Debug.Log("[GDK] tempSaveData.gameSave is null (oh no)");
}
_saveData.gameSave = x1SaveData.gameSave ?? new GameSave();
}
else
{
Debug.Log("[GDK] tempSaveData is null (oh no)");
_saveData.gameSave = new GameSave();
}
}
else
{
if (x1SaveData != null)
{
_saveData.gfxSettings = x1SaveData.gfxSettings ?? new GraphicSettings(true);
_saveData.settings = x1SaveData.settings ?? new SettingsSave();
_saveData.inputActionsJson = x1SaveData.inputActionsJson ?? ((InputManager)OWInput.SharedInputManager).commandManager.DefaultInputActions.ToJson();
}
else
{
_saveData.gfxSettings = new GraphicSettings(true);
_saveData.settings = new SettingsSave();
_saveData.inputActionsJson = ((InputManager)OWInput.SharedInputManager).commandManager.DefaultInputActions.ToJson();
}
Debug.Log(string.Format("after settings load, _saveData.gameSave is null: {0}", _saveData.gameSave == null));
Debug.Log(string.Format("_saveData loopCount: {0}", _saveData.gameSave.loopCount));
}
}
if (_isLoadingGameBlob)
{
_isLoadingGameBlob = false;
LoadGame(OW_GAME_SETTINGS_BLOB_NAME);
_isLoadingSettingsBlob = true;
return;
}
if (_isLoadingSettingsBlob)
{
_isLoadingSettingsBlob = false;
var onProfileReadDone = OnProfileReadDone;
if (onProfileReadDone == null)
{
return;
}
onProfileReadDone();
}
}
private void OnGameSaveLoadFailed(object sender, string blobName)
{
_fileOpsBusyLocks--;
if (_isLoadingGameBlob)
{
_saveData.gameSave = new GameSave();
SaveGame(_saveData.gameSave, null, null, null);
_isLoadingGameBlob = false;
LoadGame(OW_GAME_SETTINGS_BLOB_NAME);
_isLoadingSettingsBlob = true;
return;
}
if (_isLoadingSettingsBlob)
{
_saveData.settings = new SettingsSave();
_saveData.gfxSettings = new GraphicSettings(true);
_saveData.inputActionsJson = ((InputManager)OWInput.SharedInputManager).commandManager.DefaultInputActions.ToJson();
SaveGame(null, _saveData.settings, _saveData.gfxSettings, _saveData.inputActionsJson);
_isLoadingSettingsBlob = false;
var onProfileReadDone = OnProfileReadDone;
if (onProfileReadDone == null)
{
return;
}
onProfileReadDone();
}
}
[Serializable]
public class QSBX1SaveData
{
[XmlElement("gameSave")]
public GameSave gameSave;
[XmlElement("gameMultSave")]
[OptionalField(VersionAdded = 5)]
public GameSave gameMultSave;
[XmlElement("settings")]
public SettingsSave settings;
[XmlElement("gfxSettings")]
[OptionalField(VersionAdded = 2)]
public GraphicSettings gfxSettings;
[OptionalField(VersionAdded = 3)]
[NonSerialized]
public InputRebindableData bindingSettings;
[OptionalField(VersionAdded = 4)]
public string inputActionsPacked;
private InputActionAsset _inputActionsSave;
[JsonIgnore]
public string inputActionsJson
{
get => inputActionsPacked;
set
{
inputActionsPacked = value;
if (!string.IsNullOrEmpty(inputActionsPacked))
{
_inputActionsSave = InputActionAsset.FromJson(inputActionsPacked);
return;
}
_inputActionsSave = ((InputManager)OWInput.SharedInputManager).commandManager.DefaultInputActions;
}
}
[JsonIgnore]
public InputActionAsset inputActionsSave
{
get
{
if (_inputActionsSave == null && !string.IsNullOrEmpty(inputActionsPacked))
{
try
{
_inputActionsSave = InputActionAsset.FromJson(inputActionsPacked);
}
catch (Exception)
{
_inputActionsSave = null;
}
}
return _inputActionsSave;
}
}
[OnDeserializing]
private void SetDefaultValuesOnDeserializing(StreamingContext context)
{
gfxSettings = null;
bindingSettings = null;
inputActionsPacked = null;
}
}
}

View File

@ -1,12 +0,0 @@
using QSB.Utility;
using System.Collections.Generic;
namespace QSB.SaveSync;
public static class QSBProfileManager
{
public static readonly List<QSBProfileData> _profiles = new();
public static QSBProfileData _currentProfile;
public static QSBProfileData mostRecentProfile
=> _profiles.MaxBy(x => x.lastModifiedTime);
}

File diff suppressed because it is too large Load Diff