Removed own memberwise cast function, and replaced with one by jpmikkers. This needs converting to a recursion-less version and having the loop issue I think it has fixed
This commit is contained in:
parent
1ec0570ed9
commit
65f1e5bec4
|
@ -6,7 +6,8 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace StardewModdingAPI.API
|
||||
{
|
||||
public class Game
|
||||
class Game
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,8 @@ namespace StardewModdingAPI.Events
|
|||
{
|
||||
try
|
||||
{
|
||||
Program.IsGameReferenceDirty = true;
|
||||
var test = Program.gamePtr;
|
||||
UpdateTick.Invoke(null, EventArgs.Empty);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StardewModdingAPI.ExtensionMethods
|
||||
{
|
||||
public static class ArrayExtensions
|
||||
{
|
||||
public static void ForEach(this Array array, Action<Array, int[]> action)
|
||||
{
|
||||
if (array.LongLength == 0) return;
|
||||
ArrayTraverse walker = new ArrayTraverse(array);
|
||||
do action(array, walker.Position);
|
||||
while (walker.Step());
|
||||
}
|
||||
|
||||
//public static void ForEach(Array array, Action<Array, int[]> action)
|
||||
//{
|
||||
// if (array.LongLength == 0) return;
|
||||
// ArrayTraverse walker = new ArrayTraverse(array);
|
||||
// do action(array, walker.Position);
|
||||
// while (walker.Step());
|
||||
//}
|
||||
}
|
||||
|
||||
internal class ArrayTraverse
|
||||
{
|
||||
public int[] Position;
|
||||
private int[] maxLengths;
|
||||
|
||||
public ArrayTraverse(Array array)
|
||||
{
|
||||
maxLengths = new int[array.Rank];
|
||||
for (int i = 0; i < array.Rank; ++i)
|
||||
{
|
||||
maxLengths[i] = array.GetLength(i) - 1;
|
||||
}
|
||||
Position = new int[array.Rank];
|
||||
}
|
||||
|
||||
public bool Step()
|
||||
{
|
||||
for (int i = 0; i < Position.Length; ++i)
|
||||
{
|
||||
if (Position[i] < maxLengths[i])
|
||||
{
|
||||
Position[i]++;
|
||||
for (int j = 0; j < i; j++)
|
||||
{
|
||||
Position[j] = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace StardewModdingAPI.ExtensionMethods
|
||||
{
|
||||
//From https://github.com/jpmikkers/net-object-deep-copy/blob/master/ObjectExtensions.cs
|
||||
public static class ObjectExtensions
|
||||
{
|
||||
public static T Copy<T>(this object original)
|
||||
{
|
||||
return (T)new DeepCopyContext().InternalCopy(original, true);
|
||||
}
|
||||
|
||||
private class DeepCopyContext
|
||||
{
|
||||
private static readonly Func<object, object> CloneMethod;
|
||||
private readonly Dictionary<Object, Object> m_Visited;
|
||||
private readonly Dictionary<Type, FieldInfo[]> m_NonShallowFieldCache;
|
||||
|
||||
static DeepCopyContext()
|
||||
{
|
||||
MethodInfo cloneMethod = typeof(Object).GetMethod("MemberwiseClone", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
var p1 = Expression.Parameter(typeof(object));
|
||||
var body = Expression.Call(p1, cloneMethod);
|
||||
CloneMethod = Expression.Lambda<Func<object, object>>(body, p1).Compile();
|
||||
//Console.WriteLine("typeof(object) contains {0} nonshallow fields", NonShallowFields(typeof(object)).Count());
|
||||
}
|
||||
|
||||
public DeepCopyContext()
|
||||
{
|
||||
m_Visited = new Dictionary<object, object>(new ReferenceEqualityComparer());
|
||||
m_NonShallowFieldCache = new Dictionary<Type, FieldInfo[]>();
|
||||
}
|
||||
|
||||
private static bool IsPrimitive(Type type)
|
||||
{
|
||||
if (type.IsValueType && type.IsPrimitive) return true;
|
||||
if (type == typeof(String)) return true;
|
||||
if (type == typeof(Decimal)) return true;
|
||||
if (type == typeof(DateTime)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public Object InternalCopy(Object originalObject, bool includeInObjectGraph)
|
||||
{
|
||||
if (originalObject == null) return null;
|
||||
var typeToReflect = originalObject.GetType();
|
||||
if (IsPrimitive(typeToReflect)) return originalObject;
|
||||
|
||||
if (typeof(XElement).IsAssignableFrom(typeToReflect)) return new XElement(originalObject as XElement);
|
||||
if (typeof(Delegate).IsAssignableFrom(typeToReflect)) return null;
|
||||
|
||||
if (includeInObjectGraph)
|
||||
{
|
||||
object result;
|
||||
if (m_Visited.TryGetValue(originalObject, out result)) return result;
|
||||
}
|
||||
|
||||
var cloneObject = CloneMethod(originalObject);
|
||||
|
||||
if (includeInObjectGraph)
|
||||
{
|
||||
m_Visited.Add(originalObject, cloneObject);
|
||||
}
|
||||
|
||||
if (typeToReflect.IsArray)
|
||||
{
|
||||
var arrayElementType = typeToReflect.GetElementType();
|
||||
|
||||
if (IsPrimitive(arrayElementType))
|
||||
{
|
||||
// for an array of primitives, do nothing. The shallow clone is enough.
|
||||
}
|
||||
else if (arrayElementType.IsValueType)
|
||||
{
|
||||
// if its an array of structs, there's no need to check and add the individual elements to 'visited', because in .NET it's impossible to create
|
||||
// references to individual array elements.
|
||||
Array clonedArray = (Array)cloneObject;
|
||||
clonedArray.ForEach((array, indices) => array.SetValue(InternalCopy(clonedArray.GetValue(indices), false), indices));
|
||||
}
|
||||
else
|
||||
{
|
||||
// it's an array of ref types
|
||||
Array clonedArray = (Array)cloneObject;
|
||||
clonedArray.ForEach((array, indices) => array.SetValue(InternalCopy(clonedArray.GetValue(indices), true), indices));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var fieldInfo in CachedNonShallowFields(typeToReflect))
|
||||
{
|
||||
var originalFieldValue = fieldInfo.GetValue(originalObject);
|
||||
// a valuetype field can never have a reference pointing to it, so don't check the object graph in that case
|
||||
|
||||
Log.Error("Replace this with a recurse-less version");
|
||||
var clonedFieldValue = InternalCopy(originalFieldValue, !fieldInfo.FieldType.IsValueType);
|
||||
fieldInfo.SetValue(cloneObject, clonedFieldValue);
|
||||
}
|
||||
}
|
||||
|
||||
return cloneObject;
|
||||
}
|
||||
|
||||
private FieldInfo[] CachedNonShallowFields(Type typeToReflect)
|
||||
{
|
||||
FieldInfo[] result;
|
||||
|
||||
if (!m_NonShallowFieldCache.TryGetValue(typeToReflect, out result))
|
||||
{
|
||||
result = NonShallowFields(typeToReflect).ToArray();
|
||||
m_NonShallowFieldCache[typeToReflect] = result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From the given type hierarchy (i.e. including all base types), return all fields that should be deep-copied
|
||||
/// </summary>
|
||||
/// <param name="typeToReflect"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<FieldInfo> NonShallowFields(Type typeToReflect)
|
||||
{
|
||||
while (typeToReflect != typeof(object))
|
||||
{
|
||||
foreach (var fieldInfo in typeToReflect.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly))
|
||||
{
|
||||
if (IsPrimitive(fieldInfo.FieldType)) continue; // this is 5% faster than a where clause..
|
||||
yield return fieldInfo;
|
||||
}
|
||||
typeToReflect = typeToReflect.BaseType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ReferenceEqualityComparer : EqualityComparer<Object>
|
||||
{
|
||||
public override bool Equals(object x, object y)
|
||||
{
|
||||
return ReferenceEquals(x, y);
|
||||
}
|
||||
|
||||
public override int GetHashCode(object obj)
|
||||
{
|
||||
if (obj == null) return 0;
|
||||
// The RuntimeHelpers.GetHashCode method always calls the Object.GetHashCode method non-virtually,
|
||||
// even if the object's type has overridden the Object.GetHashCode method.
|
||||
return RuntimeHelpers.GetHashCode(obj);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -17,6 +17,13 @@ namespace StardewModdingAPI.Helpers
|
|||
private static void InjectMethod(ILProcessor ilProcessor, Instruction target, MethodReference method)
|
||||
{
|
||||
Instruction callEnterInstruction = ilProcessor.Create(OpCodes.Call, method);
|
||||
|
||||
if(method.HasThis)
|
||||
{
|
||||
Instruction loadObjInstruction = ilProcessor.Create(OpCodes.Ldarg_0);
|
||||
ilProcessor.InsertBefore(target, loadObjInstruction);
|
||||
}
|
||||
|
||||
if (method.HasParameters)
|
||||
{
|
||||
Instruction loadObjInstruction = ilProcessor.Create(OpCodes.Ldarg_0);
|
||||
|
|
|
@ -9,140 +9,5 @@ namespace StardewModdingAPI.Helpers
|
|||
{
|
||||
public static class ReflectionHelper
|
||||
{
|
||||
private class FieldCompatability
|
||||
{
|
||||
public List<Tuple<FieldInfo, FieldInfo>> MatchingFields = new List<Tuple<FieldInfo, FieldInfo>>();
|
||||
public List<Tuple<FieldInfo, FieldInfo>> FixableMismatchingFields = new List<Tuple<FieldInfo, FieldInfo>>();
|
||||
public List<Tuple<FieldInfo, FieldInfo>> UnfixableMismatchingFields = new List<Tuple<FieldInfo, FieldInfo>>();
|
||||
public List<FieldInfo> FieldsMissingFromA = new List<FieldInfo>();
|
||||
public List<FieldInfo> FieldsMissingFromB = new List<FieldInfo>();
|
||||
}
|
||||
|
||||
private static FieldCompatability AssessFieldCompatbility(FieldInfo[] a, FieldInfo[] b)
|
||||
{
|
||||
FieldCompatability results = new FieldCompatability();
|
||||
List<FieldInfo> editableB = new List<FieldInfo>(b);
|
||||
|
||||
foreach (var leftField in a)
|
||||
{
|
||||
FieldInfo matchingField = null;
|
||||
foreach(var rightField in editableB)
|
||||
{
|
||||
if(leftField.Name == rightField.Name)
|
||||
{
|
||||
matchingField = rightField;
|
||||
if (leftField.FieldType == rightField.FieldType)
|
||||
{
|
||||
results.MatchingFields.Add(Tuple.Create(leftField, rightField));
|
||||
}
|
||||
else if(leftField.FieldType.FullName == rightField.FieldType.FullName)
|
||||
{
|
||||
results.FixableMismatchingFields.Add(Tuple.Create(leftField, rightField));
|
||||
}
|
||||
else
|
||||
{
|
||||
results.UnfixableMismatchingFields.Add(Tuple.Create(leftField, rightField));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (matchingField != null)
|
||||
{
|
||||
editableB.Remove(matchingField);
|
||||
}
|
||||
else
|
||||
{
|
||||
results.FieldsMissingFromB.Add(leftField);
|
||||
}
|
||||
}
|
||||
|
||||
results.FieldsMissingFromA.AddRange(editableB);
|
||||
return results;
|
||||
}
|
||||
|
||||
private static void WarnMismatch(FieldCompatability compatibility, string source)
|
||||
{
|
||||
if (compatibility.UnfixableMismatchingFields.Any())
|
||||
{
|
||||
Log.Warning("Unfixable type mismatch in {0}. Mods which depend on these types may fail to work properly", source);
|
||||
foreach (var mismatch in compatibility.UnfixableMismatchingFields)
|
||||
{
|
||||
Log.Warning("- {0} is not of type {1}, is of type {2}", mismatch.Item1.Name, mismatch.Item1.FieldType.FullName, mismatch.Item2.FieldType.FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void WarnMissing(FieldCompatability compatibility, string source)
|
||||
{
|
||||
if (compatibility.FieldsMissingFromA.Any() || compatibility.FieldsMissingFromB.Any())
|
||||
{
|
||||
Log.Warning("The following fields are not present in both objects", source);
|
||||
|
||||
foreach (var mismatch in compatibility.FieldsMissingFromA)
|
||||
Log.Warning("- Left Object Missing {0} ({1})", mismatch.Name, mismatch.FieldType.FullName);
|
||||
foreach (var mismatch in compatibility.FieldsMissingFromA)
|
||||
Log.Warning("- Right Object Missing {0} ({1})", mismatch.Name, mismatch.FieldType.FullName);
|
||||
}
|
||||
}
|
||||
|
||||
private static void RecursiveMemberwiseCast(Type toType, Type fromType, object to, object from)
|
||||
{
|
||||
Log.Verbose("Writing {0}", toType.Name);
|
||||
|
||||
Stack<Tuple<Type, Type, object, object>> pendingCasts = new Stack<Tuple<Type, Type, object, object>>();
|
||||
List<object> alreadyProcessed = new List<object>();
|
||||
|
||||
pendingCasts.Push(Tuple.Create(toType, fromType,
|
||||
to, from));
|
||||
|
||||
while (pendingCasts.Count > 0)
|
||||
{
|
||||
Tuple<Type, Type, object, object> current = pendingCasts.Pop();
|
||||
|
||||
Type castTo = current.Item1;
|
||||
Type castFrom = current.Item2;
|
||||
object objectToSet = current.Item3;
|
||||
object baseObject = current.Item4;
|
||||
|
||||
var targetFields = castTo
|
||||
.GetFields().Where(n => !n.IsInitOnly && !n.IsLiteral).ToArray();
|
||||
var baseFields = castFrom
|
||||
.GetFields().Where(n => !n.IsInitOnly && !n.IsLiteral).ToArray();
|
||||
|
||||
var compatibility = AssessFieldCompatbility(targetFields, baseFields);
|
||||
|
||||
WarnMissing(compatibility, from.GetType().FullName);
|
||||
WarnMismatch(compatibility, from.GetType().FullName);
|
||||
|
||||
foreach (var match in compatibility.MatchingFields)
|
||||
{
|
||||
var toValue = match.Item1.GetValue(objectToSet);
|
||||
var fromValue = match.Item2.GetValue(baseObject);
|
||||
|
||||
match.Item2.SetValue(objectToSet, fromValue);
|
||||
}
|
||||
|
||||
foreach (var fixableMismatch in compatibility.FixableMismatchingFields)
|
||||
{
|
||||
var toValue = fixableMismatch.Item1.GetValue(objectToSet);
|
||||
var fromValue = fixableMismatch.Item2.GetValue(baseObject);
|
||||
|
||||
if (fromValue != null && !alreadyProcessed.Any(n => Object.ReferenceEquals(n, fromValue)))
|
||||
{
|
||||
alreadyProcessed.Add(fromValue);
|
||||
//pendingCasts.Push(Tuple.Create(fixableMismatch.Item1.FieldType, fixableMismatch.Item2.FieldType,
|
||||
// toValue, fromValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static T MemberwiseCast<T>(this object @base) where T : new()
|
||||
{
|
||||
T retObj = new T();
|
||||
RecursiveMemberwiseCast(@base.GetType(), retObj.GetType().BaseType, retObj, @base);
|
||||
return retObj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StardewModdingAPI.Helpers
|
||||
{
|
||||
public static class StardewAssembly
|
||||
{
|
||||
private static Assembly ModifiedGameAssembly { get; set; }
|
||||
private static CecilContext StardewContext { get; set; }
|
||||
private static CecilContext SmapiContext { get; set; }
|
||||
|
||||
public static void ModifyStardewAssembly()
|
||||
{
|
||||
StardewContext = new CecilContext(CecilContextType.Stardew);
|
||||
SmapiContext = new CecilContext(CecilContextType.SMAPI);
|
||||
|
||||
CecilHelper.InjectEntryMethod(StardewContext, SmapiContext, "StardewValley.Game1", ".ctor", "StardewModdingAPI.Program", "Test");
|
||||
CecilHelper.InjectExitMethod(StardewContext, SmapiContext, "StardewValley.Game1", "Initialize", "StardewModdingAPI.Events.GameEvents", "InvokeInitialize");
|
||||
CecilHelper.InjectExitMethod(StardewContext, SmapiContext, "StardewValley.Game1", "LoadContent", "StardewModdingAPI.Events.GameEvents", "InvokeLoadContent");
|
||||
CecilHelper.InjectExitMethod(StardewContext, SmapiContext, "StardewValley.Game1", "Update", "StardewModdingAPI.Events.GameEvents", "InvokeUpdateTick");
|
||||
CecilHelper.InjectExitMethod(StardewContext, SmapiContext, "StardewValley.Game1", "Draw", "StardewModdingAPI.Events.GraphicsEvents", "InvokeDrawTick");
|
||||
}
|
||||
|
||||
public static void LoadStardewAssembly()
|
||||
{
|
||||
ModifiedGameAssembly = Assembly.Load(StardewContext.ModifiedAssembly.GetBuffer());
|
||||
//ModifiedGameAssembly = Assembly.UnsafeLoadFrom(Constants.ExecutionPath + "\\Stardew Valley.exe");
|
||||
}
|
||||
|
||||
internal static void Launch()
|
||||
{
|
||||
ModifiedGameAssembly.EntryPoint.Invoke(null, new object[] { new string[] { } });
|
||||
}
|
||||
|
||||
internal static void WriteModifiedExe()
|
||||
{
|
||||
StardewContext.WriteAssembly("StardewValley-Modified.exe");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
HiDef
|
|
@ -3,6 +3,7 @@ using Microsoft.Xna.Framework.Graphics;
|
|||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using StardewModdingAPI.Events;
|
||||
using StardewModdingAPI.ExtensionMethods;
|
||||
using StardewModdingAPI.Helpers;
|
||||
using StardewModdingAPI.Inheritance;
|
||||
using StardewModdingAPI.Inheritance.Menus;
|
||||
|
@ -26,27 +27,31 @@ namespace StardewModdingAPI
|
|||
|
||||
public static Texture2D DebugPixel { get; private set; }
|
||||
|
||||
private static object modifiedAssemblyGame;
|
||||
//public static Game1 gamePtr;
|
||||
public static bool IsGameReferenceDirty { get; set; }
|
||||
|
||||
public static object gameInst;
|
||||
|
||||
public static Game1 _gamePtr;
|
||||
public static Game1 gamePtr
|
||||
{
|
||||
get
|
||||
{
|
||||
return modifiedAssemblyGame.MemberwiseCast<Game1>();
|
||||
if(IsGameReferenceDirty && gameInst != null)
|
||||
{
|
||||
_gamePtr = gameInst.Copy<Game1>();
|
||||
}
|
||||
return _gamePtr;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ready;
|
||||
|
||||
public static Assembly StardewAssembly;
|
||||
public static Type StardewProgramType;
|
||||
public static FieldInfo StardewGameInfo;
|
||||
public static Form StardewForm;
|
||||
|
||||
public static Thread gameThread;
|
||||
public static Thread consoleInputThread;
|
||||
|
||||
public static CecilContext StardewContext;
|
||||
public static CecilContext SmapiContext;
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -79,25 +84,18 @@ namespace StardewModdingAPI
|
|||
/// </summary>
|
||||
private static void ConfigureMethodInjection()
|
||||
{
|
||||
StardewContext = new CecilContext(CecilContextType.Stardew);
|
||||
SmapiContext = new CecilContext(CecilContextType.SMAPI);
|
||||
|
||||
//StardewContext.ReplaceMethodInstruction(OpCodes.Newobj, "System.Void StardewValley.Game1::.ctor()");
|
||||
//CecilHelper.RedirectConstructor(StardewContext, SmapiContext, "StardewValley.Program", "Main",
|
||||
// "StardewValley.Game1", ".ctor", "StardewModdingAPI.Inheritance.SGame", ".ctor");
|
||||
CecilHelper.InjectExitMethod(StardewContext, SmapiContext, "StardewValley.Game1", ".ctor", "StardewModdingAPI.Program", "Test");
|
||||
CecilHelper.InjectExitMethod(StardewContext, SmapiContext, "StardewValley.Game1", "Initialize", "StardewModdingAPI.Events.GameEvents", "InvokeInitialize");
|
||||
CecilHelper.InjectExitMethod(StardewContext, SmapiContext, "StardewValley.Game1", "LoadContent", "StardewModdingAPI.Events.GameEvents", "InvokeLoadContent");
|
||||
CecilHelper.InjectExitMethod(StardewContext, SmapiContext, "StardewValley.Game1", "Update", "StardewModdingAPI.Events.GameEvents", "InvokeUpdateTick");
|
||||
CecilHelper.InjectExitMethod(StardewContext, SmapiContext, "StardewValley.Game1", "Draw", "StardewModdingAPI.Events.GraphicsEvents", "InvokeDrawTick");
|
||||
//TODO - Invoke Resize
|
||||
|
||||
StardewAssembly.ModifyStardewAssembly();
|
||||
|
||||
#if DEBUG
|
||||
StardewAssembly.WriteModifiedExe();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public static void Test(object instance)
|
||||
{
|
||||
modifiedAssemblyGame = instance;
|
||||
gameInst = instance;
|
||||
IsGameReferenceDirty = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -138,7 +136,8 @@ namespace StardewModdingAPI
|
|||
|
||||
if (!File.Exists(Constants.ExecutionPath + "\\Stardew Valley.exe"))
|
||||
{
|
||||
throw new FileNotFoundException(string.Format("Could not found: {0}\\Stardew Valley.exe", Constants.ExecutionPath));
|
||||
StardewModdingAPI.Log.Error("Replace this");
|
||||
//throw new FileNotFoundException(string.Format("Could not found: {0}\\Stardew Valley.exe", Constants.ExecutionPath));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,44 +149,26 @@ namespace StardewModdingAPI
|
|||
StardewModdingAPI.Log.Info("Initializing SDV Assembly...");
|
||||
|
||||
// Load in the assembly - ignores security
|
||||
StardewAssembly = Assembly.Load(StardewContext.ModifiedAssembly.GetBuffer());
|
||||
StardewProgramType = StardewAssembly.GetType("StardewValley.Program", true);
|
||||
StardewGameInfo = StardewProgramType.GetField("gamePtr");
|
||||
StardewAssembly.LoadStardewAssembly();
|
||||
StardewModdingAPI.Log.Comment("SDV Loaded Into Memory");
|
||||
|
||||
// Change the game's version
|
||||
StardewModdingAPI.Log.Verbose("Injecting New SDV Version...");
|
||||
Game1.version += string.Format("-Z_MODDED | SMAPI {0}", Constants.VersionString);
|
||||
|
||||
// Create the thread for the game to run in.
|
||||
gameThread = new Thread(RunGame);
|
||||
StardewModdingAPI.Log.Info("Starting SDV...");
|
||||
gameThread.Start();
|
||||
Application.ThreadException += StardewModdingAPI.Log.Application_ThreadException;
|
||||
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
|
||||
AppDomain.CurrentDomain.UnhandledException += StardewModdingAPI.Log.CurrentDomain_UnhandledException;
|
||||
|
||||
// Wait for the game to load up
|
||||
while (!ready) ;
|
||||
|
||||
//SDV is running
|
||||
StardewModdingAPI.Log.Comment("SDV Loaded Into Memory");
|
||||
|
||||
//Create definition to listen for input
|
||||
StardewModdingAPI.Log.Verbose("Initializing Console Input Thread...");
|
||||
consoleInputThread = new Thread(ConsoleInputThread);
|
||||
|
||||
// The only command in the API (at least it should be, for now)
|
||||
Command.RegisterCommand("help", "Lists all commands | 'help <cmd>' returns command description").CommandFired += help_CommandFired;
|
||||
//Command.RegisterCommand("crash", "crashes sdv").CommandFired += delegate { Game1.player.draw(null); };
|
||||
|
||||
StardewModdingAPI.Log.Verbose("Applying Final SDV Tweaks...");
|
||||
|
||||
StardewAssembly.EntryPoint.Invoke(null, new object[] { new string[] { } });
|
||||
//StardewInvoke(() =>
|
||||
//{
|
||||
// gamePtr.IsMouseVisible = false;
|
||||
// gamePtr.Window.Title = "Stardew Valley - Version " + Game1.version;
|
||||
// StardewForm.Resize += Events.GraphicsEvents.InvokeResize;
|
||||
//});
|
||||
|
||||
//var test = (Game1)StardewGameInfo.GetValue(StardewProgramType);
|
||||
StardewAssembly.Launch();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -241,21 +222,6 @@ namespace StardewModdingAPI
|
|||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static void RunGame()
|
||||
{
|
||||
Application.ThreadException += StardewModdingAPI.Log.Application_ThreadException;
|
||||
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
|
||||
AppDomain.CurrentDomain.UnhandledException += StardewModdingAPI.Log.CurrentDomain_UnhandledException;
|
||||
|
||||
try
|
||||
{
|
||||
ready = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
StardewModdingAPI.Log.Error("Game failed to start: " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
static void StardewForm_Closing(object sender, CancelEventArgs e)
|
||||
{
|
||||
|
|
|
@ -73,8 +73,7 @@
|
|||
<PlatformTarget>x86</PlatformTarget>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
<DefineConstants>
|
||||
</DefineConstants>
|
||||
<DefineConstants>DEBUG</DefineConstants>
|
||||
<UseVSHostingProcess>true</UseVSHostingProcess>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
|
@ -106,7 +105,7 @@
|
|||
<HintPath>..\packages\Mono.Cecil.0.9.6.1\lib\net45\Mono.Cecil.Rocks.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Stardew Valley, Version=1.0.5905.5747, Culture=neutral, processorArchitecture=x86">
|
||||
<Reference Include="Stardew Valley, Version=1.0.5912.41135, Culture=neutral, processorArchitecture=x86">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\..\Games\SteamLibrary\steamapps\common\Stardew Valley\Stardew Valley.exe</HintPath>
|
||||
</Reference>
|
||||
|
@ -119,8 +118,7 @@
|
|||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="xTile, Version=2.0.4.0, Culture=neutral, processorArchitecture=x86">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<Reference Include="xTile">
|
||||
<HintPath>..\..\..\..\Games\SteamLibrary\steamapps\common\Stardew Valley\xTile.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
@ -143,10 +141,13 @@
|
|||
<Compile Include="Events\Mine.cs" />
|
||||
<Compile Include="Events\Player.cs" />
|
||||
<Compile Include="Events\Time.cs" />
|
||||
<Compile Include="ExtensionMethods\Array.cs" />
|
||||
<Compile Include="ExtensionMethods\Object.cs" />
|
||||
<Compile Include="Extensions.cs" />
|
||||
<Compile Include="Helpers\CecilContext.cs" />
|
||||
<Compile Include="Helpers\CecilHelper.cs" />
|
||||
<Compile Include="Helpers\ReflectionHelper.cs" />
|
||||
<Compile Include="Helpers\StardewAssembly.cs" />
|
||||
<Compile Include="Inheritance\ItemStackChange.cs" />
|
||||
<Compile Include="Inheritance\Menus\SBobberBar.cs" />
|
||||
<Compile Include="Inheritance\Menus\SGameMenu.cs" />
|
||||
|
@ -163,9 +164,6 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<Content Include="Manifest Resources\Microsoft.Xna.Framework.RuntimeProfile.txt">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
|
Loading…
Reference in New Issue