Class not found attempting to register

Hi all,

I am attempting to implement multiplayer into my game. I have copied TestChatClient.java and TestChatServer.java from JMETests into two different projects, in the attempt to make an actual client and server. The only modifications I have made are the ones to make them work independently.

The TestChatServer works fine. However, the TestChatClient always throws an error when starting up:

java.lang.RuntimeException: Class not found attempting to register:Registration[-45 = mygame.TestChatServer$ChatMessage, serializer=null]
at com.jme3.network.message.SerializerRegistrationsMessage$Registration.register(SerializerRegistrationsMessage.java:174)
at com.jme3.network.message.SerializerRegistrationsMessage.registerAll(SerializerRegistrationsMessage.java:138)
at com.jme3.network.service.serializer.ClientSerializerRegistrationsService.messageReceived(ClientSerializerRegistrationsService.java:67)
at com.jme3.network.service.serializer.ClientSerializerRegistrationsService.messageReceived(ClientSerializerRegistrationsService.java:49)
at com.jme3.network.base.MessageListenerRegistry.messageReceived(MessageListenerRegistry.java:73)
at com.jme3.network.base.DefaultClient.dispatch(DefaultClient.java:458)
at com.jme3.network.base.DefaultClient$Redispatch.messageReceived(DefaultClient.java:466)
at com.jme3.network.base.ConnectorAdapter.dispatch(ConnectorAdapter.java:132)
at com.jme3.network.base.ConnectorAdapter.run(ConnectorAdapter.java:174)
Caused by: java.lang.ClassNotFoundException: mygame.TestChatServer$ChatMessage
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at com.jme3.network.message.SerializerRegistrationsMessage$Registration.register(SerializerRegistrationsMessage.java:163)
... 8 more

The issue is related to TCP, as I can send and receive UDP messages fine, but I can only send (but not receive) TCP ones.

Here is my entire TestChatClient code:

public class TestChatClient extends JFrame {


private Client client;
private JEditorPane chatLog;
private StringBuilder chatMessages = new StringBuilder();
private JTextField nameField;
private JTextField messageField;

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 TestChatClient(String host) throws IOException {
    super("jME3 Test Chat Client - to:" + host);

    // Build out the UI       
    setDefaultCloseOperation(DISPOSE_ON_CLOSE);
    setSize(800, 600);

    chatLog = new JEditorPane();
    chatLog.setEditable(false);
    chatLog.setContentType("text/html");
    chatLog.setText("<html><body>");

    getContentPane().add(new JScrollPane(chatLog), "Center");

    // A crude form       
    JPanel p = new JPanel();
    p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
    p.add(new JLabel("Name:"));
    nameField = new JTextField(System.getProperty("user.name", "yourname"));
    Dimension d = nameField.getPreferredSize();
    nameField.setMaximumSize(new Dimension(120, d.height + 6));
    p.add(nameField);
    p.add(new JLabel("  Message:"));
    messageField = new JTextField();
    p.add(messageField);
    p.add(new JButton(new SendAction(true)));
    p.add(new JButton(new SendAction(false)));

    getContentPane().add(p, "South");

    client = Network.connectToServer(NAME, VERSION,
            host, PORT, UDP_PORT);
    client.addMessageListener(new ChatHandler(), ChatMessage.class);
    client.start();
}

public static String getString(Component owner, String title, String message, String initialValue) {
    return (String) JOptionPane.showInputDialog(owner, message, title, JOptionPane.PLAIN_MESSAGE,
            null, null, initialValue);
}

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

public static void main(String... args) throws Exception {
    initializeClasses();

    // Grab a host string from the user
    String s = getString(null, "Host Info", "Enter chat host:", "localhost");
    if (s == null) {
        System.out.println("User cancelled.");
        return;
    }

    TestChatClient test = new TestChatClient(s);
    test.setVisible(true);
}

private class ChatHandler implements MessageListener<Client> {

    public void messageReceived(Client source, Message m) {
        ChatMessage chat = (ChatMessage) m;

        System.out.println("Received:" + chat);

        // One of the least efficient ways to add text to a
        // JEditorPane
        chatMessages.append("<font color='#00a000'>" + (m.isReliable() ? "TCP" : "UDP") + "</font>");
        chatMessages.append(" -- <font color='#000080'><b>" + chat.getName() + "</b></font> : ");
        chatMessages.append(chat.getMessage());
        chatMessages.append("<br />");
        String s = "<html><body>" + chatMessages + "</body></html>";
        chatLog.setText(s);

        // Set selection to the end so that the scroll panel will scroll
        // down.
        chatLog.select(s.length(), s.length());
    }
}

@Serializable
public static class ChatMessage extends AbstractMessage {

    private String name;
    private String message;

    public ChatMessage() {
    }

    public ChatMessage(String name, String message) {
        setName(name);
        setMessage(message);
    }

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

    public String getName() {
        return name;
    }

    public void setMessage(String s) {
        this.message = s;
    }

    public String getMessage() {
        return message;
    }

    public String toString() {
        return name + ":" + message;
    }
}

private class SendAction extends AbstractAction {

    private boolean reliable;

    public SendAction(boolean reliable) {
        super(reliable ? "TCP" : "UDP");
        this.reliable = reliable;
    }

    public void actionPerformed(ActionEvent evt) {
        String name = nameField.getText();
        String message = messageField.getText();

        ChatMessage chat = new ChatMessage(name, message);
        chat.setReliable(reliable);
        System.out.println("Sending:" + chat);
        client.send(chat);
    }

    
}
}

For some reason, it appears it’s looking for the class mygame.TestChatServer$ChatMessage, rather than mygame.TestChatClient$ChatMessage, but I cannot find any code matching “ChatServer” in my code, so I do not know why it is looking for the incorrect class.

If you are going to split them in this way then you should have a third project with the shared code that they both depend on.

Because no matter what TestChatClient.ChatMessage and TestChatServer.ChatMessage will be different classes. You can force SpiderMonkey to treat them the same with a custom serializer ID but I warn you that you will regret it every day of your natural life.