[Solved] SpiderMonkey not sending/receiving UDP data correctly

Hi all,

I have created a client and server with SpiderMoney, so that testers can play around together.

However it seems that UDP data is not being received. TCP data (e.g. data about if you or someone else has been hit or damages) is fine, but UDP data (telling about position) appears to noe be working correctly.

When the client starts up, I get two errors:


These errors only seem to happen some of the time, and sometimes people can see each other.

What is causing this, and what could I do to fix it?

What version of JME are you running? How do you setup your server and your client?

Do the TestChatClient and TestChatServer apps when sending UDP?

Server and client are modified versions of TestChatServer and TestChatClient.
So, currently, the server just rebroadcasts location messages from the client.

Currently the client and the server are in the same project.

Can I see your server setup?

Note: some networking bugs have been fixed in alpha3.

I ran into a similar issue semi-recently in which my firewall (iptables) wasn’t allowing UDP packets to be received. Many firewalls need a separate rule to open a port to UDP and TCP.

Yeah, that’s true, too. That’s why I asked if the TestChatClient and TestChatServer were working… no answer on that, yet.

Yes, TestChatClient/Server works.

Here you go:

public class PlaneClient {


private Client client;
public static final String NAME = "PlaneStorm";
public static final int VERSION = 1;
public static final int PORT = 51101;
public static final int UDP_PORT = 51101;

public PlaneClient(String host) throws IOException {
    int connects = 0;
    initializeClasses();
    try{
    client = Network.connectToServer(NAME, VERSION,
            host, PORT, UDP_PORT);
    }catch (Exception ex){
        throw new IOException("Unable to connect to server at "
            + host + "!");
    }
    ChatHandler handler = new ChatHandler();
    client.addMessageListener(handler, PlaneMessage.class);
    client.addMessageListener(handler, PlaneData.class);
    client.addMessageListener(handler, Damage.class);
    client.start();
    while (!client.isConnected()){
        try {
            Thread.sleep(100);
        } catch (InterruptedException ex) {
           ex.printStackTrace();
        }
        connects++;
        if (connects > 100){
            throw new IOException("Unable to connect to server at "
            + host + "!");
        }
    }
    System.out.println("Connected to server!");
}

public static void initializeClasses() {
    // Doing it here means that the client code only needs to
    // call our initialize. 
    Serializer.registerClass(PlaneData.class);
    Serializer.registerClass(PlaneMessage.class);
    Serializer.registerClass(Damage.class);
}

private class ChatHandler implements MessageListener<Client> {
    @Override
    public void messageReceived(Client source, Message m) {
        if (m instanceof PlaneMessage){
            planeMessage(m);
        }else
        if (m instanceof PlaneData){
            planeData(m);
            System.err.println("PlaneData!");
        }else{
            System.err.println("Invalid message!");
        }
    }
}
public void planeData(Message m){
    PlaneData data = (PlaneData) m;
    if (Plane.ifExists(data.name)){
        Plane pl = Plane.getPlane(data.name);
        pl.health = data.health;
    }
    System.out.println("PData");
    if (data.name == null || PlaneClient.name == null){
        return;
    }
    if (data.name.equals(PlaneClient.name)){
        
        /*if (!Main.m.crashed){
        Main.m.health = data.health;
        }else{
            Main.m.health = 0;
        }*/
        Main.m.health = data.health;
        System.out.println("Data received! " + data.health);
    }else{
        System.out.println("Not me: " + data.name + " | " + PlaneClient.name);
    }
}

public void planeMessage(Message m){
    PlaneMessage chat = (PlaneMessage) m;

        //System.out.println("ReceivedLoc:" + chat.getLocation());
        String name = chat.getName();
        if (!name.equals(PlaneClient.name)){
        if (Plane.ifExists(name)){
            Plane pl = Plane.getPlane(name);
            pl.setLocation(chat.getLocation());
            pl.setRotation(chat.getRotation());
            System.out.println("Set rotation/location!");
        }else{
            if (!Plane.create.contains(name)){
                Plane.create.add(name);
            }
        }
        }
}

public void closeConnection(){
    client.close();
}

public static String name = "Bob" + ((int) (Math.random() * 1000));

public void sendPlaneData(){
    if (!started()) return;
    
    Vector3f location = Main.m.plane.getLocalTranslation();
    Quaternion rotation = Main.m.plane.getLocalRotation();
    //System.out.println("Sending: " + (location == null) + "" + (rotation == null));
    PlaneMessage chat = new PlaneMessage(name, location, rotation);
    chat.setReliable(true);
    
    client.send(chat);
}

public void sendData(){
    if (!started()) return;
    
    
    PlaneData chat = new PlaneData(name, Main.health);
    chat.setReliable(true);
    client.send(chat);
}

public boolean started(){
    if (!client.isStarted()){
        client.start();
        return false;
    }else{
        return true;
    }
}

public void sendDamage(Plane pl, Vector3f location){
    if (!started()) return;
    Damage dam = new Damage(pl.name, location);
    dam.setReliable(true);
    client.send(dam);
}
}

Server:

