How Godot Handles Command-Line Arguments and Startup Options: A Deep Dive into the Engine Source
Godot parses command-line arguments in Main::setup() inside main/main.cpp, separating engine options from user arguments using -- or ++ separators, then stores them in the OS singleton for access via OS.get_cmdline_user_args() in GDScript.
The godotengine/godot repository implements a robust command-line argument handling system that bridges platform-specific entry points with high-level scripting APIs. Understanding how Godot processes startup options reveals how the engine distinguishes between internal configuration flags and custom user parameters passed to your game.
The Entry Point: From Platform Code to Main::setup()
Every Godot executable begins in a platform-specific entry point, such as platform/windows/godot_windows.cpp for Windows or the macOS main function. These entry points immediately forward the raw argc and argv parameters to the engine core.
The real processing begins in Main::setup() located in main/main.cpp. This function receives the raw argument count and vector, then begins transforming them into Godot's internal data structures.
Parsing Logic: Separating Engine and User Arguments
The Argument Collection Phase
Inside Main::setup(), Godot first collects all arguments into a List<String> args. This includes both the standard argv values and any platform-specific arguments retrieved from OS::get_cmdline_platform_args().
// main/main.cpp – building the args list
for (int i = 0; i < argc; i++) {
args.push_back(String::utf8(argv[i]));
}
for (const String &arg : platform_args) {
args.push_back(arg);
}
The Separator Mechanism (-- and ++)
Godot uses a specific parsing loop to distinguish between engine arguments and user arguments intended for the game itself. When the parser encounters -- or ++, it sets a flag called adding_user_args. From that point forward, every token is stored in a separate user_args list rather than being interpreted as an engine option.
// main/main.cpp – separator handling
else if (arg == "--" || arg == "++") {
adding_user_args = true;
} else {
main_args.push_back(arg);
}
This design allows developers to pass arguments to their game without Godot attempting to parse them as engine flags. For example, ./godot -- -level 3 passes -level 3 directly to the game script while -- itself is consumed by the engine.
Storage and Access: The OS Singleton
set_cmdline() Implementation
After parsing completes, Main::setup() stores the separated arguments in the OS singleton via OS::set_cmdline(). This method receives three parameters: the executable path, the engine arguments (main_args), and the user arguments (user_args).
OS::get_singleton()->set_cmdline(execpath, main_args, user_args);
Inside core/os/os.cpp, the implementation stores these in private members:
void OS::set_cmdline(const char *p_execpath,
const List<String> &p_args,
const List<String> &p_user_args) {
_execpath = String::utf8(p_execpath);
_cmdline = p_args;
_user_args = p_user_args;
}
Script API Exposure
Godot exposes user arguments to scripting languages through core/core_bind.cpp. The binding layer registers OS.get_cmdline_user_args() for GDScript, C#, and other supported languages.
// core/core_bind.cpp – binding declaration
ClassDB::bind_method(D_METHOD("get_cmdline_user_args"), &OS::get_cmdline_user_args);
GDScript can then access these arguments at runtime:
# my_script.gd
func _ready():
var args = OS.get_cmdline_user_args()
if args.size() > 0:
print("User arguments after '--': ", args)
else:
print("No extra user arguments supplied.")
Running ./godot -- -level 3 --name "Bob" would output:
User arguments after '--': ["-level", "3", "--name", "Bob"]
Special Handling: Help Output and Forwarding
Godot maintains a comprehensive help system in Main::print_help(), which enumerates every supported flag and indicates availability across different build types (editor, debug export, release export).
The engine also implements forwardable arguments through forwardable_cli_arguments. When the editor launches a game instance, it automatically passes certain flags (like --debug or --audio-driver) to the child process. This mechanism ensures that editor settings propagate to running projects without manual intervention.
// In Main::setup(), after parsing:
if (arg == "--audio-driver") {
forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(arg);
forwardable_cli_arguments[CLI_SCOPE_TOOL].push_back(N->get());
}
Summary
- Entry Point: Platform-specific code forwards
argc/argvtoMain::setup()inmain/main.cpp. - Parsing: Arguments are collected into a
List<String>, then processed in a loop that respects--or++separators to distinguish engine options from user arguments. - Storage:
OS::set_cmdline()incore/os/os.cppstores the executable path, engine arguments, and user arguments in the OS singleton. - Script Access:
core/core_bind.cppexposesOS.get_cmdline_user_args()to GDScript and other languages. - Forwarding: The editor uses
forwardable_cli_argumentsto pass specific flags to launched game instances.
Frequently Asked Questions
How do I access command-line arguments in GDScript?
Use OS.get_cmdline_user_args() to retrieve arguments passed after the -- or ++ separator. Arguments before the separator are consumed by the engine as startup options. For example, if you run ./godot -- -difficulty hard, calling OS.get_cmdline_user_args() returns an array containing ["-difficulty", "hard"].
What is the difference between -- and ++ separators in Godot?
Both -- and ++ serve identical purposes in main/main.cpp: they signal the parser to stop interpreting subsequent tokens as engine arguments and begin storing them as user arguments. The dual support provides flexibility for developers who may prefer one style over the other, but functionally they produce the same behavior in the argument parsing loop.
How does the Godot editor forward arguments to a running project?
When you run a project from the editor, Godot uses the forwardable_cli_arguments map to determine which flags should propagate to the child process. During parsing in Main::setup(), certain flags like --debug or --audio-driver are added to this map. Later, when the editor spawns the game instance, it appends these stored arguments to the command line, ensuring the running project inherits critical engine settings from the editor environment.
Where are command-line arguments stored internally in Godot?
After parsing in main/main.cpp, arguments are stored in the OS singleton via OS::set_cmdline() in core/os/os.cpp. The OS class maintains three private members: _execpath (the executable path), _cmdline (engine arguments), and _user_args (arguments after the separator). These lists persist for the application lifetime and are accessed by the scripting API through core/core_bind.cpp.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →