-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProgram.cs
More file actions
117 lines (100 loc) · 4.58 KB
/
Copy pathProgram.cs
File metadata and controls
117 lines (100 loc) · 4.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// This file is the main entry point when we build a full
// executable in C#. It is executed in sequence as if this
// were a static void Main. This is the standard since
// C# 9 and .NET 6.
// See: https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/program-structure/#top-level-statements-vs-main-method
using DotCL;
var isTestMode = false;
for (int i = 0; i < args.Length; i++) {
if (args[i] == "--test") {
isTestMode = true;
}
}
// Boot dotcl BEFORE constructing the Game so the Lisp side has a chance to
// (dotnet:define-class "MonoGameCLOSProxy" (Game) ...) and the dynamically
// emitted assembly is loaded. Then we instantiate the Lisp-defined type
// via DotclHost.Call("MAKE-GAME") and Run() it on the main thread.
DotclHost.Initialize();
// Register my Custom C#-implemented Lisp package with DotCL's
// package registry.
MonoUtilsRegistrar.Initialize();
// dotcl's ResolveDotNetType does not seem to need the types force
// previously loaded at this point anymore.
var manifestPath = Path.Combine(
AppContext.BaseDirectory, "dotcl-fasl", "dotcl-deps.txt");
Console.WriteLine($"[Program.cs] manifest: {manifestPath}");
var loaded = DotclHost.LoadFromManifest(manifestPath);
Console.WriteLine($"[Program.cs] LoadFromManifest loaded {loaded} fasls");
//////////////////////////////////////////////////////////////////////////////
// Tests
if (isTestMode) {
Console.WriteLine($"[Program.cs] Running Base class tests...");
// Test the creation to call base classes of random C# objects
var Child = new Child();
Child.Speak();
Console.WriteLine("The next line should be the parent");
DynamicBaseCaller.CallBaseMethod_VoidVoid(Child, "Speak");
Console.WriteLine();
var Calc = new AdvancedCalculator();
Console.WriteLine(Calc.Add(1, "two"));
Console.WriteLine("The next line should be the Base Calculator");
Console.WriteLine(
DynamicBaseCaller.CallBaseMethod(Calc, "Add",
new Type[] { typeof(int), typeof(string) },
new object[] { 3, "four" })
);
Console.WriteLine();
Console.WriteLine("The next line should be the parent (again)");
var ret = DynamicBaseCaller.CallBaseMethod(Child, "Speak", null, null);
Console.WriteLine($"Value of ret: {ret}; is ret null? {ret == null}");
if (ret != null) {
Console.WriteLine($"Type of ret: {ret.GetType().FullName}");
}
Console.WriteLine();
var func = DynamicBaseCaller.CallBaseMethodBuilder(Calc, "Add",
new Type[] { typeof(int), typeof(string) });
Console.WriteLine("The next 2 lines should be the Base Calculator (again)");
Console.WriteLine(func(Calc, new object[] { 5, "six" }));
Console.WriteLine(DynamicBaseCaller.CallFunc(func, Calc, 7, "eight"));
Console.WriteLine($"[Program.cs] Running Lisp tests...");
// FIXME: Not sure why this actually works without being specified in the
// dungeon-slime-tests package
DotclHost.Call("RUN-ALL-TESTS");
Console.WriteLine($"[Program.cs] Not running game; in --test mode.");
return;
}
///////////////////////////////////////////////////////////////////////////
// Main game
// MAKE-GAME (defined in main.lisp) returns a MonoGameCLOSProxy instance.
// FIXME: I'm really not sure why this works without being qualified with
// DUNGEON-SLIME: or ::
var gameObj = DotclHost.Call("MAKE-GAME");
if (gameObj is LispDotNetObject dno &&
dno.Value is Microsoft.Xna.Framework.Game game) {
Console.WriteLine($"[Program.cs] running game: {game.GetType().FullName}");
// We invoke the (non-virtual) Run() method on this object directly, rather than casting this
// to Game, which would run that specific Run() method
var runMethod = game.GetType().GetMethod("Run",
System.Reflection.BindingFlags.Public |
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.DeclaredOnly); // Non-virtual
if (runMethod != null) {
Console.WriteLine("[Program.cs] Executing actual object's shadowed Run()");
try {
runMethod.Invoke(game, null);
} catch (System.Reflection.TargetInvocationException ex) {
Console.WriteLine($"[Program.cs] Got exception: {ex}");
throw ex.InnerException ?? ex;
// TODO: Catch other exceptions
}
} else {
// There was no shadowed Run() so just use the one on the Game
// TODO: Catch exceptions
game.Run();
}
// Not necessary if the process ends after this, but if it doesn't:
game.Dispose();
} else {
throw new InvalidOperationException(
$"MAKE-GAME returned unexpected: {gameObj?.GetType().Name}");
}