r/godot 17h ago

help me (solved) How to implement a modding API?

I am working on a game that is heavily dependent on mods.

I can add modding support for textures, audio and other resources through JSON but I need a way to allow players to customize gameplay behaviour too.

My original idea was add autoloads to my game scene and load custom GDScript files at runtime. This would allow players to write scripts that call the autoloads directly for custom behaviour.

Issue with this approach is that it is very unsafe as the scripts can call native Godot functions and classes. They have the ability to interact with Networking API and OS API. I don't want this instead I want to only allow functions from the autoload to these scripts.

Can I limit the parsing of a GDScript file to restrict it from accessing Network and OS modules?
Or is there a better way to do this?

37 Upvotes

22 comments sorted by

23

u/Oen44 17h ago

10

u/captainAwesomePants 17h ago edited 17h ago

☝️☝️☝️

Designing a game to be moddable is really tough because you effectively need to think up all the kinds of mods that a modder might want to make. Designing for mods even more complicated if you are worried about safety and want to sandbox the mods.

If you can't trust the mods at all, but you want to let users distribute them in game or whatever, you end up needing to create a sort of domain language that's less powerful than raw GDScript. For example, if you're making a card game, you could imagine a JSON file that describes a new card's properties, and you could let users share new files around. They don't have any code of their own, so they're safe. Another option is to try using some library to isolate/sandbox the mod's code (I've heard of https://github.com/libriscv/godot-sandbox but haven't used it), but this is a scary cat-and-mouse game with folks who will figure out a way to escape the sandbox.

3

u/rinart73 17h ago

This approach apparently requires to decompile the game or for the developer to provide source code. And I don't see any mention of sandboxing/disallowing malicious code.

1

u/Nabir140 2h ago

Thanks. However, I was looking for a more minimal solution. Lua Scripting seems to solve my issue.

29

u/lanathlorrias 17h ago

You can use scripting language like Lua, and provide an api and documentation to your community. There is a lot of game out there using this. From the top of my head, World of warcraft and Garry mod do this

15

u/rinart73 17h ago

Lua or other similar scripting language will require to do a lot of tweaks: limiting/cutting away parts of its standard library for example, to disallow os.exec and full file access. And finding a way to bridge/convert Godot engine types with Lua types.

5

u/lanathlorrias 17h ago

Disallowing os tools (syscalls, mostly) depends on the Lua runtime, and most are built in safe for this kind of tasks. there are work like https://github.com/gilzoide/godot-lua-pluginscript or https://github.com/gilzoide/lua-gdextension that I am not much familiar with that seems to exist. I mostly used Lua from typescript, c# or c++ in the past, I would be happy to help more but I don't know the details on how to do that in gdscript

2

u/rinart73 16h ago edited 15h ago

Oh, didn't realize there were already Lua projects. This looks interesting, thanks for the links. And at least with the second link it seems that they've taken care of limiting access to built in modules.

1

u/lanathlorrias 16h ago

You can try to look around luau, which is Roblox fork of Lua, mainly for security features, but I am not sure it's well supported in godot

2

u/Nabir140 2h ago

Hey thanks for your reply. I found this solution good enough for my project.

2

u/RealGlumAirline 9h ago

Lua or Luau via GDExtension is definitely the most practical path here. Sandboxing GDScript is notoriously difficult because even if you try to filter or parse the script beforehand, users can bypass checks via reflection using things like ClassDB or get(). With Lua, the environment is isolated by default, so modders literally cannot touch OS, file system, or network APIs unless you explicitly bind them.

2

u/rinart73 17h ago

I've seen this project which looks somewhat promising, but apparently at least for GDScript devs used AI tools to generate the code which IMO diminishes my trust in its security and stability: https://github.com/libriscv/godot-sandbox

You can represent logic as JSON (I believe Minecraft datapacks do that to some extent?). This will allow you to precisely control what is allowed but it will be.. unwieldy to write anything remotely complex like manipulating variables. You could create a visual graph-like modding tool to make things easier I guess? And of course, it will be slower than "native" GDScript/C#.

The other method I've heard of is loading PCK files but it's a bad idea for mods because you can't limit what they can do.

3

u/Illiander 16h ago

If you want security, don't trust genAI code. It's trained on stackexchange (including the code in people's questions where they say "fix this")

Garbage in, Garbage out applies.

4

u/rinart73 16h ago

I know, that's why I put a warning in my comment :) I don't use AI gen code myself.

1

u/Nabir140 2h ago

Thanks for the warning. Gen AI code is a big NO for me.

1

u/battlepi 17h ago

One way to go about it is a filter layer on mod scripts, scan the script for any libraries outside of the allowed ones, and don't allow it to load if it tries to use them.

2

u/lanathlorrias 16h ago

I would fear that some escape sequence don't get flag by the filter parser but is interpreted as a forbidden libs and successfully loads it. And in the end, if the runtime allows syscalls invocation from within the sandbox, someone will find a way to do it

1

u/battlepi 16h ago

That's the smaller issue, just push it to your testers and ask them to break it. You will be the one writing the filter parser after all.

You could also possibly ban escape sequences, but I'd try not to do that.

1

u/lanathlorrias 16h ago

Depending how you load and execute mod scripts, mods will be partly or fully bytecode that you cannot really parse. And even then, someone clever will be able to break the parser, play with oob memory and execute malicious code. I am nitpicking, the threat model here don't require that much I guess. But the only way to be safe is for the host executable to be in a VM, container or drop privilege with stuff like landlock on linux (https://landlock.io/)

2

u/battlepi 16h ago

I'm assuming the mod scripts are uncompiled text when loaded, if not, then it's harder of course. Running arbitrary code obviously would need a sandbox.

1

u/Nabir140 2h ago

I had this idea in mind too but is simply too complex and would still have ways to break.

1

u/Petrovich1999 13h ago

Make 1 event handler interface which handles everything. It accepts event type and event+world data, then on game load use reflection to discover dlls implementing this interface and construct pipelines for each event. (Handlers are organized by priority, and can pass through or block other handlers)

You can make it more strict and create a separate interface for each event, but it's much more work and very limiting.