Requirements:
- It’s a card game. BP_Card is my card actor that the user sees visually on the screen. It has card game things like card art, card text, a cost, etc.
- There are 4 players, each player has a private Hand and a private Deck
- When players play cards on the board, they are revealed face up.
- PlayerA can see the cards in their own hand but not their own face down deck. All other players can’t see cards in PlayerA’s hand or deck.
- When PlayerA plays a card on the board, it is revealed face up to all players.
- The server only sends card face data to clients who should be physically seeing the face of the card at that point in gameplay. This is all to prevent people from hacking the game and seeing contents of face down cards.
- So an unrevealed card looks like this:
- Name - None
- Text - None
- Cost - None
- Card front art - None
- Card back art -
- And a revealed card will look l like this:
- Name -
- Text -
- Cost -
- Card front art -
- Card back art -
Here are a couple ways I can think of to do this. Architecture 1 i’ve done before and I know works. But i’m also considering Architecture 2 now. And who knows, maybe you folks can come up with a better way.
Architecture 1:
- Server spawns BP_Cards and replicates them to all clients.
- Note that for this game I need to turn off “replicate movement”.
- The actual card data is kept on the server only and only sent to the client if the card is revealed to that client.
- When the server wants to reveal a card to a client or clients, the server sends an RPC with card data. The client receives it and re-loads the BP_Card with the new information (name, text, art, cost, etc)
- I know this works, i’ve implemented it on a game last year and it worked great.
- When a single property changes on the card, I lean heavily on replication using RepNotify & OnRep functions to update the client.
- One downside to this architecture, is that the BP_card class ends up getting a big mixture of server only logic and client only logic, and even some mixed server & client logic. I found this to be the most complicated part, organizing it all in a clean way that was very quickly understandable so that when you look at a graph or a function you immediately know “Is that for server or a function or both?”
- Another downside to this architecture, I’m creating BP_Card class actors on the server which does feel bad. Because you’re really not supposed to create visual UI things on the server. At least that’s what I’ve read… Anyone have an opinion on this bit?
Architecture 2:
- Server does not spawn BP_Cards at all, only the clients spawn BP_Card actors.
- The server, instead, only views cards as cardDataStructs since that’s all the server cares about anyways.
- And since the clients are the only ones who care about the visual aspect of the cards, clients are the only ones spawning the actual BP_Card.
- Here is where this one gets tricky though.
- When a single property on the card changes, i’m not using replication so I cannot use the convenience of RepNotify & OnRep functions. I must instead take cardDataStruct and send it from the server to the client. And then the client needs to unpack it and re-load the card. While this makes sense at the time you’re first loading the card, this doesn’t make sense when you want to make changes to one property of the card. For example, let’s say you need to update the cost of a card and change it from a 3 to a 2. Or update the visibility of a property on the card. There’s no good way for me to do that with this Architecture because I’m not using replication & RepNotify. The easiest way for me to handle this is to just send the entire cardDataStruct again from the server to the client and have the client update the entire card again. So not just updating only the thing that’s changed, updating the entire card. Which feels really wasteful and inefficient. Keep in mind, my game’s cards will have way more properties than the ones listed above. Alternatively, I could implement some sort of single property update system with this architecture. Where I send only data to the client that needs updated. But this is extremely complex and annoying. Requires staying in sync, and diffing, and basically re-inventing replication which I don’t want to do. If I being to go down the path of re-inventing replication I’d rather just use Architecture 1.
Final thoughts:
I’m using Blueprints only FYI.
In my previous card game I made, I used Architecture 1 and it ended up working great and it was a perfect fit for that game because there were only 2 players and I even replicated movement of the cards (with a couple scene layers for offset to position the other player on the other side of the board). For this new game, I think Architecture 1 is less of a perfect fit but still seems fine. So I’m considering Architecture 2 but it seems worse so far.