Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Loading (#9) #11

Merged
merged 1 commit into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Implement Loading (#9)
* Not working; Set content manager level

* Set content manager level

* More trying around

* Update bug description

* More fooling around

* Update bug coment

* Fully implement loading.

Change how files are copied. (Maybe changing it again, I have an idea)
This also changes the blocking behaviour while a save is loaded.
Change loading menu label.

* Make fields static and init once

Untested

* What are static constructors

* Remove evenFlags set

It was in fact unneeded
  • Loading branch information
gitAdrianK authored May 8, 2024
commit 6c75a0a17f2ff653e0dae149ce6ae6e57d5e2512
4 changes: 0 additions & 4 deletions MoreSaves/ModEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ public static class ModEntry

public static Harmony harmony;
public static SaveLube saveLube;
/// <summary>
/// If it should be prevented or not that Jump King is able to save/load.
/// </summary>
public static bool shouldPrevent;

[MainMenuItemSetting]
public static TextButton LoadSavefile(object factory, GuiFormat format)
Expand Down
2 changes: 1 addition & 1 deletion MoreSaves/Models/ModelLoadOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public static MenuSelectorClosePopup CreateLoadOptions(object factory, GuiFormat

MenuSelectorClosePopup menuSelector = new MenuSelectorClosePopup(gui_left);

menuSelector.AddChild(new TextInfo("Loading will close JK!", Color.Red));
menuSelector.AddChild(new TextInfo("Load Save!", Color.White));

int num = 0;
for (int i = page * AMOUNT; i < page * AMOUNT + AMOUNT; i++)
Expand Down
148 changes: 102 additions & 46 deletions MoreSaves/Nodes/NodeLoadSave.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
using BehaviorTree;
using HarmonyLib;
using JumpKing;
using JumpKing.Level;
using JumpKing.MiscEntities.WorldItems.Inventory;
using JumpKing.MiscSystems.Achievements;
using JumpKing.SaveThread;
using JumpKing.Workshop;
using System;
using System.IO;
using System.Linq;
using System.Reflection;

namespace MoreSaves.Nodes
{
/// <summary>
/// Node to copy in a save into the Jump King folder.
/// Node to load a save from the mod into Jump King.
/// All required fields will be set and the JK menu will reload/update.
/// Force closes the game.
/// </summary>
public class NodeLoadSave : IBTnode
Expand All @@ -26,81 +30,133 @@ public class NodeLoadSave : IBTnode
private static readonly string INVENTORY = "inventory.inv";
private static readonly string SETTINGS = "general_settings.set";

/// <summary>
/// Folders that make up the path to the file.
/// </summary>
private string[] folders;
private static JKContentManager contentManager;
private static SaveManager saveManager;

private static Type saveLube;
private static Type encryption;
private static Type achievementManager;

private static MethodInfo setCombinedSave;
private static MethodInfo setPlayerStats;
private static MethodInfo setInventory;
private static MethodInfo setGeneralSettings;

private static MethodInfo loadFile;
private static MethodInfo loadCombinedSaveFile;
private static MethodInfo loadEventFlags;
private static MethodInfo loadPlayerStats;
private static MethodInfo loadInventory;

private static MethodInfo saveProgramStartInitialize;
private static MethodInfo saveCombinedSaveFile;

private static object achievementManagerInstance;
private static Traverse achievementManagerTraverse;

private string directory;

static NodeLoadSave()
{
// Classes and methods.
contentManager = Game1.instance.contentManager;
saveManager = SaveManager.instance;

saveLube = AccessTools.TypeByName("JumpKing.SaveThread.SaveLube");
encryption = AccessTools.TypeByName("FileUtil.Encryption.Encryption");
achievementManager = AccessTools.TypeByName("JumpKing.MiscSystems.Achievements.AchievementManager");

setCombinedSave = saveLube.GetMethod("set_CombinedSave");
setPlayerStats = saveLube.GetMethod("set_PlayerStatsAttemptSnapshot");
setInventory = saveLube.GetMethod("set_inventory");
setGeneralSettings = saveLube.GetMethod("set_generalSettings");

loadFile = encryption.GetMethod("LoadFile");
loadCombinedSaveFile = loadFile.MakeGenericMethod(typeof(CombinedSaveFile));
loadEventFlags = loadFile.MakeGenericMethod(typeof(EventFlagsSave));
loadPlayerStats = loadFile.MakeGenericMethod(typeof(PlayerStats));
loadInventory = loadFile.MakeGenericMethod(typeof(Inventory));

saveProgramStartInitialize = saveLube.GetMethod("ProgramStartInitialize");
saveCombinedSaveFile = saveLube.GetMethod("SaveCombinedSaveFile");

achievementManagerInstance = achievementManager.GetField("instance", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
achievementManagerTraverse = Traverse.Create(achievementManagerInstance);
}

public NodeLoadSave(params string[] folders)
{
this.folders = folders;
directory = ModEntry.dllDirectory;
foreach (string folder in folders)
{
directory += folder + SEP;
}
}

protected override BTresult MyRun(TickData p_data)
{
SaveManager saveManager = SaveManager.instance;
try
{
ModEntry.shouldPrevent = true;
saveManager.StopSaving();
// Optimally start gameplay after the reload.
Type saveLube = AccessTools.TypeByName("JumpKing.SaveThread.SaveLube");
Type encryption = AccessTools.TypeByName("FileUtil.Encryption.Encryption");
Type achievementManager = AccessTools.TypeByName("JumpKing.MiscSystems.Achievements.AchievementManager");

MethodInfo saveWithoutWrite = saveLube.GetMethod("SaveWithoutWrite", BindingFlags.NonPublic | BindingFlags.Static);
MethodInfo saveCombinedSaveFile = saveWithoutWrite.MakeGenericMethod(typeof(CombinedSaveFile));
MethodInfo saveEventFlags = saveWithoutWrite.MakeGenericMethod(typeof(EventFlagsSave));
MethodInfo savePlayerStats = saveWithoutWrite.MakeGenericMethod(typeof(PlayerStats));
MethodInfo saveInventory = saveWithoutWrite.MakeGenericMethod(typeof(Inventory));
MethodInfo saveGeneralSettings = saveWithoutWrite.MakeGenericMethod(typeof(GeneralSettings));

MethodInfo loadFile = encryption.GetMethod("LoadFile");
MethodInfo loadCombinedSaveFile = loadFile.MakeGenericMethod(typeof(CombinedSaveFile));
MethodInfo loadEventFlags = loadFile.MakeGenericMethod(typeof(EventFlagsSave));
MethodInfo loadPlayerStats = loadFile.MakeGenericMethod(typeof(PlayerStats));
MethodInfo loadInventory = loadFile.MakeGenericMethod(typeof(Inventory));

MethodInfo saveInit = saveLube.GetMethod("ProgramStartInitialize");

string directory = ModEntry.dllDirectory;
foreach (string folder in folders)
{
directory += folder + SEP;
}

// Load from dllDirectory
CombinedSaveFile combinedSaveFile = (CombinedSaveFile)loadCombinedSaveFile.Invoke(null, new object[] { $"{directory}{SEP}{SAVES}{SEP}{COMBINED}" });
EventFlagsSave eventFlags = (EventFlagsSave)loadEventFlags.Invoke(null, new object[] { $"{directory}{SEP}{SAVES_PERMA}{SEP}{EVENT}" });
PlayerStats playerStats = (PlayerStats)loadPlayerStats.Invoke(null, new object[] { $"{directory}{SEP}{SAVES_PERMA}{SEP}{STATS}" });
Inventory inventory = (Inventory)loadInventory.Invoke(null, new object[] { $"{directory}{SEP}{SAVES_PERMA}{SEP}{INVENTORY}" });
GeneralSettings generalSettings = XmlSerializerHelper.Deserialize<GeneralSettings>($"{directory}{SEP}{SAVES_PERMA}{SEP}{SETTINGS}");

saveCombinedSaveFile.Invoke(null, new object[] { SAVES, COMBINED, combinedSaveFile });
saveEventFlags.Invoke(null, new object[] { SAVES_PERMA, EVENT, eventFlags });
savePlayerStats.Invoke(null, new object[] { SAVES_PERMA, STATS, playerStats });
saveInventory.Invoke(null, new object[] { SAVES_PERMA, INVENTORY, inventory });
saveGeneralSettings.Invoke(null, new object[] { SAVES_PERMA, SETTINGS, generalSettings });
// Root and level.
string root;
Level level = null;

if (playerStats.steam_level_id == null)
{
root = "Content";
}
else
{
level = WorkshopManager.instance.levels.First(lvl => lvl.ID == playerStats.steam_level_id);
root = level.Root;
}

// Save and set
contentManager.ReinitializeAssets();

if (root == "Content")
{
contentManager.SetLevel(root);
}
else
{
contentManager.SetLevel(root, level);
}

setCombinedSave.Invoke(null, new object[] { combinedSaveFile });
setPlayerStats.Invoke(null, new object[] { playerStats });
setInventory.Invoke(null, new object[] { inventory });
setGeneralSettings.Invoke(null, new object[] { generalSettings });
EventFlagsSave.Save = eventFlags;

saveInit.Invoke(null, null);
saveCombinedSaveFile.Invoke(null, null);
saveProgramStartInitialize.Invoke(null, null);

object achievementManagerInstance = achievementManager.GetField("instance", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null);
var s = Traverse.Create(achievementManagerInstance).Field("m_snapshot").SetValue(playerStats);
achievementManagerTraverse.Field("m_snapshot").SetValue(playerStats);

// TODO: Set flags.
contentManager.LoadAssets(Game1.instance);
LevelManager.LoadScreens();

Game1.instance.contentManager.audio.menu.Select.Play();
contentManager.audio.menu.Select.Play();
Game1.instance.m_game.UpdateMenu();
// CONSIDER: Start gameplay.
}
catch (Exception e)
{
Game1.instance.contentManager.audio.menu.MenuFail.Play();
contentManager.audio.menu.MenuFail.Play();
throw e;
}
finally
{
saveManager.StartSaving();
ModEntry.shouldPrevent = false;
}
return BTresult.Success;
}
Expand Down
2 changes: 1 addition & 1 deletion MoreSaves/Nodes/NodeOpenFolderExplorer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ public class NodeOpenFolderExplorer : IBTnode
{
protected override BTresult MyRun(TickData p_data)
{
Game1.instance.contentManager.audio.menu.Select.Play();
Process.Start("explorer.exe", Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
Game1.instance.contentManager.audio.menu.Select.Play();
return BTresult.Success;
}
}
Expand Down
57 changes: 15 additions & 42 deletions MoreSaves/Patching/SaveLube.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,68 +13,41 @@ namespace MoreSaves.Patching
/// </summary>
public class SaveLube
{
private static Type saveLube;

private static MethodInfo originalSave;
private static MethodInfo saveCombined;
private static MethodInfo saveEventFlags;
private static MethodInfo savePlayerStats;
private static MethodInfo saveInventory;
private static MethodInfo saveGeneralSettings;
private static HarmonyMethod patchSave;

private static MethodInfo originalSCSF;
private static HarmonyMethod patchSCSF;

public SaveLube()
{
saveLube = AccessTools.TypeByName("JumpKing.SaveThread.SaveLube");
Type saveLube = AccessTools.TypeByName("JumpKing.SaveThread.SaveLube");

originalSave = saveLube.GetMethod("Save");
saveCombined = originalSave.MakeGenericMethod(typeof(CombinedSaveFile));
saveEventFlags = originalSave.MakeGenericMethod(typeof(EventFlagsSave));
savePlayerStats = originalSave.MakeGenericMethod(typeof(PlayerStats));
saveInventory = originalSave.MakeGenericMethod(typeof(Inventory));
saveGeneralSettings = originalSave.MakeGenericMethod(typeof(GeneralSettings));

patchSave = new HarmonyMethod(AccessTools.Method(typeof(SaveLube), nameof(PreventWriting)));
MethodInfo generic = saveLube.GetMethod("Save");
MethodInfo saveCombinedSaveFile = generic.MakeGenericMethod(typeof(CombinedSaveFile));
MethodInfo saveEventFlags = generic.MakeGenericMethod(typeof(EventFlagsSave));
MethodInfo savePlayerStats = generic.MakeGenericMethod(typeof(PlayerStats));
MethodInfo saveInventory = generic.MakeGenericMethod(typeof(Inventory));
MethodInfo saveGeneralSettings = generic.MakeGenericMethod(typeof(GeneralSettings));
HarmonyMethod patch = new HarmonyMethod(AccessTools.Method(typeof(SaveLube), nameof(CopySavefile)));

ModEntry.harmony.Patch(
saveCombined,
prefix: patchSave
saveCombinedSaveFile,
postfix: patch
);
ModEntry.harmony.Patch(
saveEventFlags,
prefix: patchSave
postfix: patch
);
ModEntry.harmony.Patch(
savePlayerStats,
prefix: patchSave
postfix: patch
);
ModEntry.harmony.Patch(
saveInventory,
prefix: patchSave
postfix: patch
);
ModEntry.harmony.Patch(
saveGeneralSettings,
prefix: patchSave
postfix: patch
);

originalSCSF = saveLube.GetMethod("SaveCombinedSaveFile");
patchSCSF = new HarmonyMethod(AccessTools.Method(typeof(SaveLube), nameof(SaveCombinedSaveFile)));

ModEntry.harmony.Patch(
originalSCSF,
postfix: patchSCSF
);
}

public static bool PreventWriting()
{
return !ModEntry.shouldPrevent;
}

public static void SaveCombinedSaveFile()
public static void CopySavefile()
{
if (ModEntry.saveName == string.Empty)
{
Expand Down