I want to make a 3d card game with 2 players. For that I wanted to use the multiplayerSynchronizer and multiplayerSpawner to sync all the nodes, but I ran into a problem with my approach.
I created a hand, card and field scene.
Card is obviously just a 3d card with some data depending on what it can do.
Hand can take multiple cards and arrange them so it looks good.
Field can take multiple cards and arrange them so it looks good.
Then I got some game manager that is supposed to do all the logic (like card_clicked → gm → opens options → player input → gm → play card → check for response → …)
Now the first issue I ran into is that I made it so that the server creates both hands and each peer now sets the positions of these. Now the positions of the same hand instance is different for player 1 and 2 since I have like a 70° top-down view on the field and can’t just place the hand the same as the opponent sees it.
Is there any better way for this to work (so each card instance has the same position on each peer and just some transform on it?), it seems to make a lot of stuff like moving the card really annoying when they don’t have the same position in any way on the respective peers.
Would also appreciate any hints for multiplayer stuff in general. Thanks in advance!
I think if you need arbitrary positioning for the cards then you should stick with “real” transforms. It might be annoying, but I think it is ultimately less confusing than translating back and forth between transforms on different clients.
However, I wonder if you already have a system for organizing cards that’s more logical than position. Do the cards go into specific slots/squares/hexes/spaces on the board? Could you do your logic in that system instead and only convert to real transforms for visual purposes?
The only problem if facing is with the player hands right now. But I already have the idea how to fix it.
The issue was that the global positions on the board were the same between vlients, but the hand position differs. (Since the hand is turned so the cards face the players view and so I cant just let P1 see P2s hand as the opponent hand, imagine it like in mtg online) Then position sync and using transforms would be kind of hard, since if I play a card, for one player the card (in tge same hand-relative position) would move from lets say global (0,1,1,70°x) to global field pos and for the other player it ahould move from global (0,2,0.5,-30°) to the same pos. Would be hard and unintuitive to make any kind of tween like this, while only moving on the authority and syncing the movement.
In my new approach I just have a hand clone scene that is like a dummy hand, updated on any hand update. So I just have a hand visible for the player, and a dummy visible for the opponent for each hand. Can have 2 seperate positions/tweens for the card to field then without problems.
Atm I have a problem setting the client to be the authority of its hand, since its created on the server and the ready/syncing the autority are in wrong order (just for one of the 2 hands for some reason). Probably should let the client create his hands and sync them to the server, since id need some sketchy fixes otherwise.
EDIT: Seems like I should spawn everything from the server. Only problem I get is this:
E 0:00:06:687 on_replication_start: The MultiplayerSynchronizer at path “/root/Game/ZonesInCamera/SyncedHand2/HandSynchronizer” is unable to process the pending spawn since it has no network ID. This might happen when changing the multiplayer authority during the “_ready” callback. Make sure to only change the authority of multiplayer synchronizers during “_enter_tree” or the “_spawn_custom” callback of their multiplayer spawner.
<C++ Error> Condition “pending_sync_net_ids.is_empty()” is true. Returning: ERR_INVALID_DATA
<C++ Source> modules/multiplayer/scene_replication_interface.cpp:243 @ on_replication_start()
For the hands I create on the server, but give the client the authority. I get the error only on the client.
As the error says you are setting the authority at the wrong time, or before the multiplayer peer is created.
As a side note, if you plan for this to be wildly used and want to skirt players from cheating you should only allow the server/authority to make hands, and also not sync the value of your opponents hand to the user. Only sync the value when its played, or when appropriate. The only thing you allow the user is input for the moves they make.
Yea, found a fix for it in the forum, calling a function that sets the authority after _ready. Got me the next problem of the client not always knowing the authority_id (synced int) at this point in time.
Now I’m setting it in _process if it’s not set already… might be a sketchy workaround, but seems to work.
The server-side only creation of hands seems kind of useless for anti-cheat purposes if it’s a peer to peer connection, where the lobby-creator == server. Not sure in what extent anti-cheat is possible (easily) here.
That is true, i guess it depends on how much effort you want to put in. It would still make it harder for one peer to cheat. So if you are not a cheater you can easily host a game that wont support easy cheating from your opponent. And if the host is cheating you could avoid that host, and/or host your own game.
You could also make a dedicated server model and still support a “peer-to-peer” where one peer hosts the the dedicated server but is also given a client role when playing. (I.e. you branch your dedicated server in the scene tree.) Then if you wanted to support a true server in the middle approach you could easily do that by spinning up servers in the cloud, or a trusted host advertise a servers.