1

I've been following another java game development tutorial in order to get a basic understanding of threading, I have taken these tutorials and combined them with openGL programming that I have learnt previously. Except I think I may have missed something, because upon calling engine.stop() (this includes thread.join()), the window closes and the program hangs in the background, and you have to end its process.

Here is My Code:

package com.daboom.threadgl;

import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;

import com.daboom.threadgl.graphics.Camera;

public class EngineMain implements Runnable {

    private boolean running;
    private Thread thread;
    private Camera cam;


    /**
     * Starts the Dual Threaded System, does not need to be called, 
     * already called upon application beginning
     */
    @Override
    public void run() {
        initDisplay();
        long lastTime = System.nanoTime();
        long timer = System.currentTimeMillis();
        final double ns = 1000000000.0 / 60.0;
        double delta = 0;
        int frames = 0;
        int updates = 0;
        while (running) {
            long now = System.nanoTime();
            delta += (now - lastTime) / ns;
            lastTime = now;
            while (delta >= 1) {
                update();

                updates++;
                delta--;
            }

            render();
            if (Display.isCloseRequested())
                break;
            frames++;

            if (System.currentTimeMillis() - timer > 1000) {
                timer += 1000;
                System.out.println(updates + " u/sec " + frames + " f/sec");
                Display.setTitle(Game.gameName + " || " + updates + " u/sec " + frames
                        + " f/sec ||");
                updates = 0;
                frames = 0;
            }

        }
        stop();
    }

    private void update() {
        game.update();
    }

    Game game;

    private void render() {
        GL11.glLoadIdentity();
        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
        cam.useView();
        game.render3D();
        cam.setProjection2D(Display.getWidth(), Display.getHeight());
        game.render2D();
        Display.update();
        cam.resetTo3D();
    }

    public EngineMain() {
        cam = new Camera();
        game = new Game(this, cam);
    }

    private void initDisplay() {
        try {
            Display.setDisplayMode(new DisplayMode(800, 600));
            Display.setTitle(Game.gameName);
            Display.create();
        } catch (LWJGLException e) {
            e.printStackTrace();
        }

        GL11.glEnable(GL11.GL_BLEND);
        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
        GL11.glClearColor(0, 0, 0, 1);
        cam.setProjection3D(63,(float) Display.getWidth() / (float)Display.getHeight(), 0.3f,
                1000);


        try {
            Keyboard.create();
            Mouse.create();
        } catch (LWJGLException e) {
            e.printStackTrace();
        }
    }

    /**
     * Starts the Multi-Threading and the Game
     * 
     */
    public synchronized void start() {
        running = true;
        thread = new Thread(this, "Display");
        thread.start();
    }
    /**
     * Safely Stops the program
     * Call this to exit the game
     */
    public synchronized void stop() {
        running = false;
        Mouse.destroy();
        Keyboard.destroy();
        Display.destroy();

        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        EngineMain main = new EngineMain();
        main.start();
    }

}

When i use his tutorials, with out the modifications to use his code, the process ends properly.

I cannot for the life of me, identify the problem.

P.S if you need more comments, just ask.

EDIT: Here is the original code before modification

package com.thecherno.rain;

import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;

import javax.swing.JFrame;

import com.thecherno.rain.graphics.Screen;
import com.thecherno.rain.input.Keyboard;

public class Game extends Canvas implements Runnable {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    public static int width = 300;
    public static int height = width / 16 * 9;
    public static int scale = 3;
    public static String title = "Rain";
    private Thread thread;
    private JFrame frame;
    private Keyboard key;
    private boolean running = false;

    private Screen screen;

    private BufferedImage image = new BufferedImage(width, height,
            BufferedImage.TYPE_INT_RGB);
    private int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer())
            .getData();

    public Game() {
        Dimension size = new Dimension(width * scale, height * scale);
        setPreferredSize(size);
        screen = new Screen(width, height);
        frame = new JFrame();
        key = new Keyboard();
        addKeyListener(key);
    }

    public synchronized void start() {
        running = true;
        thread = new Thread(this, "Display");
        thread.start();
    }

    public synchronized void stop() {
        running = false;
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void run() {
        long lastTime = System.nanoTime();
        long timer = System.currentTimeMillis();
        final double ns = 1000000000.0 / 60.0;
        double delta = 0;
        int frames = 0;
        int updates = 0;
        while (running) {
            long now = System.nanoTime();
            delta += (now - lastTime) / ns;
            lastTime = now;
            while (delta >= 1) {
                update();
                updates++;
                delta--;
            }
            render();
            frames++;

            if (System.currentTimeMillis() - timer > 1000) {
                timer += 1000;
                System.out.println(updates + " u/sec " + frames + " f/sec");
                frame.setTitle(title + " | " + updates + " u/sec " + frames
                        + " f/sec");
                updates = 0;
                frames = 0;
            }

        }
        stop();
    }

    int x, y;

    public void update() {
        key.update();

        if (key.up)
            y--;
        if (key.down)
            y++;
        if (key.left)
            x--;
        if (key.right)
            x++;
    }

    public void render() {
        BufferStrategy bs = getBufferStrategy();
        if (bs == null) {
            createBufferStrategy(3);
            return;
        }
        screen.clear();
        screen.render(x, y);

        for (int i = 0; i < pixels.length; i++) {
            pixels[i] = screen.pixels[i];
        }

        Graphics g = bs.getDrawGraphics();
        g.fillRect(0, 0, getWidth(), getHeight());
        g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
        g.dispose();
        bs.show();
    }

    public static void main(String[] args) {
        Game game = new Game();
        game.frame.setResizable(false);
        game.frame.setTitle(Game.title);
        game.frame.add(game);
        game.frame.pack();
        game.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        game.frame.setLocationRelativeTo(null);
        game.frame.setVisible(true);

        game.start();
    }

}
Da_Boom
  • 65
  • 1
  • 6
  • 1
    I'm not a Java expert, but stop is in your case called from the thread itself. So basically the thread waits for itself to stop, which is probably not working. – BDL Mar 05 '15 at 13:40

