godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.12k stars 69 forks source link

Add conditional compilation directives to GDScript #7375

Open jtnicholl opened 1 year ago

jtnicholl commented 1 year ago

Describe the project you are working on

A multiplayer game with a separate dedicated client and cloud-hosted server.

Describe the problem or limitation you are having in your project

In my game, there are lines of code which are only used on the server but not the client, or vice versa. Since I do not plan to distribute the server for self-hosting before the game reaches end-of-life, I would rather not have server-only code present in the client for script-kiddies to datamine. Doing runtime checks to see whether it's the client or server also has a performance impact, though it's likely not significant (especially if it's not checking every frame).

A similar situation I can imagine is multiplatform games. Code related to Steam achievements etc only needs to be in the Steam version, and code related to consoles only in the console versions.

Another one I can think of is game demos. Code that is only used in the full version of a game would best be omitted entirely from the demo version.

Describe the feature / enhancement and how it helps to overcome the problem or limitation

A solution would be support for ifdef-style preprocessor statements, allowing code to be excluded entirely from certain exports.

This could be expanded further into additional preprocessing features. Some ideas:

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

There would be a new syntax added for preprocessor statements. Script-wide operations such as minification would be configured in the export options. Preprocessor addons such as obfuscators would work like other editor addons.

C's syntax using # would not work because GDScript uses # for comments, so it'd have to use something else. Perhaps just replace the # with a symbol that isn't used, such as ?.

If this enhancement will not be used often, can it be worked around with a few lines of script?

For ifdef statements, you can use something like OS.has_feature, but the code is still there for dataminers to see. You could instead have multiple versions of the script and only include them in certain exports, but this is not simple to do. You could also use C++ or C# which support this already, but that's not a few lines of script, it's rewriting your entire project.

Is there a reason why this should be core and not an add-on in the asset library?

I'm not sure if this could be done with an addon, but if so I assume it would be very difficult. Perhaps you could run a script at export, but it would also have to work when running the project from the editor. And depending on how it works, the code editor might complain of syntax errors because of the preprocessor statements.

jtnicholl commented 1 year ago

Agh, I just realized #4234 exists, I don't know why I didn't see it the first time I searched. This is very similar, but a bit wider in scope, since that only includes ifdefs. I'm guessing it still counts as a duplicate though? Apologies if so.

Edit: Actually, now that I've read it again, they aren't so similar. That one is suggesting evaluating existing OS.get_feature calls at export seamlessly, while this is suggesting a full-on preprocessor system with a new syntax. We already have duplicates suggesting the same feature done in different ways (such as 7329 and 5130, which suggest adding structs as reference-types and as value-types respectively). Still, my bad for somehow missing it while searching.

dalexeev commented 1 year ago

Currently in 4.x GDScript does not support exporting compiled bytecode (*.gdc) unlike in 3.x. The source code is exported as is. First we need to resolve this issue, without this conditional compilation is not possible.

If the preprocessor is run in an editor, then GDScript will not be able to analyze the stripped code and show errors and warnings. If the preprocessor will not run in the editor, then the following code will cause a parser error:

#%ifdef debug
var a = 1
#%else
var a = 2
#%endif

Since the define directive is not mentioned in this proposal, you are only talking about conditional compilation, not about macros, as far as I understand.

I think there is no need for directives, regular ifs can also be analyzed and stripped by the compiler if they only contain constant expressions and/or calls like OS.has_feature() with constant arguments. Also we could add static if (or if constexpr or when) so you can be sure that the if will be resolved at compile time. The only problem is that the class body cannot contain ifs. godotengine/godot#26649 proposes version keyword.

Also I made an export plugin for 4.x: dalexeev/gdscript-preprocessor.

AlfishSoftware commented 1 year ago

I really like static if syntax for the directive, if it would work even at top-level code. It's quite intuitive.

AlfishSoftware commented 1 year ago

As for "nameof" feature, I would suggest a more generic @"some.code" + some.@"code" that:

You use it like this:

my_obj.emit(MyClass.@"my_method", arg1, arg2)
prints(@"ThisClass.this_method", @"some_value=", some_value)

which (if all identifiers are valid) compiles to:

my_obj.emit("my_method", arg1, arg2)
prints("ThisClass.this_method", "some_value=", some_value)
Anutrix commented 4 months ago

Another use case not mentioned in this ticket yet is when using platform-specific GDExtension-registered Classes/Nodes.

AllenDang commented 1 week ago

Another use case not mentioned in this ticket yet is when using platform-specific GDExtension-registered Classes/Nodes.

I second this, I'm writing a gdextension in rust and some functions are only available for iOS, currently it's impossible to use those functions in GDScript, always report cannot find specified functions.

Really need the conditional compilation directives.