11using Rampastring . Tools ;
2+ using Rampastring . XNAUI ;
23using System ;
34using System . IO ;
45using System . Reflection ;
56using TSMapEditor . Models ;
7+ using TSMapEditor . Rendering ;
8+ using TSMapEditor . UI ;
9+ using TSMapEditor . UI . Windows ;
610using Westwind . Scripting ;
711
812namespace TSMapEditor . Scripts
913{
14+ // Has to be a class, Westwind.Scripting does not appear to recognize this if it is a struct.
15+ public class ScriptDependencies
16+ {
17+ public Map Map ;
18+ public ICursorActionTarget CursorActionTarget ;
19+ public EditorState EditorState ;
20+ public WindowManager WindowManager ;
21+ public WindowController WindowController ;
22+
23+ public ScriptDependencies ( Map map , ICursorActionTarget cursorActionTarget , EditorState editorState , WindowManager windowManager , WindowController windowController )
24+ {
25+ Map = map ;
26+ CursorActionTarget = cursorActionTarget ;
27+ EditorState = editorState ;
28+ WindowManager = windowManager ;
29+ WindowController = windowController ;
30+ }
31+ }
32+
1033 public static class ScriptRunner
1134 {
1235 private static object scriptClassInstance ;
13- private static MethodInfo getDescriptionMethod ;
36+
37+ private static MethodInfo getDescriptionMethod ; // V1 only
1438 private static MethodInfo performMethod ;
15- private static MethodInfo getSuccessMessageMethod ;
39+ private static MethodInfo getSuccessMessageMethod ; // V1 only
40+
41+ public static int ActiveScriptAPIVersion ;
42+
43+ public static string CompileScript ( ScriptDependencies scriptDependencies , string scriptPath )
44+ {
45+ if ( ! File . Exists ( scriptPath ) )
46+ return "The script file does not exist!" ;
47+
48+ var sourceCode = File . ReadAllText ( scriptPath ) ;
49+ string error = CompileSource ( scriptDependencies , sourceCode ) ;
50+ if ( error != null )
51+ return error ;
52+
53+ return null ;
54+ }
55+
56+ public static string GetDescriptionFromScriptV1 ( )
57+ {
58+ return ( string ) getDescriptionMethod . Invoke ( scriptClassInstance , null ) ;
59+ }
1660
17- public static string RunScript ( Map map , string scriptPath )
61+ public static string RunScriptV1 ( Map map , string scriptPath )
1862 {
1963 if ( scriptClassInstance == null || performMethod == null || getSuccessMessageMethod == null )
2064 throw new InvalidOperationException ( "Script not properly compiled!" ) ;
@@ -26,7 +70,7 @@ public static string RunScript(Map map, string scriptPath)
2670 performMethod . Invoke ( scriptClassInstance , new object [ ] { map } ) ;
2771 return ( string ) getSuccessMessageMethod . Invoke ( scriptClassInstance , null ) ;
2872 }
29- catch ( Exception ex ) // rare case where catching Exception is OK, we cannot know what the script can throw
73+ catch ( Exception ex ) // catching Exception is OK, we cannot know what the script can throw
3074 {
3175 string errorMessage = ex . Message ;
3276
@@ -44,27 +88,43 @@ public static string RunScript(Map map, string scriptPath)
4488 }
4589 }
4690
47- public static ( string error , string description ) GetDescriptionFromScript ( Map map , string scriptPath )
91+ public static string RunScriptV2 ( )
4892 {
49- if ( ! File . Exists ( scriptPath ) )
50- return ( "The script file does not exist!" , null ) ;
93+ try
94+ {
95+ performMethod . Invoke ( scriptClassInstance , null ) ;
96+ }
97+ catch ( Exception ex ) // catching Exception is OK, we cannot know what the script can throw
98+ {
99+ string errorMessage = ex . Message ;
51100
52- var sourceCode = File . ReadAllText ( scriptPath ) ;
53- string error = CompileSource ( map , sourceCode ) ;
54- if ( error != null )
55- return ( error , null ) ;
101+ while ( ex . InnerException != null )
102+ {
103+ ex = ex . InnerException ;
104+ errorMessage += Environment . NewLine + Environment . NewLine +
105+ "Inner exception message: " + ex . Message + Environment . NewLine +
106+ "Stack trace: " + ex . StackTrace ;
107+ }
108+
109+ Logger . Log ( "Exception while running script. Returned exception message: " + errorMessage ) ;
56110
57- return ( null , ( string ) getDescriptionMethod . Invoke ( scriptClassInstance , null ) ) ;
111+ return "An error occurred while running the script. Returned error message: " + Environment . NewLine + Environment . NewLine + errorMessage ;
112+ }
113+
114+ return null ;
58115 }
59116
60- private static string CompileSource ( Map map , string source )
117+ private static string CompileSource ( ScriptDependencies scriptDependencies , string source )
61118 {
62119 var script = new CSharpScriptExecution ( ) { SaveGeneratedCode = true } ;
63120 script . AddLoadedReferences ( ) ;
64121 script . AddNamespace ( "TSMapEditor" ) ;
65122 script . AddNamespace ( "TSMapEditor.Models" ) ;
66123 script . AddNamespace ( "TSMapEditor.Rendering" ) ;
67124 script . AddNamespace ( "TSMapEditor.GameMath" ) ;
125+ script . AddNamespace ( "TSMapEditor.Scripts" ) ;
126+ script . AddNamespace ( "TSMapEditor.UI" ) ;
127+ script . AddNamespace ( "TSMapEditor.UI.Controls" ) ;
68128
69129 getDescriptionMethod = null ;
70130 performMethod = null ;
@@ -77,8 +137,80 @@ private static string CompileSource(Map map, string source)
77137 return script . ErrorMessage ;
78138 }
79139
140+ int version = 1 ;
141+ Type classType = instance . GetType ( ) ;
142+ var properties = classType . GetProperties ( ) ;
143+ var apiVersionProperty = Array . Find ( properties , prop => prop . Name == "ApiVersion" ) ;
144+ if ( apiVersionProperty != null )
145+ {
146+ if ( apiVersionProperty . PropertyType != typeof ( int ) )
147+ {
148+ return "ApiVersion property is not an integer!" ;
149+ }
150+
151+ version = ( int ) apiVersionProperty . GetValue ( instance ) ;
152+ }
153+
154+ ActiveScriptAPIVersion = version ;
155+
156+ if ( version == 1 )
157+ {
158+ return ExtractScriptV1 ( instance ) ;
159+ }
160+ else if ( version == 2 )
161+ {
162+ return ExtractScriptV2 ( instance , scriptDependencies ) ;
163+ }
164+
165+ return $ "Unsupported scripting API version: { version } . Contact the script's author for troubleshooting.";
166+ }
167+
168+ private static string ExtractScriptV2 ( object instance , ScriptDependencies scriptDependencies )
169+ {
170+ scriptClassInstance = instance ;
171+ Type classType = instance . GetType ( ) ;
172+
173+ var methods = classType . GetMethods ( ) ;
174+ foreach ( MethodInfo method in methods )
175+ {
176+ if ( method . Name == "Perform" )
177+ {
178+ performMethod = method ;
179+ if ( performMethod . GetParameters ( ) . Length > 0 )
180+ {
181+ return "The Perform method has one or more parameters." + Environment . NewLine +
182+ "It should have no parameters in a V2 script." + Environment . NewLine +
183+ "To access map data, access ScriptDependencies.Map." ;
184+ }
185+ }
186+ }
187+
188+ var properties = classType . GetProperties ( ) ;
189+ foreach ( PropertyInfo property in properties )
190+ {
191+ var setter = property . GetSetMethod ( ) ;
192+ if ( setter == null )
193+ continue ;
194+
195+ if ( property . Name == "ScriptDependencies" )
196+ {
197+ setter . Invoke ( instance , [ scriptDependencies ] ) ;
198+ }
199+ }
200+
201+ if ( performMethod == null )
202+ {
203+ return "The script does not declare the Perform method." ;
204+ }
205+
206+ return null ;
207+ }
208+
209+ private static string ExtractScriptV1 ( object instance )
210+ {
80211 scriptClassInstance = instance ;
81212 Type classType = instance . GetType ( ) ;
213+
82214 var methods = classType . GetMethods ( ) ;
83215 foreach ( MethodInfo method in methods )
84216 {
0 commit comments