Puppeteer
Emperor
I'm breaking this out as a separate topic:
Embedded Script Interpreters
Lua and Python are interpreted script languages. Lua (including its C# implementation MoonSharp) and IronPython in a particular Python are embedded (optionally so for IronPython), meaning the interpreter is compiled into your project. No dynamic loading beyond reading a text file or string is needed because the project understands Lua or Python script.
You can just expose the script environment to a class or interface definition and let the script have at it. For non-privileged scripts, just design the class methods and setters so you can't break the rules. e.g. At the "control this unit" class level, prevent a settler from walking on water or taking too many moves if the script tries to.
I prototyped this in SimpleGame. I defined a Turn API/class in C# code and document it in the readme. In SimpleGame I have the Lua scripts defined as constants or getters or something, but that's only because I couldn't be bothered to write the code to read in a text file or provide a script editor for a proof of concept.
Other Mod Methods
Don't think about Godot here. Think about C#. Godot is just our display and UI. (Possible exceptions for handling various media, but I think we're talking code extensibility here.)
InC# all CLR, DLL assemblies can be made. In the SimpleGame examples shown above, a class API is defined that can be exposed to an interpreted script. We can pass that same interface/class to any CLR assembly it seems to me. In SimpleGame I pass a Turn object to a turn handler function/method, so why can't that turn handler be loaded in a CLR DLL? I haven't proof-of-concepted this yet, but I can't see any blockers from here.
Ozymandias was hyped about "a dll for each civ," and I think we can have exactly that if we want. I'm hand-waving away some of the implementation, but it seems to me a path to a DLL could be specified in a BIQ-analog, and the module class/interface API in question can use the handler function defined in that DLL for that particular thing.
Note that this opens modding to any CLR language. But it would need to reference the interface definition and be precompiled into DLLs, of course, unlike the embedded options which could read any script string/text on the fly. I think .NET DLLs are platform-independent, but I'm not entirely sure of that.
Editing to add: In my idealistic vision, this interface defining how to do a thing (e.g. take a turn) would be the same accessed by the UI, so the human player and any script are controlling the game through the same interface. That may be much easier said than done, though...I haven't really thought that through completely. That would mean lots of probing the interface class for legal options, and I'm not sure that will be fun, productive, or practical to code as such, especially in a remote server multiplayer environment.
Then you get into areas like interfaces where mods could hook in, and not having worked on a project where modules could be loaded dynamically (especially modules in another languages, such as Lua), I'm much less certain how to approach it.
My current project uses plugins in Python, and I've done Minecraft Forge modding which is based on event callbacks, so I have some experience with the pattern but I don't know yet how closely those ideas carry over to Godot and especially Lua. It would be great if we can find a working example of runtime Godot plugins.
Embedded Script Interpreters
Lua and Python are interpreted script languages. Lua (including its C# implementation MoonSharp) and IronPython in a particular Python are embedded (optionally so for IronPython), meaning the interpreter is compiled into your project. No dynamic loading beyond reading a text file or string is needed because the project understands Lua or Python script.
You can just expose the script environment to a class or interface definition and let the script have at it. For non-privileged scripts, just design the class methods and setters so you can't break the rules. e.g. At the "control this unit" class level, prevent a settler from walking on water or taking too many moves if the script tries to.
I prototyped this in SimpleGame. I defined a Turn API/class in C# code and document it in the readme. In SimpleGame I have the Lua scripts defined as constants or getters or something, but that's only because I couldn't be bothered to write the code to read in a text file or provide a script editor for a proof of concept.
Spoiler Lua handler setup code :
See explanation following code.
This LuaAI class implements the IAI interface (Artificial Intelligence, a turn-taker) and is a child of MoonSharp.Script which is the Lua interpreter class.
The constructor registers the Turn class/interface definition in the Lua environment, meaning the Lua script can access the properties and methods of the C# Turn object. (It also calls the parent constructor in a way to sandbox itself; to prevent the script from accessing the filesystem, for example.)
The MoonSharp.Script.DoString() call runs the passed script in the Lua environment instantiated by being a child of MoonSharp.Script. See example Lua code below. In this case I'm defining a Lua handler function player_turn. (Note there are some automatic capitalization changes between C# names and Lua names.)
The PlayTurn() method of the code above (part of the IAI interface) handles the Turn object by passing it directly to the player_turn function inside the configured-in-constructor Lua environment.
Code:
class LuaAI : Script, IAI {
public LuaAI(string script) : base(CoreModules.Preset_HardSandbox) {
UserData.RegisterType<Turn>();
DoString(script);
}
public void PlayTurn(Turn turn) {
DynValue _ = Call(Globals["player_turn"], turn);
}
}
The constructor registers the Turn class/interface definition in the Lua environment, meaning the Lua script can access the properties and methods of the C# Turn object. (It also calls the parent constructor in a way to sandbox itself; to prevent the script from accessing the filesystem, for example.)
The MoonSharp.Script.DoString() call runs the passed script in the Lua environment instantiated by being a child of MoonSharp.Script. See example Lua code below. In this case I'm defining a Lua handler function player_turn. (Note there are some automatic capitalization changes between C# names and Lua names.)
The PlayTurn() method of the code above (part of the IAI interface) handles the Turn object by passing it directly to the player_turn function inside the configured-in-constructor Lua environment.
Code:
function player_turn(turn)
if turn.isEnemyInRange == true then
turn.attack()
else
turn.move(1,0)
end
end
Other Mod Methods
Don't think about Godot here. Think about C#. Godot is just our display and UI. (Possible exceptions for handling various media, but I think we're talking code extensibility here.)
In
Ozymandias was hyped about "a dll for each civ," and I think we can have exactly that if we want. I'm hand-waving away some of the implementation, but it seems to me a path to a DLL could be specified in a BIQ-analog, and the module class/interface API in question can use the handler function defined in that DLL for that particular thing.
Note that this opens modding to any CLR language. But it would need to reference the interface definition and be precompiled into DLLs, of course, unlike the embedded options which could read any script string/text on the fly. I think .NET DLLs are platform-independent, but I'm not entirely sure of that.
Editing to add: In my idealistic vision, this interface defining how to do a thing (e.g. take a turn) would be the same accessed by the UI, so the human player and any script are controlling the game through the same interface. That may be much easier said than done, though...I haven't really thought that through completely. That would mean lots of probing the interface class for legal options, and I'm not sure that will be fun, productive, or practical to code as such, especially in a remote server multiplayer environment.
Last edited: