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 a session number command line argument to distinguish multiple instances of Godot #8809

Open
aaronfranke opened this issue Jan 6, 2024 · 12 comments
Milestone

Comments

@aaronfranke
Copy link
Member

aaronfranke commented Jan 6, 2024

Describe the project you are working on

A multiplayer game where during development I launch multiple instances of Godot to launch the server and one or many clients. At the moment, we are using a hacky work-around where all instances try to be the server when they launch, so the first succeeds, and the others fail to bind to the port so they fail to start the server and instead become clients.

Describe the problem or limitation you are having in your project

When you select multiple instances from the Debug -> Run Multiple Instances menu, each of the sessions shows up in the debugger separately with a number:

Screenshot 2024-01-05 at 6 06 38 PM

However, there is no way to detect which is which in GDScript. I would like to be able to detect this number from GDScript so that I can check for session ID 2 and have it be the server (launch 1 -> only client, launch 2 -> client and server, launch 3 or 4 -> one server and the rest are clients).

Aside from solving the problem of fighting over the server, I would like to manually implement a system to use subfolders for config files, where session 1 stores in user://1/, session 2 stores in user://2/, etc. Currently all sessions I launch are using the same config files, which causes strange conflicts sometimes.

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

I propose passing the session number to each session, so that we can identify the session number from GDScript by reading the OS command line arguments:

func _ready():
	print(OS.get_cmdline_args())
Screenshot 2024-01-05 at 6 13 33 PM

Why start from 1? This matches what shows up in the Godot editor. The number 0 is probably better suited towards special uses, such as indicating --session-number was not defined, so this is being run standalone, not from the Godot editor.

Alternatively, we could allow customizing the arguments per instance. See proposal #522 and the PR godotengine/godot#65753. Or possibly both. I still need the ability to distinguish client sessions from each other, so with this solution I would give each unique number arguments anyway.

See also proposal #3357

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

https://github.com/aaronfranke/godot/tree/session-number

		List<String> instance_args;
		for (const String &E : args) {
			// List<> does not have a duplicate() function, so we have to do this.
			instance_args.push_back(E);
		}
		instance_args.push_back("--session-number");
		instance_args.push_back(itos(i + 1));
		Error err = OS::get_singleton()->create_instance(instance_args, &pid);

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

Not easily, Godot does not provide a way to inject different arguments per session. It only allows setting the amount of instances and set arguments that are passed to all sessions.

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

Yes, running multiple instances of Godot is a core feature.

@aaronfranke aaronfranke added this to the 4.x milestone Jan 6, 2024
@akyoto
Copy link

akyoto commented Jan 25, 2024

Running one or two instances shouldn't modify the session number of the first window.

From your examples it sounds like I'd get 0 for running one window, and 1 2 for running two windows.
In the client/server cases that I'm thinking of, the developer would want 0 and 0 1.
This lets you test client0 alone or client0 and client1 together.

So session numbers, imho, should start at 0.


Other than that I'm happy to see someone mentioning this problem and proposing a solution, I thought I was the only one running hacks like...

var instance_id := OS.get_process_id() % 4
var client_id := "client%d" % instance_id

...to put a different user account to each of the 4 connected clients.

@aaronfranke
Copy link
Member Author

@akyoto That is not at all what I'm proposing. If there is only one window run from the editor, it would get number 1. Godot would never give you 0. What I mentioned above is that in your own code that handles the number, you could use 0 to indicate that none was assigned, such as when being run standalone, not from the Godot editor.

IMO it would match user expectations for the session number to match the number in the editor (the alternative would be to change the debugger to use "Session 0" as the first session).

@akyoto
Copy link

akyoto commented Jan 27, 2024

Looking at userspace pseudo-code:

A.1) Starting IDs from 0 and omitting the parameter in single-window:

session_id := 0
is_multi_session := false
is_standalone := OS.has_feature("template")

for each arg:
	if arg is "--session-id":
		session_id = next
		is_multi_session = true

A.2) Starting IDs from 0 and including the parameter in single-window - this doesn't work because it misses multi-session information.

B.1) Starting IDs from 1 and omitting the parameter in single-window:

session_id := 1
is_multi_session := false
is_standalone := OS.has_feature("template")

for each arg:
	if arg is "--session-id":
		session_id = next
		is_multi_session = true

B.2) Starting IDs from 1 and including the parameter in single-window: - this also doesn't work because it misses multi-session information.


A.2 and B.2 also clutter the cmdline args in single window runs which I don't like.


The only difference here between A.1 and B.1 is the session_id variable initialization.
It boils down to 0 or 1 for the default state, and in IT assuming data is 0 by default seems more sane to me.

I suggest A.1.

@aaronfranke
Copy link
Member Author

At the end of the day I'm fine with either option, I just want to avoid confusion with it mismatching the debugger UI.

@akyoto
Copy link

akyoto commented Jan 28, 2024

Yea I see your point. I'm happy with either of those options, it would be an upgrade to the hacks we currently need.

@hello-adam
Copy link

hello-adam commented Feb 2, 2024

What if the command line arguments were just configurable for each instance that gets launched in the project settings somewhere? That way the default behavior wouldn't change in the unlikely(?) case that someone has a project that will react poorly to unexpected command line arguments. Also, it means you don't need to decide what the "right" default arguments are.

I'd be happy to try to implement this and do a PR if nobody's already on it.

(UPDATE: somebody was already on this and it was approved a few days ago - godotengine/godot#65753)

@aaronfranke
Copy link
Member Author

@hello-adam That makes sense. I'm also wondering if we should have both: make it configurable, but give session numbers by default. So the default command line arguments would be like --session-number 1, --session-number 2, etc, and users can replace this or add to it.

@hello-adam
Copy link

I'd definitely be able to work with default parameters, but I suspect the pull request would be easier for the maintainers to accept if it didn't change the default behavior of projects. Plus you avoid all the debate about what those parameters should be.

Maybe there could be a button/dropdown/menu that lets you quickly adopt some kind of multi-run preset like the one you describe? And those presets could be extended by editor plugins?

@aaronfranke
Copy link
Member Author

@hello-adam I think it's too simple to bother adding a preset feature like that. It would be better to not have a preset feature, and either provide the defaults I suggested above, or have it be blank by default.

@Infinixius
Copy link

A temporary solution that's worked for me when trying to separate servers from clients is using the pgrep command on Linux:

if OS.has_feature("editor"):
	var pid = OS.get_process_id()
	var pgrep_output = []
	var pgrep_exitcode = OS.execute("pgrep", ["-f", "remote-debug"], pgrep_output) # Get all running Godot instances
	
	pgrep_output = pgrep_output[0].split("\n")[0].replace("[", "")
	
	if str(pgrep_output) == str(pid):
		# We should become the dedicated server
	else:
		# We should become a client

Obviously not-portable, and kind of a hack, but it works for now.

@hello-adam
Copy link

hello-adam commented Feb 5, 2024

looks like there's already a PR that might resolve this in 4.3: godotengine/godot#65753

I'm an idiot, looks like you already covered all this @aaronfranke

@Eoin-ONeill-Yokai
Copy link

Is this PR I made ages ago related?

godotengine/godot#64913

If so I could just fix it up. I've been a bit busy lately but I think I could just polish it to cover your exact needs.

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