# How Godot Handles Command-Line Arguments and Startup Options: A Deep Dive into the Engine Source

> Explore how Godot handles command-line arguments, detailing the source code in main.cpp and accessing user args via OS.get_cmdline_user_args() in GDScript.

- Repository: [Godot Engine/godot](https://github.com/godotengine/godot)
- Tags: deep-dive
- Published: 2026-02-26

---

**Godot parses command-line arguments in `Main::setup()` inside [`main/main.cpp`](https://github.com/godotengine/godot/blob/main/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`](https://github.com/godotengine/godot/blob/main/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`](https://github.com/godotengine/godot/blob/main/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()`.

```cpp
// 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.

```cpp
// 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`).

```cpp
OS::get_singleton()->set_cmdline(execpath, main_args, user_args);

```

Inside [`core/os/os.cpp`](https://github.com/godotengine/godot/blob/main/core/os/os.cpp), the implementation stores these in private members:

```cpp
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`](https://github.com/godotengine/godot/blob/main/core/core_bind.cpp). The binding layer registers `OS.get_cmdline_user_args()` for GDScript, C#, and other supported languages.

```cpp
// 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:

```gdscript

# 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.

```cpp
// 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/argv` to `Main::setup()` in [`main/main.cpp`](https://github.com/godotengine/godot/blob/main/main/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()` in [`core/os/os.cpp`](https://github.com/godotengine/godot/blob/main/core/os/os.cpp) stores the executable path, engine arguments, and user arguments in the OS singleton.
- **Script Access**: [`core/core_bind.cpp`](https://github.com/godotengine/godot/blob/main/core/core_bind.cpp) exposes `OS.get_cmdline_user_args()` to GDScript and other languages.
- **Forwarding**: The editor uses `forwardable_cli_arguments` to 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`](https://github.com/godotengine/godot/blob/main/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`](https://github.com/godotengine/godot/blob/main/main/main.cpp), arguments are stored in the OS singleton via `OS::set_cmdline()` in [`core/os/os.cpp`](https://github.com/godotengine/godot/blob/main/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`](https://github.com/godotengine/godot/blob/main/core/core_bind.cpp).