*Internally screams* A unified save core handles save cases where things go wrong.

This commit is contained in:
2018-09-21 10:46:11 -07:00
parent 7e40cb2583
commit 036e090b80
16 changed files with 473 additions and 80 deletions

View File

@ -1,32 +0,0 @@
using System;
using Omegasis.SaveAnywhere.Framework;
namespace Omegasis.SaveAnywhere.API
{
public class SaveAnywhereAPI : ISaveAnywhereAPI
{
public event EventHandler BeforeSave;
public event EventHandler AfterSave;
public event EventHandler AfterLoad;
public SaveAnywhereAPI(SaveManager manager)
{
BeforeSave = new EventHandler(empty);
AfterSave= new EventHandler(empty);
AfterLoad= new EventHandler(empty);
manager.BeforeSave += (sender, e) => { BeforeSave.Invoke(sender, e); };
manager.AfterSave += (sender, e) => { AfterSave.Invoke(sender, e); };
manager.AfterLoad += (sender, e) => { AfterLoad.Invoke(sender, e); };
}
/// <summary>
/// Used to initialize empty event handlers.
/// </summary>
/// <param name="o"></param>
/// <param name="args"></param>
private void empty(object o, EventArgs args){
}
}
}

View File

@ -35,21 +35,7 @@ namespace Omegasis.SaveAnywhere.Framework
/// <summary> Currently displayed save menu (null if no menu is displayed) </summary>
private NewSaveGameMenu currentSaveMenu;
/*********
** Events
*********/
/// <summary>
/// Event that fires before game save
/// </summary>
public event EventHandler BeforeSave;
/// <summary>
/// Event that fires after game save
/// </summary>
public event EventHandler AfterSave;
/// <summary>
/// Event that fires after game load
/// </summary>
public event EventHandler AfterLoad;
/*********
** Public methods
@ -64,10 +50,6 @@ namespace Omegasis.SaveAnywhere.Framework
this.Reflection = reflection;
this.OnLoaded = onLoaded;
this.BeforeSave = new EventHandler(empty);
this.AfterSave = new EventHandler(empty);
this.AfterLoad = new EventHandler(empty);
}
private void empty(object o, EventArgs args)
@ -97,8 +79,8 @@ namespace Omegasis.SaveAnywhere.Framework
{
currentSaveMenu.SaveComplete -= CurrentSaveMenu_SaveComplete;
currentSaveMenu = null;
AfterSave.Invoke(this, EventArgs.Empty);
//AfterSave.Invoke(this, EventArgs.Empty);
UnifiedSaveCore.UnifiedSaveCore.SaveEvents_AfterSave(this, EventArgs.Empty);
}
/// <summary>Clear saved data.</summary>
@ -111,8 +93,8 @@ namespace Omegasis.SaveAnywhere.Framework
/// <summary>Initiate a game save.</summary>
public void BeginSaveData()
{
// Fire Event before saving data
BeforeSave.Invoke(this, EventArgs.Empty);
SaveAnywhere.ModMonitor.Log("SVE ANYWHERE WHAT YOU DOING???");
UnifiedSaveCore.UnifiedSaveCore.SaveEvents_BeforeSave(this, EventArgs.Empty);
// save game data
Farm farm = Game1.getFarm();
@ -164,8 +146,8 @@ namespace Omegasis.SaveAnywhere.Framework
this.OnLoaded?.Invoke();
// Notify other mods that load is complete
AfterLoad.Invoke(this, EventArgs.Empty);
//AfterLoad.Invoke(this, EventArgs.Empty);
UnifiedSaveCore.UnifiedSaveCore.SaveEvents_AfterLoad(this, EventArgs.Empty);
}
/// <summary>

View File

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Omegasis.SaveAnywhere.API;
using Omegasis.SaveAnywhere.Framework;
using StardewModdingAPI;
using StardewModdingAPI.Events;
@ -69,16 +68,6 @@ namespace Omegasis.SaveAnywhere
customMenuOpen = false;
}
/// <summary>
/// Exposes the SaveAnywhere API to other SMAPI mods
/// </summary>
/// <returns></returns>
public override object GetApi()
{
SaveAnywhereAPI api = new SaveAnywhereAPI(SaveManager);
return api;
}
/*Notes. Mods that want to support save anywhere will get the api for Save anywhere and then add their clean up code to the events that happen for Before/After Save and Loading.
Example with pseudo code.
SaveAnywhere.api.BeforeSave+=StardustCore.Objects.CleanUpBeforeSave;

View File

@ -70,13 +70,14 @@
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Xml" />
<Reference Include="UnifiedSaveCore">
<HintPath>..\UnifiedSaveCore\bin\Release\UnifiedSaveCore.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\GlobalAssemblyInfo.cs">
<Link>Properties\GlobalAssemblyInfo.cs</Link>
</Compile>
<Compile Include="API\ISaveAnywhereAPI.cs" />
<Compile Include="API\SaveAnywhereAPI.cs" />
<Compile Include="Framework\ModConfig.cs" />
<Compile Include="Framework\Models\CharacterType.cs" />
<Compile Include="Framework\Models\PositionData.cs" />

View File

@ -1,10 +1,11 @@
{
"Name": "Save Anywhere",
"Author": "Alpha_Omegasis",
"Version": "2.8.1",
"Version": "3.0.0",
"Description": "Lets you save almost anywhere.",
"UniqueID": "Omegasis.SaveAnywhere",
"EntryDll": "SaveAnywhere.dll",
"MinimumApiVersion": "2.3",
"UpdateKeys": [ "Nexus:444" ]
"MinimumApiVersion": "2.4",
"Dependencies": [
]
}

View File

@ -79,6 +79,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Vocalization", "Vocalizatio
{7B1E9A54-ED9E-47AA-BBAA-98A6E7CB527A} = {7B1E9A54-ED9E-47AA-BBAA-98A6E7CB527A}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AUnifiedSaveCore", "UnifiedSaveCore\AUnifiedSaveCore.csproj", "{ACAF0BAE-6495-4F1B-8B1F-E34BF7CCF51A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -401,6 +403,18 @@ Global
{1651701C-DB36-43C7-B66D-2700171DD9A9}.x86|Any CPU.Build.0 = Release|Any CPU
{1651701C-DB36-43C7-B66D-2700171DD9A9}.x86|x86.ActiveCfg = Release|Any CPU
{1651701C-DB36-43C7-B66D-2700171DD9A9}.x86|x86.Build.0 = Release|Any CPU
{ACAF0BAE-6495-4F1B-8B1F-E34BF7CCF51A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ACAF0BAE-6495-4F1B-8B1F-E34BF7CCF51A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ACAF0BAE-6495-4F1B-8B1F-E34BF7CCF51A}.Debug|x86.ActiveCfg = Debug|Any CPU
{ACAF0BAE-6495-4F1B-8B1F-E34BF7CCF51A}.Debug|x86.Build.0 = Debug|Any CPU
{ACAF0BAE-6495-4F1B-8B1F-E34BF7CCF51A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ACAF0BAE-6495-4F1B-8B1F-E34BF7CCF51A}.Release|Any CPU.Build.0 = Release|Any CPU
{ACAF0BAE-6495-4F1B-8B1F-E34BF7CCF51A}.Release|x86.ActiveCfg = Release|Any CPU
{ACAF0BAE-6495-4F1B-8B1F-E34BF7CCF51A}.Release|x86.Build.0 = Release|Any CPU
{ACAF0BAE-6495-4F1B-8B1F-E34BF7CCF51A}.x86|Any CPU.ActiveCfg = Release|Any CPU
{ACAF0BAE-6495-4F1B-8B1F-E34BF7CCF51A}.x86|Any CPU.Build.0 = Release|Any CPU
{ACAF0BAE-6495-4F1B-8B1F-E34BF7CCF51A}.x86|x86.ActiveCfg = Release|Any CPU
{ACAF0BAE-6495-4F1B-8B1F-E34BF7CCF51A}.x86|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{ACAF0BAE-6495-4F1B-8B1F-E34BF7CCF51A}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>UnifiedSaveCore</RootNamespace>
<AssemblyName>UnifiedSaveCore</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Framework\IInformationHandler.cs" />
<Compile Include="Framework\LocationHandler.cs" />
<Compile Include="ISaveCoreAPI.cs" />
<Compile Include="ModCore.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SaveCoreAPI.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="manifest.json" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Analyzer Include="..\packages\Pathoschild.Stardew.ModBuildConfig.2.1.0\analyzers\dotnet\cs\StardewModdingAPI.ModBuildConfig.Analyzer.dll" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\Pathoschild.Stardew.ModBuildConfig.2.1.0\build\Pathoschild.Stardew.ModBuildConfig.targets" Condition="Exists('..\packages\Pathoschild.Stardew.ModBuildConfig.2.1.0\build\Pathoschild.Stardew.ModBuildConfig.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Pathoschild.Stardew.ModBuildConfig.2.1.0\build\Pathoschild.Stardew.ModBuildConfig.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Pathoschild.Stardew.ModBuildConfig.2.1.0\build\Pathoschild.Stardew.ModBuildConfig.targets'))" />
</Target>
</Project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2"/>
</startup>
</configuration>

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnifiedSaveCore.Framework
{
public interface IInformationHandler
{
void beforeSave();
void afterSave();
void afterLoad();
}
}

View File

@ -0,0 +1,76 @@
using Microsoft.Xna.Framework;
using StardewValley;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnifiedSaveCore.Framework
{
public class LocationHandler:IInformationHandler
{
/// <summary>
/// The locations to store while saving is occuring.
/// </summary>
public List<GameLocation> locations;
public GameLocation oldLocation;
public Vector2 position;
public int oldFacingDirection;
public LocationHandler()
{
this.locations = new List<GameLocation>();
}
/// <summary>
/// Restores all game locations once the game is finished loading.
/// </summary>
public void afterSave()
{
foreach (var loc in locations)
{
Game1.locations.Add(loc);
}
locations.Clear();
Game1.warpFarmer(oldLocation.name, (int)position.X*Game1.tileSize, (int)position.Y*Game1.tileSize, oldFacingDirection);
}
//Removes all game locations for the game to save.
public void beforeSave()
{
UnifiedSaveCore.monitor.Log("BEFORE SAVE HAS BEEN CALLED!");
oldLocation = Game1.player.currentLocation;
position = Game1.player.position;
oldFacingDirection = Game1.player.facingDirection;
Vector2 bed = Game1.player.mostRecentBed;
Game1.warpFarmer("Farmhouse", (int)bed.X, (int)bed.Y, 2);
foreach (var loc in Game1.locations)
{
UnifiedSaveCore.monitor.Log(loc.GetType().ToString());
//ModCore.monitor.Log();
foreach (var type in UnifiedSaveCore.modTypes)
{
if (loc.GetType().ToString() == type.ToString())
{
UnifiedSaveCore.monitor.Log("Temporarily removing unexpected location type: " + loc.GetType().ToString());
locations.Add(loc);
}
}
}
foreach (var v in locations)
{
Game1.locations.Remove(v);
}
}
public void afterLoad()
{
//do nothing
}
}
}

View File

@ -1,13 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Omegasis.SaveAnywhere.API
namespace UnifiedSaveCore
{
/// <summary>
/// Interface for the Save Anywhere API
/// Other mods can use this interface to get the
/// API from the SMAPI helper
/// </summary>
public interface ISaveAnywhereAPI
public interface ISaveCoreAPI
{
/*********
** Events

View File

@ -0,0 +1,164 @@
using StardewModdingAPI;
using StardewValley;
using StardewValley.Characters;
using StardewValley.Monsters;
using StardewValley.Quests;
using StardewValley.TerrainFeatures;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
using UnifiedSaveCore.Framework;
namespace UnifiedSaveCore
{
/// <summary>
/// Bare bones mod that interfaces events for saving.
/// </summary>
public class UnifiedSaveCore:Mod
{
//https://stackoverflow.com/questions/14663763/how-to-add-an-attribute-to-a-property-at-runtime
public static List<Type> modTypes;
public static List<IInformationHandler> dataHandlers;
public static SaveCoreAPI saveCoreAPI;
public static IMonitor monitor;
public static IModHelper helper;
public override void Entry(IModHelper helper)
{
monitor = this.Monitor;
helper = this.Helper;
StackFrame[] frames = new StackTrace().GetFrames();
Assembly initialAssembly = (from f in frames
select f.GetMethod().ReflectedType.Assembly
).Distinct().ElementAt(1);
Monitor.Log(initialAssembly.FullName);
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
modTypes = new List<Type>();
List<string> namespacesToIgnore = new List<string>()
{
"System",
"Mono",
"StardewModdingAPI",
"Lidgren",
"Microsoft",
"Monogames",
"Monogame",
"MonoGame",
"Netcode",
"Steamworks",
"GalaxyCSharp",
"xTile",
"StardewModdingAPI"
};
foreach (Assembly asm in assemblies)
{
bool ignoreNamespace = false;
AssemblyName name2= asm.GetName();
foreach (var t in asm.GetTypes())
{
string[] nameSpace = t.ToString().Split('.');
foreach (var name in namespacesToIgnore)
{
if (name.ToString() == nameSpace.ElementAt(0).ToString() || name.ToString()==name2.Name.ToString())
{
ignoreNamespace = true;
break;
}
}
if (ignoreNamespace) break;
var type = t;
if(t.IsInterface)
{
Monitor.Log(t.ToString()+" is an interface. Skipping XML Serialization ignore.");
continue;
}
var aName = name2;
var ab = AppDomain.CurrentDomain.DefineDynamicAssembly(aName, AssemblyBuilderAccess.Run);
var mb = ab.DefineDynamicModule(aName.Name);
var tb = mb.DefineType(type.Name + "Proxy", System.Reflection.TypeAttributes.Public| TypeAttributes.NotPublic, type);
var attrCtorParams = new Type[] {};
var attrCtorInfo = typeof(System.Xml.Serialization.XmlIgnoreAttribute).GetConstructor(attrCtorParams);
var attrBuilder = new CustomAttributeBuilder(attrCtorInfo, new object[] { });
tb.SetCustomAttribute(attrBuilder);
Monitor.Log("XML Serialization Ignore: "+type.ToString());
modTypes.Add(t);
}
if (ignoreNamespace) continue;
Monitor.Log(asm.FullName);
//Monitor.Log(name2.Name.ToString());
//FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(asm.Location);
//AssemblyName asmName = asm.GetName();
//string name = asmName.Name;
//Version asmV = asmName.Version;
//string fileV = fvi.FileVersion;
//string prodV = fvi.ProductVersion;
//Console.WriteLine("{0} VERSIONS: (A){1} (F){2} (P){3}", name, asmV, fileV, prodV);
}
StardewModdingAPI.Events.SaveEvents.BeforeSave += SaveEvents_BeforeSave;
StardewModdingAPI.Events.SaveEvents.AfterSave += SaveEvents_AfterSave;
StardewModdingAPI.Events.SaveEvents.AfterLoad += SaveEvents_AfterLoad;
saveCoreAPI = new SaveCoreAPI();
dataHandlers = new List<IInformationHandler>();
dataHandlers.Add(new LocationHandler());
}
public static void SaveEvents_AfterLoad(object sender, EventArgs e)
{
saveCoreAPI.invoke_AfterLoad(sender, e); //give priority to mod authors first then use brute force methods.
foreach (IInformationHandler handler in dataHandlers)
{
handler.afterLoad();
}
}
public static void SaveEvents_AfterSave(object sender, EventArgs e)
{
saveCoreAPI.invoke_AfterSave(sender, e);//give priority to mod authors first then use brute force methods.
foreach (IInformationHandler handler in dataHandlers)
{
handler.afterSave();
}
}
public static void SaveEvents_BeforeSave(object sender, EventArgs e)
{
saveCoreAPI.invoke_BeforeSave(sender, e);//give priority to mod authors first then use brute force methods.
foreach (IInformationHandler handler in dataHandlers)
{
handler.beforeSave();
}
}
public override object GetApi()
{
return new SaveCoreAPI();
}
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("UnifiedSaveCore")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("UnifiedSaveCore")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("acaf0bae-6495-4f1b-8b1f-e34bf7ccf51a")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnifiedSaveCore
{
public class SaveCoreAPI
{
public event EventHandler BeforeSave;
public event EventHandler AfterSave;
public event EventHandler AfterLoad;
public SaveCoreAPI()
{
BeforeSave = new EventHandler(empty);
AfterSave = new EventHandler(empty);
AfterLoad = new EventHandler(empty);
}
/// <summary>
/// Used to initialize empty event handlers.
/// </summary>
/// <param name="o"></param>
/// <param name="args"></param>
private void empty(object o, EventArgs args)
{
}
public void invoke_BeforeSave(object sender, EventArgs args)
{
if(BeforeSave!=null)
BeforeSave.Invoke(sender, args);
}
public void invoke_AfterSave(object sender, EventArgs args)
{
if(AfterSave!=null)
AfterSave.Invoke(sender, args);
}
public void invoke_AfterLoad(object sender, EventArgs args)
{
if(AfterLoad!=null)
AfterLoad.Invoke(sender, args);
}
}
}

View File

@ -0,0 +1,10 @@
{
"Name": "Unified Save core",
"Author": "Alpha_Omegasis",
"Version": "0.0.1",
"Description": "Allows for a core api for mod authors to handle saving/loading.",
"UniqueID": "Omegasis.UnifiedSaveCore",
"EntryDll": "UnifiedSaveCore.dll",
"MinimumApiVersion": "2.0",
"UpdateKeys": [ "" ]
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Pathoschild.Stardew.ModBuildConfig" version="2.1.0" targetFramework="net461" />
</packages>