How To Think About Multiplayer State

Godot Version

Godot 4.4

Question

Looking at various documentation, articles, and demos for Godot multiplayer, I’m left wondering how to logically think about multiplayer state. There’s plenty of examples of “just initialize a server, join a peer, sync these nodes/properties, and boom - working game”. A lot of examples seem to hinge on every player seeing the same screen - the scene tree is synchronized between players.

But what if I am looking to have different players see different views of the overall game? I’ve been thinking about a card game of sorts as an example. Suppose each player has a has “deck” and a “hand” of cards. Players still need to join a server lobby to start a game - that seems to be straightforward to do. But what I get hung up on is the how to think about, share, and ultimately render game state for each player.

Players obviously need to know about each out - how many other players, how many cards they hold and the card they play. But each player would not see each others hand, and further-more, while each player’s scene tree would be similar, but each would focus their own hand, not that of others.

In a single-player implementation, this seems relatively simple to do, I can have a scene that is the game view and manager, which would have a scene of the deck that consists of “card back” nodes and a scene of the player hand that consists of “card front” scenes that would be a sprite, a position, and would hold logic for what to do when played. A player hand scene or just mouse events could be used to interact with the various nodes, each of which would be responsible for itself and its effects. ie playing a “card front” would invoke code in it.

But in multiplayer, how does one think about the state? A lot of tutorials just utilize the MultiplayerSpawner, but i dont think that really suitable here? I’m leaning towards considering the state of the game and the client scene tree that the players see as distinct things, which goes against how i would implement them in single player where the sprites, position, as well as logic and behavior is baked in to the same nodes. Especially if theres interaction between players - eg player 1 plays a card that forces player 2 to discard a card - player 1 cannot see player 2’s card, but the clients (and the server?) need to have this back and forth to carry out the game logic across a global game state that different players have different insights into and different control over.

Overall, is there a good description of how to think about and approach a multiplayer game in Godot where the players each have a distinct view and only have knowledge of a part of the overall game state and can enter complex interactions between each other?

Instead of a separate “card back” scene you give the cards a front/back view. Flipped cards still have to store their value, so they have all the same variables and functionality. In mulitplayer you set the cards to show front/back based on if the client has ownership

set_showing_front(is_multiplayer_authority())

Somewhat similarly 3D multiplayer games use the same scene tree with all the same data between clients, the only thing that changes is the current camera based on who owns it

$Camera3D.current = is_multiplayer_authority()

You don’t need to change all the data, only how it’s viewed by each client.

1 Like

utilizing the multiplayer authority could work, but it seems bit clunky, especially if it has to conditionally gate all card behavior. I’ll try to elaborate on my point of confusion.

If i have a single base type of Card scene that represents a card it would end up holding:

  • card type
  • is flipped
  • various control/node2d elements used to render the card
  • attached scripts that would define its behavior/logic
  • multiplayer owner id?

In game, for the local player, there would be something like a PlayerDeck node that would hold face down cards and a PlayerHand node that would hold drawn cards, the act of drawing a card would toggle it face up, and the act of playing the card would invoke its specific script/logic.

But for remote players, thier Cards would not behave the same way. the local player can not interact with them, and they would likely be drawn differently (much smaller, since there could be many remote players). They would also occupy different paths im the scene tree which as far as i can tell some synchronization mechanisms dont like. That makes it sound having a singular Card scene that does everything introduces a lot of code that would be used in exclusive use cases, and potentially holding visual elements for both local and remote players leading to rather complex scripts.

I was thinking about representing cards separatly for local and remote players. Something like a Card scene that would be used when the client plays and have all the bells and whistles (front, back, maybe a flip shader, be draggable, can be in deck, in hand, on play area, etc) and a RemoteCard that would be basically just a small sprite of the card back, used to show how many cards a remote player is holding.

I would envision a scene tree like:
Root (node used to manage game creation)

  • Game (main client game view)
    • RemotePlayerGallery (container to see other players)
      • RemotePlayer1 (shows small icons of the deck and player hand - effectively counts, highlights current player)
        • RemoteDeckIcon
          • RemoteDeckCardCountLabel
        • RemoteHand
          • RemoteCardIcon
          • RemoteCardIcon, etc
      • RemotePlayer2, etc
    • PlayerDeck
      • Card
      • Card, etc
    • PlayerHand
      • Card
      • Card, etc
    • CardPlayArena
      • Card (currently played card)
      • Controls to commit play, undo, and other interactions

I think my disconnect is that both local and remote cards are really the same thing from the point of view of the game state - there are x players, each has y cards in thier deck and z cards in thier hand. But each player sees them in a different node structure. This led me to consider having some sort of GameState node that hold the actual game information and is sync’d and acted upon by clients and the various visuals clients see be derived from it. But this makes the visuals a lot more difficult to work with if the logic is decoupled into a completely different node.

In summary, i think that reusing the same scene type for local and remote players causes issues when showing local and remote players differently and leads to a having a lot of mutually exclusive logic coexisting in the nodes. But decoupling state and utilizing specific scenes for local vs remote players makes it major headache to implement logic since godot is structured for nodes to hold and manage thier own state.

Honestly I would just implement custom socket multiplayer for that scenario, it would be much easier and less stressful. Just make a simple C# console app act as a server, and let the Godot clients connect to it. To prevent cheating, the server would be the one to assign the cards and remember their state, and it would only send each client the info that they really need.

1 Like