godotengine / godotengine/godot-proposals

Add some features to `MultiplayerSpawner` so as to achieve writing a piece of code for both local play and multiplayer play

Open
#11,368 0 comments 2 reactions 0 assignees View on GitHub
topic:multiplayer
Dominant language
No language data
Stars
1.4k
Forks
102
PR merge metrics
PR metrics pending

Description

### Describe the project you are working on

A multiplayer game in which players control their role in turn.

### Describe the problem or limitation you are having in your project

1. I want all peers to spawn nodes under the same spawn_path but now they must rely on the server or anyone who has the only MultiplayerSpawner's authority to do the leading spawning. That's because the MultiplayerSpawner can only be authorized by one player at the same time and there can't be more than 1 MultiplayerSpawner which uses the same spawn_path and contains the same auto spawn elements.
2. When a player starts a game without being a server, I have to do a lot of work to let another player to join him halfway. For example, I have to remove all the nodes that need to be spawned on other peers and re-add them to the SceneTree. Furthermore, these nodes must call `request_ready()` to make the MultiplayerSpawner track them. It seems that a lot of work and cpu time can be avoided because they are just re-done again to let the MultiplayerSpawner track them.
3. When a player quits game for some reason, the spawns owned by him will be destroyed on other remote peers. It's kind of difficult to make these nodes retained unless do some hacking.

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

First, let's specify a term: **Authority**. It only makes sense when properties need to be synchronized. When used in conjunction with MultiplayerSynchronizer, only when the player who has the authority modifies certain properties of the spawns will the changes be synchronized to the remote peers. While for the player who doesn't have the authority, any modifications made to the properties of the spawns won't be synchronized to the remote peers.

I will describe 3 new features for the MultiplayerSpawner (for the sake of convenience, the term "_Spawner_" will be used hereinafter to replace it) .

- The first feature can be summarized as "ownership". Specifically, the authority of the _Spawner_ with the same path among all peers automatically belongs to the same player. The authority of the nodes (spawns) they generate also belongs to that player. When the spawns detect children entering the SceneTree, the authority can be propagated to the children, and these children can continue to propagate the authority to their future children. When the player who owns the authority of the _Spawner_ changes the object's owner_id, it is equivalent to giving its authority to the other player(s). It will automatically synchronize its own owner_id to the _Spawner_ with the same path on other peers and automatically change the authority of the spawns they have ever generated. This feature facilitates developers to develop games where multiple players take turns to control the same object.
- The second feature is that it supports multiple _Spawners_ to monitor the same `spawn_path` and the same auto-spawn nodes (elements in the Auto Spawn List). All that is needed is to set different `owner_id`s for multiple _Spawners_. In order to avoid conflicts among multiple _Spawners_, the controller should perhaps use node.set_multiplayer_authority(id) to set a controller for the node before calling add_child(node). In this way, only the _Spawner_ whose owner_id is equal to that id will copy the node to other remote peers. This feature allows all peers to generate spawns into the same parent container without relying on the centralized capability of the authorized peer.
- The third feature is that it supports seamless switching between the local (offline) state and the online state. When a player switches from the local state to the online state, the _Spawner_ can copy the spawns generated in the local state to other remote peers. When a player switches from the online state to the local state, the spawns generated by that player in the online state are allowed to be retained (especially retained on other remote peers). This feature can conveniently enable players to allow others to join an ongoing "local" game in the middle of it, without having to start a new game from the begining. It also facilitates developers to implement functions like AI taking over the dropped players, avoiding the problem that when a player drops out, the spawns within the scope of their control disappear and prevent other players from continuing the game.

There will be several new properties exposed to users, they are:

- owner_id: String = "NAN" # can be set to "NAN", "self", "0", a positive int string, or a negative int string
- sync_authority_to_peers: bool = true
- sync_authority_to_spawns: bool = true
- spawns_pass_authority: bool = true
- rename_function: Callable
- restore_name_disconnected: bool = true
- enum DISCONNECTION_BEHAVIOR
- disconnection_behavior: int = DISCONNECTION_BEHAVIOR.PASS_AUTHORITY_TO_SERVER
- expire_time_to_vote: float = INF

I will describe the new properties in detail:

- owner_id: String = "NAN":
```
## Store the ID of the player it belongs to (each player can obtain their player ID through
## multiplayer.get_unique_id()).
##
## - The default value is "NAN", which means it doesn't belong to any player, and any
## nodes generated in the spawn_path won't be recorded by the Spawner. It's equivalent
## to the Spwaner having no effect at all.
##
## - When it is set to "self", the authority of this Spawner belongs to the local (offline)
## player himself/herself. This is so that the Spawner can record the spawns generated
## when not online. When the player's state changes to the online state, the Spawner
## will automatically set the owner_id to the player ID, and rename itself and the previously
## generated spawns by adding a suffix ("_%s" % owner_id), and replicate itself on other
## remote peers, thus copying the spawns generated in the offline state to other remote peers
##
## WARNING In this case, developers need to pay attention to the way of referencing this
## Spawner to avoid being unable to find the node after it is automatically renamed.
## That is to say, don't use ways that refer to the original node name such as
## $MultiplayerSpawner, %MultiplayerSpawner, or get_node("MultiplayerSpawner") to
## reference the node after going online. It is recommended to complete the reference
## before going online, for example:
## @onready var spawner: MultiplayerSpawner = $MultiplayerSpawner
##
## - When it is set to "0", whenever any player adds spawns nodes under the spawn_path,
## this Spawner will automatically copy the spawns to other remote peers.
## The authority of this Spawner along with the spawns belongs to all players.
## WARNING Don't cause the infinite loop problem where replicas generate new replicas.
##
## - When it is set to a positive integer string, the authority of this Spawner
## will belong to the player whose player ID is equal to the owner_id. Only when this player
## (not others) adds spawns under the spawn_path will the Spawner copy them to other
## remote peers.
##
## - When it is set to a negative integer string, the authority of this Spawner
## belongs to all players whose player IDs are not equal to -owner_id. When the excluded
## player adds spawns under the spawn_path, they won't be copied to other remote peers
## by this Spawner; while when other players add spawns under the spawn_path, this
## Spawner will automatically copy the spawns to other remote peers.
##
## NOTICE The player who sets this value for the first time must be an offline player or the
## server. Later, if this value needs to be modified, for offline players, they can modify it
## arbitrarily; for online players, when the value is "NAN", only the server can modify it
## and synchronize it to other remote peers; in other cases, only the player whose player
## ID is equal to the owner_id can modify it (which is equivalent to handing over the
## authority to others). This also means that if the owner_id is equal to "0" or a negative
## integer string, no one can change the owner_id on their own. In this case, the
## modification can only be successful when no less than half of the players with
## permissions request to set the owner_id to a common value. This "voting" process may
## need to draw on some ideas of the decentralization in blockchain.
##
## ALERT If multiple Spawners with the same owner_id, the same spawn_path,
## and the same elements in the auto spawn list are used simultaneously in a scene,
## it will cause repeated replication of spawns and other unpredictable consequences!
## Developers need to avoid this situation from happening.
##
## Only the authority owner has the right to modify this attribute, and after modification,
## it will be automatically synchronized to the remote peers.
```

