0
\$\begingroup\$

I'm trying to write a 2D RTS game with maximum of 10 players. When server finish intializing each player receives start coordinates and game start time so each client will start processing ticks around the same time. When player moves he sends client tick with his position and direction to server. When server receives player movement it checks client's tick in packet. I don't know what to do in this types of scenarios:

  1. If client is ahead. Should I update current server state to the client tick? Should I allow client to be ahead?
  2. if client tick is the same as server I just check his data. If its correct I send it to the other players so they can simulate. What to do if it is different? Should I check if in range of previous states(tick - x, tick) data matches? Should I reconcile or just send to player "NO your are here and moving in this direction"
  3. What reconciliation actually means? Do I understand it correctly that I roll back to the state where player's data matches and then apply new input and recalculate to the current server tick?

My server and client loop looks almost the same:

    def run(self):

        while True:
            self.tickClock.tick(16)
            while not self.inqueue.empty():
                uuid, msg_type, msg_data = self.inqueue.get()
                self.handle_msg(uuid, msg_type, msg_data)
            self.apply_players_state(self.tick)
            self.player_group.update(self.tick)
            self.players_state_next(self.tick+2)
            self.tick += 1

I use pygame.clock.Clock() for fixed tick. self.inqueue is multiprocessing.Queue with received packets I want to process. In handle_msg I check type of packet and run corresponding function. For now it's just movement where I check the ticks and if coordinates match if so I update player's state for tick. apply_players_state() is used to make sure that player gets his rect and direction updated from map of states for current server tick. player_group.update() is just a pygame.sprite.Group where I move player based on his actuall coordinates and direction. player_state_next() is used to create a new item for each player for the tick given so I don't get keyError.

\$\endgroup\$

1 Answer 1

0
\$\begingroup\$

This is not a simple question to answer. Many papers have been written on the subjects at hand, and this answer can only address certain aspects. Please forgive my incompleteness.

Network delays round-trip-time(RTT) and propagation can be inconsistent.

Is cheating a concern?

If so, the server is the absolute authority and its position is the absolute truth and sent back to the client.

If not, validate the difference is possible, and then average the client and server positions, updating both with timing offsets.

RTS is unique in its timing requirements

Commands are issued rather slowly(in computer timing terms) and the results are not needed in absolute real-time.

Slow the server down to tick to say, 3-7 times per second, apply all of the enqueued commands in order, then run the actual tick repeatedly until it catches up to real-time, sending the data to the clients. This will give the client a position target adjustment close to the expected value.

This would require two server queue variables. One collecting, while the other replays.

The advantage is that all clients would experience the similar input delays (*1), 333.3 - 142.85 ms, while maintaining the real-time consistency across the clients.

The client side should queue and send requests at 2 - 3 times the processing rate. This will give time to fill the request buffer, and also further offset the phase and delay values.

Use TCP for input.

The target payload size is up to ~1000 bytes per packet.

For most game networking, I suggest using UDP, not in this case.

In this application, use TCP, also called reliable, as the transport method, if possible.

Given the slower update times, the reordering and retry delays associated with TCP, far outweigh the possibility of loss or mis-ordering of commands.

UDP responses are fine, just as long as they are given regularly.


*1: There is a phase offset error inherent in this fixed timing model that still disadvantages slower connections, but it minimizes the impact by guaranteeing the slower client a response chance within the same or next server tick.

\$\endgroup\$
2
  • \$\begingroup\$ I'm using "reliable" UDP as I implemented retransmission and acknowledgement both in client and in server. Do you still recommend using TCP in this case? Cheating is concern in my game as client could send packet which would basically teleport him or give him items. So in this case should I still slow server down? Honestly I don't see how I could not have actual state of the game and validate player's position. \$\endgroup\$
    – Mirula
    Commented Oct 17, 2023 at 9:51
  • \$\begingroup\$ The only TCP features missing are the out of order arrival and windowing. The input from the client should only be commands. The server runs the commands the same as the client. My suggestion was that the speed applied can vary between the client and server and still maintain a mostly consistent state. \$\endgroup\$
    – agone
    Commented Oct 18, 2023 at 23:29

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .