Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add conditional compilation directives to GDScript #7375

Open
jtnicholl opened this issue Jul 25, 2023 · 6 comments
Open

Add conditional compilation directives to GDScript #7375

jtnicholl opened this issue Jul 25, 2023 · 6 comments

Comments

@jtnicholl
Copy link

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:

  • An equivalent to C#'s nameof, which would help avoid typos when using methods such as Object.emit instead of Signal.emit.
  • Minification. Godot 4.0 lost the "compiled bytecode" export option, so I have seen some suggestions to strip comments, unneeded whitespace, etc on export (though I don't see a proper proposal). This could be part of the same system.
  • Extensibility via addon support. For example, there have been requests to add obfuscation on export (see Obfuscate GDScript in production export #4220), but they've been met with opposition. Making it easy to develop unofficial obfuscators as preprocessor extensions may be a good compromise.

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
Copy link
Author

jtnicholl commented Jul 25, 2023

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
Copy link
Member

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.

@dalexeev dalexeev changed the title Add preprocessing to GDScript Add conditional compilation directives to GDScript Jul 26, 2023
@AlfishSoftware
Copy link

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

@AlfishSoftware
Copy link

AlfishSoftware commented Aug 11, 2023

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

  • Evaluates to the exact quoted string after @ as a compile-time String or StringName constant.
  • The part before @ (if any) is not evaluated at runtime at all. It's only used for context.
  • The quoted part must be valid code (with valid identifiers) within the context before @. It can be partial code.
  • The script editor would use regular code (not string) syntax-coloring for the quoted part.
  • You get compiler errors if the code isn't valid and statically verifiable (e.g. on invalid identifiers).

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
Copy link

Anutrix commented Apr 30, 2024

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

@AllenDang
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants