I’m currently writing an unofficial multiplayer mod for the game Starsector. (great game btw! and a bargain for $10)
For simplicity* I’m making the game simulation(and fp) deterministic, syncing the peers, and transmitting just user-originated events using a lock-step model, with input delay appropriate to the latency between peers.
*(simplicity is of paramount importance because the entire mod is going to be bytecode injected, meaning there are practical limitations upon how much of the codebase I can interact with.)
To this end I created a prototype to test how well the lock-step model handles at various latencies. (using the tool TMnetsim as a latency proxy)
It seemed to work fine with low latencies, but exhibited weird data corruption at higher latencies.
After much hair pulling I determined it couldn’t possibly be my code, and wrote the below code as a sanity test to confirm TMnetsim is a useless piece of crap.
So 2 questions:
- The below code should work perfectly, right? (obviously with the relevant ports redirected)
- As TMnetsim appears to be a piece of unreliable crap, can anyone suggest a good tool for introducing semi-realistic latency between connections? (I could of course write one… but given the plethora of multiplayer games surely such tools already exist?!)
import java.io.IOException;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws IOException {
Socket s = new Socket("localhost", 9665);
new Thread(new SocketReader(s)).start();
new Thread(new SocketWriter(s)).start();
}
}
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
public static void main(String[] args) throws IOException {
Socket s = new ServerSocket(9765).accept();
new Thread(new SocketReader(s)).start();
new Thread(new SocketWriter(s)).start();
}
}
import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
class SocketReader implements Runnable {
Socket s;
public SocketReader(Socket s) {
this.s = s;
}
@Override
public void run() {
try {
DataInputStream dis = new DataInputStream(s.getInputStream());
int counter = 0;
while (true) {
int val = dis.readInt();
if (val != counter) {
throw new RuntimeException("stream corruption! Expected: " + counter + " received: " + val);
}
counter++;
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
class SocketWriter implements Runnable {
Socket s;
public SocketWriter(Socket s) {
this.s = s;
}
@Override
public void run() {
try {
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
int counter = 0;
while (true) {
dos.writeInt(counter);
counter++;
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}