- sync_authority_to_peers: bool = true:
```
## Whether to synchronize the owner_id to other remote peers or not.
##
## Only the authority owner has the right to modify this attribute, and after modification,
## it will be automatically synchronized to the remote peers.
```

- sync_authority_to_spawns: bool = true:
```
## Whether to automatically set the controller of the spawning node or not.
##
## Only the authority owner has the right to modify this attribute, and after modification,
## it will be automatically synchronized to the remote peers.
```

- spawns_pass_authority: bool = true:
```
## Whether the spawning node continues to pass the controller to descendant nodes
## (only when they enter the scene tree).
##
## Only the authority owner has the right to modify this attribute, and after modification,
## it will be automatically synchronized to the remote peers.
```

- rename_function: Callable:
```
## When the owner_id is "self", then player turns from local to online, it is used. Provide
## a custom function that returns the name of the new node. It needs to receive a
## name: String parameter and return a new string. When not provided, the default way
## will be used: add a suffix to the original name: "_%s" % owner_id. For example:
## func(p_name: String): return p_name + "_%s" % owner_id
##
## Only the authority owner has the right to modify this attribute, and after modification,
## it will be automatically synchronized to the remote peers.
##
## But it seems that Callable cannot be passed through net, so this is a problem here.
```

- restore_name_disconnected: bool = true:
```
## When the owner_id is "self" originally and connect to others and disconnect again,
## it is used. Whether to restore the origin name of the spawns.
##
## Only the authority owner has the right to modify this attribute, and after modification,
## it will be automatically synchronized to the remote peers.
```

- enum DISCONNECTION_BEHAVIOR:
```
## behavior when my peer disconnecting
enum DISCONNECTION_BEHAVIOR {
PASS_AUTHORITY_TO_SERVER = 1, ## Keep my spawns on the remote peers, and
## pass the authority to server. Will regress
## to `PASS_AUTHORITY_TO_ALL` if there is no
## server.

PASS_AUTHORITY_TO_ALL = 0, ## Keep my spawns on the remote peers, and
## pass the authority to all peers, so
## the owner_id will be 0.

KEEP_AUTHORITY = -1, ## Keep my spawns on the remote peers, and
## the authority remains mine.

DESTORY_SPAWNS = -2, ## Destroy my spawns on the remote peers and
## no need to deal with the authority.
}
```

- disconnection_behavior: int = DISCONNECTION_BEHAVIOR.PASS_AUTHORITY_TO_SERVER:
```
## When the connection is lost, how to handle the spawned entities on remote peers that
## belong to the permissions of this machine.
## Value range: Types defined in DISCONNECTION_BEHAVIOR, or a positive integer
## representing a certain player ID. If this player is in an offline state, then
## PASS_AUTHORITY_TO_SERVER and PASS_AUTHORITY_TO_ALL will be tried in turn.
##
## Only the authority owner has the right to modify this attribute, and after modification,
## it will be automatically synchronized to the remote peers.
```

- expire_time_to_vote: float = INF:
```
## The time limit set for voters when setting a new owner_id, with the unit being seconds.
##
## Only the authority owner has the right to modify this attribute, and after modification,
## it will be automatically synchronized to the remote peers.
```

To toggle these properties can make the MultiplayerSpawner behave like it does now, maybe.
Or we can add a new class that can achieve these features, eg. MultiplayerOwnedSynchronizer.
I guess MultiplayerSynchronizer also needs some modifications to work together with the new Spawner.

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

With the new Spawner, we can make the best use of P2P strength and decrease the server's burden if developers want to.
Developers can use less code to let the Spawner take care of the authority of every peer's spawning nodes.
Developers can use less code to let other peers take over their spawning nodes.
Developers can use less code to make a scene suitable for local play and multiplayer play by adding a new Spawner
to their scene and set owner_id to "self".
It's not a silver bullet for solving all the multiplayer problems, I admit, however there are still many simple requests this Spawner can help with.

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

I tried but failed. I tried to do some hacking by overtaking the signal bound inside of MultiplayerSpawns but it can not solve all the problems I mentioned above.

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

This can improve Godot's multiplayer game developing ability because it's handy, easy.
With the popularization of IPv6, games with light-P2P gameplay (games with small multiplayer ideas) will become more and more popular.
Godot should grab this chance to attract more developers.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.