Hooked up a few callbacks. Need to work on backwards compatability next
This commit is contained in:
parent
e1bfd54894
commit
1ec0570ed9
|
@ -0,0 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace StardewModdingAPI.API
|
||||
{
|
||||
public class Game
|
||||
{
|
||||
}
|
||||
}
|
|
@ -154,5 +154,10 @@ namespace StardewModdingAPI.Helpers
|
|||
}
|
||||
return reference;
|
||||
}
|
||||
|
||||
internal void WriteAssembly(string file)
|
||||
{
|
||||
_assemblyDefinition.Write(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,15 +16,18 @@ namespace StardewModdingAPI.Helpers
|
|||
|
||||
private static void InjectMethod(ILProcessor ilProcessor, Instruction target, MethodReference method)
|
||||
{
|
||||
var callTarget = target;
|
||||
Instruction callEnterInstruction = ilProcessor.Create(OpCodes.Call, method);
|
||||
if (method.HasParameters)
|
||||
{
|
||||
Instruction loadObjInstruction = ilProcessor.Create(OpCodes.Ldarg_0);
|
||||
ilProcessor.InsertBefore(target, loadObjInstruction);
|
||||
callTarget = loadObjInstruction;
|
||||
ilProcessor.InsertAfter(loadObjInstruction, callEnterInstruction);
|
||||
}
|
||||
Instruction callEnterInstruction = ilProcessor.Create(OpCodes.Call, method);
|
||||
ilProcessor.InsertAfter(callTarget, callEnterInstruction);
|
||||
else
|
||||
{
|
||||
ilProcessor.InsertBefore(target, callEnterInstruction);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void InjectMethod(ILProcessor ilProcessor, IEnumerable<Instruction> targets, MethodReference method)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
@ -8,6 +9,140 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,6 +78,16 @@ namespace StardewModdingAPI
|
|||
/// <param name="message"></param>
|
||||
/// <param name="values"></param>
|
||||
public static void Comment(object message, params object[] values)
|
||||
{
|
||||
Log.PrintLog(message?.ToString(), false, values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Additional comment to display to console and logging.
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="values"></param>
|
||||
public static void Warning(object message, params object[] values)
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
Log.PrintLog(message?.ToString(), false, values);
|
||||
|
|
|
@ -26,7 +26,15 @@ namespace StardewModdingAPI
|
|||
|
||||
public static Texture2D DebugPixel { get; private set; }
|
||||
|
||||
public static SGame gamePtr;
|
||||
private static object modifiedAssemblyGame;
|
||||
//public static Game1 gamePtr;
|
||||
public static Game1 gamePtr
|
||||
{
|
||||
get
|
||||
{
|
||||
return modifiedAssemblyGame.MemberwiseCast<Game1>();
|
||||
}
|
||||
}
|
||||
public static bool ready;
|
||||
|
||||
public static Assembly StardewAssembly;
|
||||
|
@ -78,13 +86,18 @@ namespace StardewModdingAPI
|
|||
//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
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static void Test(Game1 instance)
|
||||
public static void Test(object instance)
|
||||
{
|
||||
var inst = instance;
|
||||
string test = Game1.samBandName;
|
||||
var test2 = Game1.numberOfSelectedItems;
|
||||
modifiedAssemblyGame = instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -137,7 +150,6 @@ namespace StardewModdingAPI
|
|||
StardewModdingAPI.Log.Info("Initializing SDV Assembly...");
|
||||
|
||||
// Load in the assembly - ignores security
|
||||
//StardewAssembly = Assembly.UnsafeLoadFrom(Constants.ExecutionPath + "\\Stardew Valley.exe");
|
||||
StardewAssembly = Assembly.Load(StardewContext.ModifiedAssembly.GetBuffer());
|
||||
StardewProgramType = StardewAssembly.GetType("StardewValley.Program", true);
|
||||
StardewGameInfo = StardewProgramType.GetField("gamePtr");
|
||||
|
@ -165,11 +177,6 @@ namespace StardewModdingAPI
|
|||
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); };
|
||||
|
||||
//Subscribe to events
|
||||
Events.ControlEvents.KeyPressed += Events_KeyPressed;
|
||||
Events.GameEvents.LoadContent += Events_LoadContent;
|
||||
//Events.MenuChanged += Events_MenuChanged; //Idk right now
|
||||
|
||||
StardewModdingAPI.Log.Verbose("Applying Final SDV Tweaks...");
|
||||
|
||||
StardewAssembly.EntryPoint.Invoke(null, new object[] { new string[] { } });
|
||||
|
@ -242,19 +249,7 @@ namespace StardewModdingAPI
|
|||
|
||||
try
|
||||
{
|
||||
//gamePtr = new SGame();
|
||||
//StardewModdingAPI.Log.Verbose("Patching SDV Graphics Profile...");
|
||||
//Game1.graphics.GraphicsProfile = GraphicsProfile.HiDef;
|
||||
//LoadMods();
|
||||
|
||||
//StardewForm = Control.FromHandle(Program.gamePtr.Window.Handle).FindForm();
|
||||
//StardewForm.Closing += StardewForm_Closing;
|
||||
|
||||
ready = true;
|
||||
|
||||
//Game1 g1 = gamePtr as Game1;
|
||||
//StardewGameInfo.SetValue(StardewProgramType, g1);
|
||||
//gamePtr.Run();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -323,72 +318,6 @@ namespace StardewModdingAPI
|
|||
}
|
||||
}
|
||||
|
||||
static void Events_LoadContent(object o, EventArgs e)
|
||||
{
|
||||
StardewModdingAPI.Log.Info("Initializing Debug Assets...");
|
||||
DebugPixel = new Texture2D(Game1.graphics.GraphicsDevice, 1, 1);
|
||||
DebugPixel.SetData(new Color[] { Color.White });
|
||||
|
||||
#if DEBUG
|
||||
StardewModdingAPI.Log.Verbose("REGISTERING BASE CUSTOM ITEM");
|
||||
SObject so = new SObject();
|
||||
so.Name = "Mario Block";
|
||||
so.CategoryName = "SMAPI Test Mod";
|
||||
so.Description = "It's a block from Mario!\nLoaded in realtime by SMAPI.";
|
||||
so.Texture = Texture2D.FromStream(Game1.graphics.GraphicsDevice, new FileStream(_modContentPaths[0] + "\\Test.png", FileMode.Open));
|
||||
so.IsPassable = true;
|
||||
so.IsPlaceable = true;
|
||||
StardewModdingAPI.Log.Verbose("REGISTERED WITH ID OF: " + SGame.RegisterModItem(so));
|
||||
|
||||
//StardewModdingAPI.Log.Verbose("REGISTERING SECOND CUSTOM ITEM");
|
||||
//SObject so2 = new SObject();
|
||||
//so2.Name = "Mario Painting";
|
||||
//so2.CategoryName = "SMAPI Test Mod";
|
||||
//so2.Description = "It's a painting of a creature from Mario!\nLoaded in realtime by SMAPI.";
|
||||
//so2.Texture = Texture2D.FromStream(Game1.graphics.GraphicsDevice, new FileStream(_modContentPaths[0] + "\\PaintingTest.png", FileMode.Open));
|
||||
//so2.IsPassable = true;
|
||||
//so2.IsPlaceable = true;
|
||||
//StardewModdingAPI.Log.Verbose("REGISTERED WITH ID OF: " + SGame.RegisterModItem(so2));
|
||||
|
||||
Command.CallCommand("load");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void Events_KeyPressed(object o, EventArgsKeyPressed e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void Events_MenuChanged(IClickableMenu newMenu)
|
||||
{
|
||||
StardewModdingAPI.Log.Verbose("NEW MENU: " + newMenu.GetType());
|
||||
if (newMenu is GameMenu)
|
||||
{
|
||||
Game1.activeClickableMenu = SGameMenu.ConstructFromBaseClass(Game1.activeClickableMenu as GameMenu);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void Events_LocationsChanged(List<GameLocation> newLocations)
|
||||
{
|
||||
#if DEBUG
|
||||
SGame.ModLocations = SGameLocation.ConstructFromBaseClasses(Game1.locations);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void Events_CurrentLocationChanged(GameLocation newLocation)
|
||||
{
|
||||
//SGame.CurrentLocation = null;
|
||||
//System.Threading.Thread.Sleep(10);
|
||||
#if DEBUG
|
||||
Console.WriteLine(newLocation.name);
|
||||
SGame.CurrentLocation = SGame.LoadOrCreateSGameLocationFromName(newLocation.name);
|
||||
#endif
|
||||
//Game1.currentLocation = SGame.CurrentLocation;
|
||||
//Log.LogComment(((SGameLocation) newLocation).name);
|
||||
//Log.LogComment("LOC CHANGED: " + SGame.currentLocation.name);
|
||||
}
|
||||
|
||||
public static void StardewInvoke(Action a)
|
||||
{
|
||||
StardewForm.Invoke(a);
|
||||
|
|
|
@ -125,6 +125,7 @@
|
|||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="API\Game.cs" />
|
||||
<Compile Include="Command.cs" />
|
||||
<Compile Include="Constants.cs" />
|
||||
<Compile Include="Entities\SCharacter.cs" />
|
||||
|
|
Loading…
Reference in New Issue