The Godot Q&A is currently undergoing maintenance!

Your ability to ask and answer questions is temporarily disabled. You can browse existing threads in read-only mode.

We are working on bringing this community platform back to its full functionality, stay tuned for updates.

godotengine.org | Twitter

+1 vote

Hi everyone,

I've just started to code an online multiplayer version for my game.
My pretty common concern is that the game should be playable both offline - it already is - and online, using Godot's High level multiplayer mechanisms.

I'll take the example of the server spawning a ghost at a random position (it's a pacman-like game), and transmitting it to 2 players across the network. Playing offline, the game should simply spawn the ghost.

After reading the doc and a bit of testing, I came up with three ways to go:

rpc only

rpc("spawn_ghost", random_position)

where the spawn_ghost method is defined as remotesync (thus called on both the server and the two clients).
This first method is perfectly fine when playing online, but won't work when playing locally (function is not called).

rpc + local call

rpc("spawn_ghost", random_position)
spawn_ghost(random_position)

where the spawn_ghost method is defined as remote or puppet (thus rpc-called on the two clients only, by the server).
This works and synchronises the three parties, but it raises debugger warnings - hundreds of them - about performing rpc() calls while not being online (seems legit).

rpc + local call, under conditions

if online:
  rpc("spawn_ghost", random_position)
else:
  spawn_ghost(random_position)

(where spawn_ghost is defined as remotesync)
This will work and not raise any debugger warning.

Solution?

This last solution is pretty cumbersome, because it has to be these same 4 lines of code in many many places in the code (for every synchronised action, that is, most of them).

Did I miss a simple way of calling remote/remotesync/puppet functions that works both online and offline, and is along the lines of how things should be done in Godot? (and, say, a maximum of two lines for each call)

Looking forward to your input, many thanks ind advance!

in Engine by (13 points)

in the Bomber demo that Callnou pointed out, it uses the rpc+local call as in your second example.
However the 2 functions (rpc and local) are defined as master and puppet, havent tried if this is what prevent the debugger to rais warning

puppet func stun():
    stunned = true

master func exploded(_by_who):
    if stunned:
        return
    rpc("stun") # Stun puppets
    stun() # Stun master - could use sync to do both at once

I believe master and puppet are only helpers to prevent one player from calling a function it shouldn't. They also clarify your code as to what methods should be rpc-called by whom.
But they don't prevent the debugger from raising warnings. ;)

2 Answers

+1 vote

You can still start a high-level multiplayer server even if there's only one player. This is what the Multiplayer Bomber demo does.

by (12,878 points)

Hi Calinou,
Thank you very much for your answer!

I think I'll go that way, as it allows for the one-liner rpc("spawn_ghost") if method is set to remotesync.

+1 vote

I have found a workaround for this:

func is_online():
    return get_tree().get_network_peer().get_connection_status() == NetworkedMultiplayerPeer.CONNECTION_CONNECTED

func nrpc(node: Node, name: String, args = []):
    """
    Call the `rpc` function on a node if the network is currently connected
    If the network is not connected we'll call the function name anyway
    """
    # Ensure array to simplify logic
    if typeof(args) != TYPE_ARRAY:
        args = [args]

    if not is_online():
        return funcref(node, name).call_funcv(args)
    else:
        return funcref(node, "rpc").call_funcv([name] + args)

Then you can use the following in your nodes, where Network is loaded as a Singleton.

remotesync func say(message: String):
    $VoiceLabel.text = message

remotesync func some_multiarg(message: String, name: String):
    $VoiceLabel.text = message + " " + name

func _input(event: InputEvent):
    if event.is_action_pressed("say_hello"):
        Network.nrpc(self, "say", "hello")

    if event.is_action_pressed("say_hello_to"):
        Network.nrpc(self, "say", ["hello", "some_user"])

See this issue for more information

by (28 points)
Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read Frequently asked questions and How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to [email protected] with your username.