2 Answers2

1

Probably your modifications caused some non-daemon thread to start. You can use the following method before and after your changes to see what threads are running

private static void dumpThreads() {
    Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
    for (Thread thread : threadSet) {
        System.out.println(String.format("threadName = '%s', daemon = %b",
                thread.getName(), thread.isDaemon()));
    }
}

Also see this: What is Daemon thread in Java?

Community
  • 1
  • 1
lbalazscs
  • 17,474
  • 7
  • 42
  • 50
  • Ok, this is strange.. i put your `dumpThreads()` method into the `stop()` function (right before `running` is set to false), and for some reason the program closes and the `stop()` function is never even called on the unmodified code, but yet the program exits properly, meanwhile my code `stop()` does get called and the program hangs after closing the window. – Da_Boom Mar 06 '15 at 02:02
  • Ok the only two different threads that arent running in my modified code are: `threadName = 'AWT-Windows', daemon = true` `threadName = 'Java2D Disposer', daemon = true` which both appear to be AWT related, so i dont think that is it – Da_Boom Mar 06 '15 at 02:26
  • but your code is useful for identifying what threads are still running, so ill remember that. – Da_Boom Mar 07 '15 at 10:52
0

OK, so i have figured it out... like BDL said, i cant call join while in the active thread, unfortunately, due to the unforgiving nature of LWJGL, it required a restructure of the main class.

For future reference (for me and anyone else) Here is the current code:

package com.daboom.threadgl;

import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import com.daboom.threadgl.graphics.Camera;

public class EngineMain implements Runnable {

    private boolean running;
    private Thread thread;
    private Camera cam;
    public boolean isLogicThreadRunning = false;

    /**
     * Starts the Dual Threaded System, does not need to be called, already
     */
    @Override
    public void run() {
        while (running) {
            update();
            Display.sync(60);
        }
    }

    private void update() {
        game.update();
    }

    Game game;

    private void render() {
        GL11.glLoadIdentity();
        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
        cam.useView();
        game.render3D();
        cam.setProjection2D(Display.getWidth(), Display.getHeight());
        game.render2D();
        Display.update();
        cam.resetTo3D();
    }

    public EngineMain() {

        cam = new Camera();
        game = new Game(this, cam);

    }

    private void initDisplay() {
        try {
            Display.setDisplayMode(new DisplayMode(800, 600));
            Display.setTitle(Game.gameName);
            Display.create();
        } catch (LWJGLException e) {
            e.printStackTrace();
        }

        GL11.glEnable(GL11.GL_BLEND);
        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
        GL11.glClearColor(0, 0, 0, 1);
        cam.setProjection3D(63,
                (float) Display.getWidth() / (float) Display.getHeight(), 0.3f,
                1000);

    }

    /**
     * Starts the Game engine
     * 
     */
    public synchronized void start() {
        running = true;
        if (Runtime.getRuntime().availableProcessors() > 1) {

            thread = new Thread(this, "Update");
            thread.start();
            isLogicThreadRunning = true;

        }
        else
        {
            isLogicThreadRunning = false;
            System.out.println("WARNING: SINGLE CORE SYSTEM DETECTED, FPS LIMITED TO 60");
        }
        initDisplay();
        gameLoop();
    }

    /**
     * Stops the program. Call this to exit the game
     */
    public synchronized void stop() {
        running = false;
        System.out.println(Thread.currentThread().getName());
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // dumpThreads();
    }

    public void gameLoop() {
        while (running) {
            if (!isLogicThreadRunning) {
                update();
                Display.sync(60);
            }

            render();
            if (Display.isCloseRequested())
                running = false;
        }
    }

    public static void main(String[] args) {
        EngineMain main = new EngineMain();
        main.start();
    }

}

Took forever to figure this out, and i still have no idea how the original code in the tutorial worked, but it appears that trying to implement LWJGL into it seems to break it, due to the fact that the openGL context is of a single-threaded nature.

Of course if I am doing something that isn't recommended or not right, please tell me, because I'm an amateur when it comes to threading. (actually, I'm still an amateur with openGL... i need a new PC to get the best/new features, sigh.. still stuck with 3.1)

Da_Boom
  • 65
  • 1
  • 6