Question on lockstep model on a RTS

Hi! the question is simple.
Considering the lockstep model, let’s suppose I have user A and B.

A wants to move a unit, so it sends the command to B
B receives the command and tells A that they will execute the command at step 5
A receives the message

the problem is, from the point of view of B, how do B know that A received the message? ok, I can say that A tells B that it received the message succesfully and the will move at step 5…but again…now how do A know that B received this last message?

see my point here? I hope I made myself clear.

thanks, bye!

Use TCP. Declare one player the server, the rest are clients. When a client wants to do something, it tells the server that it wants to do something. The server then broadcasts to all client that action in the next update. No client is allowed to proceed before it has received clearance (+ eventual input) from the server for that specific update to keep all clients in sync with the server. The server is always right. Trust in the server.

B is the server and A is the client, in this case.

the thing is, how does the server knows that the client received the approval?
if the client says, “hey, I received the approval, I’m going to move on next turn!” the how does the client know that
the server received this?

In a short term, If a pack of information is lost, then the game will be desync and I have no way to test that…

TCP guarantees that messages are received in order and resent when necessary. You’ll still have to account for a hard disconnect (WiFi interference, router restarts, etc). TCP will automatically notice resets after a certain timeout. You could make the clients send a heartbeat message every half second or so and have a 1 second timeout on the connections. If the connection is lost to a client, the server tells all clients to pause the game while it waits for the lost client(s) to reconnect. The disconnected client might therefore have missed the updates from the last second before the connection was timed out, so the server needs to store the data for updates that haven’t been confirmed by all clients through heartbeat messages. Then just wait for the client to catch up the last second and resume the game.

That is pretty much what WC3 does.

excellent, thanks for your advice!

So, I have another question!
I want to show you what’s the client sending to the server and viceversa:

the client sends an object called information, with a matrix of 6*5.
6 being the maximum number of possible orders in a single moment, and 5 representing
(0) action
(1) id
(2) row
(3) column
(4) step

let’s say for example, that server approves a move of the client, it will be like this:

(0) 1 = move
(1) 32
place is going to move:
(2) 4
(3) 5
execute this on this step:
(4) 12

problem is, even with this optimization, I mean, I trying to send as least information of a command possible…well
the game is still is low, should I suppose that the problem is somewhere else?

thanks!!

bye!!

Sending 65 ints (I assume) at 30Hz is just 65430 = 3,5kBs per second so I’m sure you aren’t bottlenecked by your network bandwidth. I’ll try to come with some suggestions.

I think your problem is that you have not enabled the TCP no-delay option. On each socket you create you should call setTCPNoDelay(true); on both the server and the client. Otherwise since you’re writing so few bytes each write it won’t immediately send them. Instead it will wait for a few hundred milliseconds so that it can merge the data into a single packet. Not really convenient for games.

It seems like your protocol has some flaws. There’s really no need to limit the number of updates per frame. I’d recommend that you do something like this instead: First send an object containing the number of following updates. Then send the all updates as separate objects (one per object) That way the client can read the first message, and then understand that it has to read X more update messages before it can proceed run another step. That way, if there are no updates in one frame, only the first small object is written, and when there is a burst of updates it’s still no problem.

I don’t see any reason for the step data in each message since it’s implied that that action is supposed to be done the next step. That should reduce the amount of delay you get and reduce the amount of data (not that it matters much though). Unless you have a really good reason for it I suggest you drop it.

it doesn’t do just 6 updates per frame, it does all the necessary updates per frame, the only thing is that it only sends 6 commands per frame because is the maximum number of commands a player can make at the same time (for example, you can select 6 units at the moment, and send them to do something)

I added the tcpnodelayoption and works faster, even so, is still very slow…
I’m going to explain in detail what I’m doing so you can help me…

every time the client gives a unit a new order, that order is stored on the Information object.

on the principal thread, client and server are always sending and receiving data, so, in this case, server receives this information object, processes it, and returns it back to the client…

this happens every step, is this causing the game to be slow? it’s very frustating you know…

Like I said, the problem doesn’t seem to be related to network bandwidth. It’s your code that’s behaving weirdly. What do you mean with “slow”?

there’s a lot of lag, I mean, the game works at an “good” speed for like 2 seconds and then it lags for some milliseconds…like so…

That’s not really enough information to draw any conclusions… You need to investigate it more closely yourself. And if you’re only getting a few milliseconds (<10 ms or so) how will that be noticeable to the player?

Quick aside for all interested in networking, the issue OP had is called the Two Generals Problem. TCP is a very complicated and smart way of solving it.

The last thing the OP needs is to get sucked down the rabbit hole of transaction coordination for his game protocol. TCP has the two generals problem, the protocol on top doesn’t, in uh, general. If you timestamp or otherwise uniquely identify messages then you don’t have to worry if you do end up duplicating the last message due to a lost ACK.

I give up, it works slow, and I don’t understand why, I mean, when I make a localhost test, it works smoothly, but when trying with another pc over lan, it lags a lot…

here is the code:

set_tcp_nodelay is done outside…

private void ReceiveData()
    {

        try{
                if(whoisthis == 1) //for server
                {
                    indata1 = new ObjectInputStream(sk.getInputStream());
                    
                    info_other1 = (Information) indata1.readObject();
                    ProcessInformation(info_other1); //process client info
                    
                    ProcessInformation(info); //process own info
                    
                   
                }
                else //for client
                {
                    indata1 = new ObjectInputStream(sk.getInputStream());
                    indata2 = new ObjectInputStream(sk.getInputStream());
                    
                    info_other1 = (Information) indata1.readObject();
                    info_other2 = (Information) indata2.readObject();
                    
                    ProcessInformation(info_other1);
                    ProcessInformation(info_other2);
                    
                    
                }
               }catch(IOException ioe){} catch(ClassNotFoundException css){System.out.println("nothing to do here");}
   
    }
private void SendData()
    {
        
         
        try{

              if(whoisthis == 2) //this is client
              {
                outdata1 = new ObjectOutputStream(sk.getOutputStream());
                
                outdata1.writeObject(info);
                info.CleanInformation();
                    
                info_other1.CleanInformation();
                info_other2.CleanInformation();
                
               
              }
              else //this is server
              {
                  outdata1 = new ObjectOutputStream(sk.getOutputStream());
                  outdata2 = new ObjectOutputStream(sk.getOutputStream());
                 
                  outdata1.writeObject(info); //send server info
                 
                  info.CleanInformation();
                    
                   outdata2.writeObject(info_other1); //send client info
                  info_other1.CleanInformation();
                  
              }

              
            }catch(IOException ioe){}
    }

class Information contains a matrix of 6x5 integers.

so, what am I doing wrong here?

thanks for your help.

bye

PD: I read the two generals problem…just…wow…

no possible solution?
am I doing something obviously wrong?

I don’t know… Make a simpler test then. Make a one-way file transferer or something like that.

Your not flushing streams by the looks. so a read could end up with a few bytes in a Buffer that sit there for 100s of ms before getting sent.