Hi, I’m creating an implementation plan for my turn-based 2-player card game with an authoritative server. I am planning on using Netcode for GameObjects.
However, all the tutorials I’ve seen (even from great devs) seemed to combine server-side and client-side code constantly. I also couldn’t find any satisfying answers in existing threads on similar issues. I don’t obsess over ‘best practices’, but this looked like bad software engineering that would cause me pain in the future. I’m lazy and I want to prevent headaches.
- How to decouple server- and client-side code with Netcode for GameObjects?
Examples:
- Server side: “Player” is fully visible (including their pre-shuffled deck and other secret data)
- Client side: “BasePlayer” contains publicly visible information (how many cards a player has etc.) and is used to represent the opponent. “Player” extends “BasePlayer”, represents the connected client, and contains additional secret information such as what actual cards the client holds.
Side questions:
- Is using Netcode here the right thing to do?
- What about using a completely different architecture? (e.g. Django/Spring/Nodejs backend server for the game logic)
For question 3., I assume the main selling points regarding a Unity server are: out-of-the-box matchmaking, easy hosting with dynamic resources, ease of development.
Maybe it is easily possible and I just failed to see how. I’m new to this.
Thank you for sharing your insights.
For code and asset stripping, you can use the “Multiplayer Roles” feature provided by the Dedicated Server package.
But I guess your question is more about how to separate, say, a ServerRPC from a ClientRPC in code. This is not strictly possible because RPCs are sent to and received in the same component albeit on the “other” side.
What I prefer to do is to create two components, say ServerState and ClientState. The Server RPCs go in the server component and the client in the other. Now when a client needs to call a ServerRPC the client would do so via the ServerState component.
Technically, the client isn’t actually “calling” that method and the code that runs would only run on the server side, so the separation is there even though the client code depends on the ServerState component. The big benefit is that all server-only fields are contained in the server component, so you don’t actually run the risk of using a server-only field on the client or vice versa.
For a turn based game any realtime networking framework is somewhat overkill. Especially if the turns are long and all the data exchange occurs (or could occur) at the end of a turn. Quite commonly devs think they need to have a NetworkTransform for their board figures to animate them moving across the board but that is a waste of bandwidth. The actual instruction from a player is “move piece X to 4,6” and that gets sent around to everyone, so everyone can locally animate the motion of that piece - it does not need to be realtime-synchronized at all.
Very commonly turn based games are implemented via WebRequest. But it also depends on what tech you are familiar with and how much you want to lean into services.
That ought to answer #3 as well.
2 Likes