2016-03-05 14:32:04 +08:00
using Microsoft.Xna.Framework ;
using Microsoft.Xna.Framework.Graphics ;
2016-03-09 23:05:47 +08:00
using Mono.Cecil ;
2016-03-10 00:38:12 +08:00
using Mono.Cecil.Cil ;
2016-03-05 14:32:04 +08:00
using StardewModdingAPI.Events ;
2016-03-13 09:54:22 +08:00
using StardewModdingAPI.ExtensionMethods ;
2016-03-10 02:45:04 +08:00
using StardewModdingAPI.Helpers ;
2016-03-05 14:32:04 +08:00
using StardewModdingAPI.Inheritance ;
using StardewModdingAPI.Inheritance.Menus ;
using StardewValley ;
using StardewValley.Menus ;
using System ;
2016-02-28 19:55:35 +08:00
using System.Collections.Generic ;
using System.ComponentModel ;
using System.IO ;
using System.Linq ;
using System.Reflection ;
using System.Threading ;
using System.Windows.Forms ;
2016-03-04 22:05:36 +08:00
2016-02-28 19:55:35 +08:00
namespace StardewModdingAPI
{
public class Program
{
2016-03-07 04:19:59 +08:00
private static List < string > _modPaths ;
private static List < string > _modContentPaths ;
2016-03-01 15:16:35 +08:00
public static Texture2D DebugPixel { get ; private set ; }
2016-03-13 09:54:22 +08:00
public static bool IsGameReferenceDirty { get ; set ; }
public static object gameInst ;
public static Game1 _gamePtr ;
2016-03-12 04:52:10 +08:00
public static Game1 gamePtr
{
get
{
2016-03-13 09:54:22 +08:00
if ( IsGameReferenceDirty & & gameInst ! = null )
{
_gamePtr = gameInst . Copy < Game1 > ( ) ;
}
return _gamePtr ;
2016-03-12 04:52:10 +08:00
}
}
2016-02-28 19:55:35 +08:00
2016-03-13 09:54:22 +08:00
public static bool ready ;
2016-02-28 19:55:35 +08:00
public static Form StardewForm ;
public static Thread gameThread ;
2016-03-10 02:45:04 +08:00
public static Thread consoleInputThread ;
2016-03-13 09:54:22 +08:00
2016-03-04 03:01:32 +08:00
2016-02-28 19:55:35 +08:00
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2016-03-07 04:19:59 +08:00
/// <summary>
/// Main method holding the API execution
/// </summary>
/// <param name="args"></param>
2016-02-28 19:55:35 +08:00
private static void Main ( string [ ] args )
{
2016-03-07 04:19:59 +08:00
try
{
ConfigureUI ( ) ;
ConfigurePaths ( ) ;
2016-03-09 23:05:47 +08:00
ConfigureMethodInjection ( ) ;
2016-03-10 03:04:33 +08:00
ConfigureSDV ( ) ;
GameRunInvoker ( ) ;
2016-03-07 04:19:59 +08:00
}
catch ( Exception e )
{
// Catch and display all exceptions.
2016-03-07 04:25:14 +08:00
StardewModdingAPI . Log . Error ( "Critical error: " + e ) ;
2016-03-07 04:19:59 +08:00
}
2016-03-07 04:25:14 +08:00
StardewModdingAPI . Log . Comment ( "The API will now terminate. Press any key to continue..." ) ;
2016-03-07 04:19:59 +08:00
Console . ReadKey ( ) ;
}
2016-03-09 23:05:47 +08:00
/// <summary>
/// Configures Mono.Cecil injections
/// </summary>
private static void ConfigureMethodInjection ( )
{
2016-03-13 09:54:22 +08:00
StardewAssembly . ModifyStardewAssembly ( ) ;
2016-03-10 06:11:57 +08:00
2016-03-13 09:54:22 +08:00
#if DEBUG
StardewAssembly . WriteModifiedExe ( ) ;
#endif
}
2016-03-12 04:52:10 +08:00
public static void Test ( object instance )
2016-03-10 00:38:12 +08:00
{
2016-03-13 09:54:22 +08:00
gameInst = instance ;
IsGameReferenceDirty = true ;
}
2016-03-07 04:19:59 +08:00
/// <summary>
/// Set up the console properties
/// </summary>
private static void ConfigureUI ( )
{
2016-03-07 05:58:40 +08:00
Console . Title = Constants . ConsoleTitle ;
2016-02-28 19:55:35 +08:00
2016-03-05 14:32:04 +08:00
#if DEBUG
Console . Title + = " - DEBUG IS NOT FALSE, AUTHOUR NEEDS TO REUPLOAD THIS VERSION" ;
#endif
2016-03-07 04:19:59 +08:00
}
/// <summary>
/// Setup the required paths and logging
/// </summary>
private static void ConfigurePaths ( )
{
2016-03-07 04:25:14 +08:00
StardewModdingAPI . Log . Info ( "Validating api paths..." ) ;
2016-03-07 04:19:59 +08:00
_modPaths = new List < string > ( ) ;
_modContentPaths = new List < string > ( ) ;
2016-03-05 14:32:04 +08:00
2016-03-11 04:13:57 +08:00
2016-03-05 14:32:04 +08:00
//TODO: Have an app.config and put the paths inside it so users can define locations to load mods from
2016-03-07 05:58:40 +08:00
_modPaths . Add ( Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . ApplicationData ) , "StardewValley" , "Mods" ) ) ;
_modPaths . Add ( Path . Combine ( Constants . ExecutionPath , "Mods" ) ) ;
2016-03-07 07:28:32 +08:00
_modContentPaths . Add ( Path . Combine ( Constants . ExecutionPath , "Mods" , "Content" ) ) ;
2016-03-07 05:58:40 +08:00
_modContentPaths . Add ( Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . ApplicationData ) , "StardewValley" , "Mods" , "Content" ) ) ;
2016-03-02 07:44:41 +08:00
2016-03-03 01:24:15 +08:00
//Checks that all defined modpaths exist as directories
2016-03-07 04:19:59 +08:00
_modPaths . ForEach ( path = > VerifyPath ( path ) ) ;
_modContentPaths . ForEach ( path = > VerifyPath ( path ) ) ;
2016-03-07 05:58:40 +08:00
VerifyPath ( Constants . LogPath ) ;
2016-03-01 04:01:41 +08:00
2016-03-07 05:58:40 +08:00
StardewModdingAPI . Log . Initialize ( Constants . LogPath ) ;
2016-03-07 04:19:59 +08:00
2016-03-07 05:58:40 +08:00
if ( ! File . Exists ( Constants . ExecutionPath + "\\Stardew Valley.exe" ) )
2016-03-02 07:44:41 +08:00
{
2016-03-13 09:54:22 +08:00
StardewModdingAPI . Log . Error ( "Replace this" ) ;
//throw new FileNotFoundException(string.Format("Could not found: {0}\\Stardew Valley.exe", Constants.ExecutionPath));
2016-03-01 04:01:41 +08:00
}
2016-03-07 04:51:22 +08:00
}
2016-03-10 02:45:04 +08:00
2016-03-07 04:19:59 +08:00
/// <summary>
/// Load Stardev Valley and control features
/// </summary>
private static void ConfigureSDV ( )
{
2016-03-10 03:04:33 +08:00
StardewModdingAPI . Log . Info ( "Initializing SDV Assembly..." ) ;
2016-03-12 04:52:10 +08:00
2016-03-10 03:04:33 +08:00
// Load in the assembly - ignores security
2016-03-13 09:54:22 +08:00
StardewAssembly . LoadStardewAssembly ( ) ;
StardewModdingAPI . Log . Comment ( "SDV Loaded Into Memory" ) ;
2016-03-07 05:58:40 +08:00
// Change the game's version
StardewModdingAPI . Log . Verbose ( "Injecting New SDV Version..." ) ;
Game1 . version + = string . Format ( "-Z_MODDED | SMAPI {0}" , Constants . VersionString ) ;
2016-03-05 14:32:04 +08:00
2016-03-13 09:54:22 +08:00
// Create the thread for the game to run in.
Application . ThreadException + = StardewModdingAPI . Log . Application_ThreadException ;
Application . SetUnhandledExceptionMode ( UnhandledExceptionMode . CatchException ) ;
AppDomain . CurrentDomain . UnhandledException + = StardewModdingAPI . Log . CurrentDomain_UnhandledException ;
2016-02-28 19:55:35 +08:00
2016-03-03 01:24:15 +08:00
//Create definition to listen for input
2016-03-06 23:54:09 +08:00
StardewModdingAPI . Log . Verbose ( "Initializing Console Input Thread..." ) ;
2016-03-03 01:24:15 +08:00
consoleInputThread = new Thread ( ConsoleInputThread ) ;
2016-03-12 04:52:10 +08:00
2016-03-13 09:54:22 +08:00
Command . RegisterCommand ( "help" , "Lists all commands | 'help <cmd>' returns command description" ) . CommandFired + = help_CommandFired ;
2016-03-11 04:13:57 +08:00
2016-03-13 09:54:22 +08:00
StardewAssembly . Launch ( ) ;
2016-03-07 04:19:59 +08:00
}
2016-02-28 19:55:35 +08:00
2016-03-07 04:19:59 +08:00
/// <summary>
/// Wrap the 'RunGame' method for console output
/// </summary>
private static void GameRunInvoker ( )
{
2016-03-03 01:24:15 +08:00
//Game's in memory now, send the event
2016-03-06 23:54:09 +08:00
StardewModdingAPI . Log . Verbose ( "Game Loaded" ) ;
2016-03-04 22:05:36 +08:00
Events . GameEvents . InvokeGameLoaded ( ) ;
2016-03-01 04:01:41 +08:00
2016-03-07 04:51:22 +08:00
StardewModdingAPI . Log . Comment ( "Type 'help' for help, or 'help <cmd>' for a command's usage" ) ;
2016-03-03 01:24:15 +08:00
//Begin listening to input
consoleInputThread . Start ( ) ;
2016-03-01 14:35:52 +08:00
2016-03-01 04:01:41 +08:00
while ( ready )
{
//Check if the game is still running 10 times a second
Thread . Sleep ( 1000 / 10 ) ;
}
2016-03-03 01:24:15 +08:00
//abort the thread, we're closing
2016-03-01 04:01:41 +08:00
if ( consoleInputThread ! = null & & consoleInputThread . ThreadState = = ThreadState . Running )
consoleInputThread . Abort ( ) ;
2016-03-06 23:54:09 +08:00
StardewModdingAPI . Log . Verbose ( "Game Execution Finished" ) ;
StardewModdingAPI . Log . Verbose ( "Shutting Down..." ) ;
2016-03-03 01:24:15 +08:00
Thread . Sleep ( 100 ) ;
2016-03-01 04:01:41 +08:00
Environment . Exit ( 0 ) ;
2016-03-05 14:32:04 +08:00
}
2016-03-07 04:19:59 +08:00
/// <summary>
/// Create the given directory path if it does not exist
/// </summary>
/// <param name="path">Desired directory path</param>
private static void VerifyPath ( string path )
{
try
{
if ( ! Directory . Exists ( path ) )
{
Directory . CreateDirectory ( path ) ;
}
}
catch ( Exception ex )
2016-03-07 04:25:14 +08:00
{
StardewModdingAPI . Log . Error ( "Could not create a path: " + path + "\n\n" + ex ) ;
}
2016-03-07 04:19:59 +08:00
}
2016-03-05 14:32:04 +08:00
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2016-02-29 05:01:24 +08:00
2016-03-04 03:04:29 +08:00
static void StardewForm_Closing ( object sender , CancelEventArgs e )
{
e . Cancel = true ;
2016-03-04 06:08:40 +08:00
if ( true | | MessageBox . Show ( "Are you sure you would like to quit Stardew Valley?\nUnsaved progress will be lost!" , "Confirm Exit" , MessageBoxButtons . YesNo , MessageBoxIcon . Exclamation ) = = DialogResult . Yes )
{
gamePtr . Exit ( ) ;
gamePtr . Dispose ( ) ;
StardewForm . Hide ( ) ;
ready = false ;
}
2016-03-04 03:04:29 +08:00
}
public static void LoadMods ( )
{
2016-03-06 23:54:09 +08:00
StardewModdingAPI . Log . Verbose ( "LOADING MODS" ) ;
2016-03-04 03:04:29 +08:00
int loadedMods = 0 ;
2016-03-07 04:19:59 +08:00
foreach ( string ModPath in _modPaths )
2016-03-04 03:04:29 +08:00
{
foreach ( String s in Directory . GetFiles ( ModPath , "*.dll" ) )
{
2016-03-04 04:06:25 +08:00
if ( s . Contains ( "StardewInjector" ) )
continue ;
2016-03-06 23:54:09 +08:00
StardewModdingAPI . Log . Success ( "Found DLL: " + s ) ;
2016-03-04 03:04:29 +08:00
try
{
Assembly mod = Assembly . UnsafeLoadFrom ( s ) ; //to combat internet-downloaded DLLs
2016-03-05 14:32:04 +08:00
if ( mod . DefinedTypes . Count ( x = > x . BaseType = = typeof ( Mod ) ) > 0 )
2016-03-04 03:04:29 +08:00
{
2016-03-06 23:54:09 +08:00
StardewModdingAPI . Log . Verbose ( "Loading Mod DLL..." ) ;
2016-03-05 14:32:04 +08:00
TypeInfo tar = mod . DefinedTypes . First ( x = > x . BaseType = = typeof ( Mod ) ) ;
Mod m = ( Mod ) mod . CreateInstance ( tar . ToString ( ) ) ;
2016-03-04 03:04:29 +08:00
Console . WriteLine ( "LOADED MOD: {0} by {1} - Version {2} | Description: {3}" , m . Name , m . Authour , m . Version , m . Description ) ;
loadedMods + = 1 ;
m . Entry ( ) ;
}
else
{
2016-03-06 23:54:09 +08:00
StardewModdingAPI . Log . Error ( "Invalid Mod DLL" ) ;
2016-03-04 03:04:29 +08:00
}
}
catch ( Exception ex )
{
2016-03-06 23:54:09 +08:00
StardewModdingAPI . Log . Error ( "Failed to load mod '{0}'. Exception details:\n" + ex , s ) ;
2016-03-04 03:04:29 +08:00
}
}
}
2016-03-06 23:54:09 +08:00
StardewModdingAPI . Log . Success ( "LOADED {0} MODS" , loadedMods ) ;
2016-03-04 03:04:29 +08:00
}
public static void ConsoleInputThread ( )
{
string input = string . Empty ;
while ( true )
{
Command . CallCommand ( Console . ReadLine ( ) ) ;
}
}
2016-03-12 04:52:10 +08:00
2016-03-04 03:04:29 +08:00
public static void StardewInvoke ( Action a )
{
StardewForm . Invoke ( a ) ;
2016-03-05 14:32:04 +08:00
}
static void help_CommandFired ( object o , EventArgsCommand e )
{
if ( e . Command . CalledArgs . Length > 0 )
{
Command fnd = Command . FindCommand ( e . Command . CalledArgs [ 0 ] ) ;
if ( fnd = = null )
2016-03-06 23:54:09 +08:00
StardewModdingAPI . Log . Error ( "The command specified could not be found" ) ;
2016-03-05 14:32:04 +08:00
else
{
if ( fnd . CommandArgs . Length > 0 )
2016-03-06 23:54:09 +08:00
StardewModdingAPI . Log . Info ( "{0}: {1} - {2}" , fnd . CommandName , fnd . CommandDesc , fnd . CommandArgs . ToSingular ( ) ) ;
2016-03-05 14:32:04 +08:00
else
2016-03-06 23:54:09 +08:00
StardewModdingAPI . Log . Info ( "{0}: {1}" , fnd . CommandName , fnd . CommandDesc ) ;
2016-03-05 14:32:04 +08:00
}
}
else
2016-03-06 23:54:09 +08:00
StardewModdingAPI . Log . Info ( "Commands: " + Command . RegisteredCommands . Select ( x = > x . CommandName ) . ToSingular ( ) ) ;
2016-03-05 14:32:04 +08:00
}
2016-03-06 23:54:09 +08:00
#region Logging
[Obsolete("This method is obsolete and will be removed in v0.39, please use the appropriate methods in the Log class")]
public static void Log ( object o , params object [ ] format )
{
StardewModdingAPI . Log . Info ( o , format ) ;
}
[Obsolete("This method is obsolete and will be removed in v0.39, please use the appropriate methods in the Log class")]
public static void LogColour ( ConsoleColor c , object o , params object [ ] format )
{
StardewModdingAPI . Log . Info ( o , format ) ;
}
[Obsolete("This method is obsolete and will be removed in v0.39, please use the appropriate methods in the Log class")]
public static void LogInfo ( object o , params object [ ] format )
{
StardewModdingAPI . Log . Info ( o , format ) ;
}
[Obsolete("This method is obsolete and will be removed in v0.39, please use the appropriate methods in the Log class")]
public static void LogError ( object o , params object [ ] format )
{
StardewModdingAPI . Log . Error ( o , format ) ;
}
[Obsolete("This method is obsolete and will be removed in v0.39, please use the appropriate methods in the Log class")]
public static void LogDebug ( object o , params object [ ] format )
{
StardewModdingAPI . Log . Debug ( o , format ) ;
}
[Obsolete("This method is obsolete and will be removed in v0.39, please use the appropriate methods in the Log class")]
public static void LogValueNotSpecified ( )
{
StardewModdingAPI . Log . Error ( "<value> must be specified" ) ;
}
[Obsolete("This method is obsolete and will be removed in v0.39, please use the appropriate methods in the Log class")]
public static void LogObjectValueNotSpecified ( )
{
StardewModdingAPI . Log . Error ( "<object> and <value> must be specified" ) ;
}
[Obsolete("This method is obsolete and will be removed in v0.39, please use the appropriate methods in the Log class")]
public static void LogValueInvalid ( )
{
StardewModdingAPI . Log . Error ( "<value> is invalid" ) ;
}
[Obsolete("This method is obsolete and will be removed in v0.39, please use the appropriate methods in the Log class")]
public static void LogObjectInvalid ( )
{
StardewModdingAPI . Log . Error ( "<object> is invalid" ) ;
}
[Obsolete("This method is obsolete and will be removed in v0.39, please use the appropriate methods in the Log class")]
public static void LogValueNotInt32 ( )
{
StardewModdingAPI . Log . Error ( "<value> must be a whole number (Int32)" ) ;
2016-03-05 14:32:04 +08:00
}
2016-03-06 23:54:09 +08:00
#endregion
2016-03-04 03:04:29 +08:00
}
2016-03-04 04:06:25 +08:00
}