LWJGL in a multithreaded client-server application

Started by Tutzen, January 13, 2015, 12:08:22

Previous topic - Next topic

Tutzen

Greetings!

I'm an engineering student, currently developing a simple multiplayer game in Java for one of my modules. I'd like to use LWJGL for it instead of programming with Swing, but I'm running into problems as soon as I try to use the standard LWJGL Display. I don't have much code yet, only the basic underlying client-server structure (I'm not using any more libraries, so it's all done with ServerSockets and stuff), and what I want to do is to open up a display for every client that connected to the server. That display is created, opens up and I can even draw something like a rectangle on it, but as soon as I click on it (e. g. to drag the window or just focus it), it freezes and when I click it again, I'm getting the Windows-typical "no response" message and when I try to close the window it's saying "Java(TM) Platform SE binary is not responding" (I'm getting the error message in German, but I guess it's more or less like that). After that, the client crashes.

I guess the problem has something to do with my usage of multithreading, because when I try the put LWJGL code in a separate project, everything works fine. I'll include my code as an attachment and I'm very, very thankful for any kind of help you can provide.

Best regards!

Vapalus

That is not an lwjgl problem; you are just exhausting your computer with loops.
Try Thread.sleep(1) in your while-loops, so that all the other threads get enough space to run and the window can accept input.
After creating the correct othogonal matrices for the GUI and optimizing the RAM usage with a streaming engine, I usually fail to call the initialization function; As soon as that happens, you know I will search that stupid error for ten hours.

Tutzen

Thanks for your answer! I'm pretty sure that's not the problem, though. As far as I know, I need all those (3) while loops to create such an application and it should work fine. I tried inserting the sleep statement nonetheless, but to no effect. Also, when I use a simple Java swing window instead of a LWJGL display, it works fine. If the loops were the problem, I would have issues with that, too, wouldn't I?

Cornix

Sockets block if you try to read / write on them.
If you block your OpenGL thread in the update method nothing will ever be done. The Display will NOT poll any input except in the single frame that a message is received. The Display will NOT draw anything except in the single frame a message is received. The Display can NOT be closed except for the single frame a message is received.

This is a very bad architecture, you will need at least one additional thread on the client side to control the network communication.

Tutzen

Yeah, I won't consider myself a good programmer at all. I'm still learning and this is pretty much my first catch on client-server stuff.

However, thank you very much! I created a new class "Communication" now which implements Runnable and is started in the Client class which only does the OpenGL stuff now. It works this way, I can move the windows and the client-server communication is fine, too. I'd like to have the communication thing in the Client class and a separate "Graphics" class, though, just for structuring purposes. However, if I try that, I'm getting the "No OpenGL context found in the current thread" exception. Do you know how to fix this? I know why this occurs, but I don't really understand it 'cause I don't do any OpenGL stuff in any other class before I open a new Graphics thread.

You surely helped me a lot already. :)

Vapalus

AFAIK you need to initialize OpenGL in the same thread its running.

Even if the thread.sleep did not help, its always good to have one in there.
After creating the correct othogonal matrices for the GUI and optimizing the RAM usage with a streaming engine, I usually fail to call the initialization function; As soon as that happens, you know I will search that stupid error for ten hours.

Tutzen

Quote from: Vapalus on January 13, 2015, 14:58:38
AFAIK you need to initialize OpenGL in the same thread its running.

Even if the thread.sleep did not help, its always good to have one in there.
Okay, I'll keep that in mind. :)

Yeah, that's what I've found out while searching the Internet, too. Thing is, if I'm not completely blind, I did exactly that. I wrote the Graphics class and put the following code in there:

try {
     Display.setDisplayMode(new DisplayMode(640, 480));
     Display.setFullscreen(false);
     Display.setTitle("Client");
     Display.create();
} catch(LWJGLException e) {
     System.out.println("[ERROR:] LWJGL display could not be created.");
     System.out.println(e.getMessage());
     System.exit(0);
}


Plus the while loop with the update calls, of course. I don't use any other OpenGL commands in any other class. I then create a new Graphics thread in my Client class and call start(). Does OpenGL have problems with that or am I just doing something horribly wrong?

Cornix

A Class and a Thread are not the same thing. It doesnt matter in which class you do your OpenGL calls, the only thing that is important is that everything happens in the same thread.
Its pretty safe to say that you computer is not cheating you, if the computer says you did it wrong you probably did.

Tutzen

I'm aware of that. ;) So what I do is:

- Creating an instance of the Client class
- Starting a new thread of the Graphics class in the Client class
- Initialising the OpenGL context in the constructor of the Graphics class
- Calling the update method in the run() method of the Graphics class

I'd think that he context should be initialised in the same thread the update is called, but please correct me if I'm wrong. I'm absolutely new to multithreaded programming.

Cornix

You would have to show us the updated code, otherwise we cant tell you anything.
Just post it in a reply within
tags.

Tutzen

Sure :)

This is the relevant code from the new Client class:

private void run() throws Exception {
// initialisation of socket and streams
new Thread(new Graphics()).start();
// while loop getting input from the Server
}

public static void main(String[] args) {
     Client client = new Client();
          try {
               client.run();
          } catch(Exception e) {
               System.out.println("[ERROR:] Could not start client.");
               System.out.println(e.getMessage());
          }
}


And this everything that's in the Graphics class:

public Graphics() {
     try {
          Display.setDisplayMode(new DisplayMode(640, 480));
          Display.setFullscreen(false);
          Display.setTitle("Client");
          Display.create();
     } catch(LWJGLException e) {
          System.out.println("[ERROR:] LWJGL display could not be created.");
          System.out.println(e.getMessage());
          System.exit(0);
     }
}

private void run() throws Exception {
     while(!Display.isCloseRequested()) {
          while (Keyboard.next()) {
               if (!Keyboard.getEventKeyState()) {
                    if (Keyboard.getEventKey() == Keyboard.KEY_Q) {
                         Messages.handleClientMessage("CHATMSG1");
                    }
               }
          }
          Display.update();
          Display.sync(60);
     }
     Display.destroy();
}


I'm really sorry for any stupid questions with obvious solutions. Like I said, I'm still learning.

Cornix

You are mixing something up here.
You create the Graphics object in the Client classes run() method. This is done on the main Thread. For this reason the OpenGL context is created on the main Thread.
You then have another Thread to invoke the Graphics classes run() method, this is a different thread then the one where you created your Graphics.

The simplest solution would be to move the context generation code from the constructor to the run() method, like this:
public Graphics() {
}
 
private void run() throws Exception {
     try {
          Display.setDisplayMode(new DisplayMode(640, 480));
          Display.setFullscreen(false);
          Display.setTitle("Client");
          Display.create();
     } catch(LWJGLException e) {
          System.out.println("[ERROR:] LWJGL display could not be created.");
          System.out.println(e.getMessage());
          System.exit(0);
     }
     while(!Display.isCloseRequested()) {
          while (Keyboard.next()) {
               if (!Keyboard.getEventKeyState()) {
                    if (Keyboard.getEventKey() == Keyboard.KEY_Q) {
                         Messages.handleClientMessage("CHATMSG1");
                    }
               }
          }
          Display.update();
          Display.sync(60);
     }
     Display.destroy();
}


It is a very good idea in general to do as little as possible within a constructor. Constructors are evil and always like to mess things up every once in a while.

Tutzen

Okay, I did what you suggested and it works like a charm! Thank you!

It's a little hard to get all these things right when you start working with multithreading and all that stuff. Pretty confusing. But I understand what you mean and it makes sense. You really helped me here, not only with this specific problem, but also with understanding the solutions. :)