public class PlaneServer {
// Normally these and the initialized method would
// be in shared constants or something.

public static final String NAME = "PlaneStorm";
public static final int VERSION = 1;
public static final int PORT = 51101;
public static final int UDP_PORT = 51101;

public static void initializeClasses() {
    // Doing it here means that the client code only needs to
    // call our initialize. 
    Serializer.registerClass(PlaneMessage.class);
    Serializer.registerClass(PlaneData.class);
    Serializer.registerClass(Damage.class);
}
static Server server;
public static void main(String... args) throws Exception {
    initializeClasses();

    // Use this to test the client/server name version check
    server = Network.createServer(NAME, VERSION, PORT, UDP_PORT);
    server.start();

    ChatHandler handler = new ChatHandler();
    server.addMessageListener(handler, PlaneMessage.class);
    server.addMessageListener(handler, PlaneData.class);
    server.addMessageListener(handler, Damage.class);

    // Keep running basically forever
    while (server.isRunning()){
        Thread.sleep(500);
        update();
    }
}

private static class ChatHandler implements MessageListener<HostedConnection> {

    public ChatHandler() {
    }

    public void messageReceived(HostedConnection source, Message m) {
        if (m instanceof PlaneMessage) {
            planeMessage(source, m);
        }
        if (m instanceof PlaneData){
            System.out.println("PlaneData!");
            planeData(source, m);
        }
        if (m instanceof Damage){
            planeDamage(source, m);
        }
    }
}

public static void planeData(HostedConnection source, Message m){
    PlaneData data = (PlaneData) m;
    if (names.containsKey(data.name)){
        Player pl = names.get(data.name);
        if (data.health <= 0){
            pl.crashed = true;
            pl.health = 0;
        }
        if (data.health == 3000){
            pl.crashed = false;
            pl.health = 3000;
        }
    }
}

public static void planeDamage(HostedConnection source, Message m){
    Damage data = (Damage) m;
    if (names.containsKey(data.name)){
        Player pl = names.get(data.name);
        pl.health -=50;
    }
}

public static void planeMessage(HostedConnection source, Message m){
    PlaneMessage pl = (PlaneMessage) m;
            // Keep track of the name just in case we 
            // want to know it for some other reason later and it's
            // a good example of session data
            source.setAttribute("name", ((PlaneMessage) m).getName());

            //System.out.println("Broadcasting:" + m + "  reliable:" + m.isReliable());

            // Just rebroadcast... the reliable flag will stay the
            // same so if it came in on UDP it will go out on that too
            server.broadcast(m);
            if (!names.containsKey(pl.getName())){
                Player player = new Player(pl.getName());
            }else{
                Player pla = names.get(pl.getName());
                pla.location = pl.location;
                pla.rotation = pl.rotation.getRotationColumn(2);
            }

}

static HashMap<String, Player> names = new HashMap<String, Player>();

public static class Player{
    public Vector3f location;
    public Vector3f rotation; //Convert to quaternion when sending to client
    public String name;
    public double health = 3000;
    boolean crashed = false;
    int timeout = 500;
    
    public Player(String name){
        this.name = name;
        names.put(name, this);
    }
}

long ID = 0;
public class Plane{
    public Vector3f location;
    public Vector3f rotation; //Convert to quaternion when sending to client
    public String name;
    public double health = 3000;
    boolean crashed = false;
    
    public Plane(){
        name = "Watcher" + ID;
        float rand1 = (float) (2000f + (Math.random() * 6000f));
        float rand2 = (float) (2000f + (Math.random() * 6000f));
        Vector3f lo = new Vector3f(rand1, 700, rand2);
        this.location = lo;
    }
    
}

static int MAX_HEIGHT = 1000;

public static void update(){
    System.out.println("Updating");
        for (Player pl : names.values()){
            pl.timeout--;
            if (pl.timeout < 0){
                names.remove(pl.name);
            }
            try{
            if (pl.location.y > MAX_HEIGHT){
                pl.location.y = MAX_HEIGHT;
            }
            if (pl.health > 0){
            pl.health -= 1;
            }
            System.out.println("1: " + names.values().size());
            PlaneData send = 
                    new PlaneData(pl.name, pl.health);
            send.setReliable(true);
            server.broadcast(send);
            System.out.println("2: " + send.health);
            }catch (Exception e){
                //Exception thrown because location == null
            }
        }
    }

@Serializable
public static class PlaneMessage extends AbstractMessage {

    private String name;
    public Vector3f location;
    public Quaternion rotation;

    public PlaneMessage() {
    }

    public PlaneMessage(String name, Vector3f location, Quaternion rot) {
        setName(name);
        this.location = location;
        this.rotation = rot;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setLocation(Vector3f location){
        this.location = location;
    }
    
    public Vector3f getLocation(){
        return location;
    }
    
    public void setRotation(Quaternion quat){
        this.rotation = quat;
    }
    
    public Quaternion getRotation(){
        return rotation;
    }
}

@Serializable
public static class PlaneData extends AbstractMessage {

    public String name;
    public double health;

    public PlaneData(){
        
    }
    
    public PlaneData(String name, double health) {
        this.name = name;
        this.health = health;
    }
}

@Serializable
public static class Damage extends AbstractMessage {

    public String name;
    public Vector3f location;

    public Damage(){
        
    }
    
    public Damage(String name, Vector3f location) {
        this.name = name;
        this.location = location;
    }
}
}

Before you ask, this is more of a testing/prototype project, hence some messy code.

Initialize these after you create the server.

…and you probably want to upgrade to alpha3.

Alpha1 is the latest AFAIK.

I tried doing as you suggested, but same problem.

You see how you do those in a different order?

That’s your issue.

If you upgrade to alpha3 then you don’t even have to register them on the client.

Thanks, looks like this was the issue. Seems solved, so let’s call this solved unless I encounter some sort of issue.