com.opengrave.og.MainThread.java Source code

Java tutorial

Introduction

Here is the source code for com.opengrave.og.MainThread.java

Source

/*
 * Copyright 2016 Nathan Howard
 * 
 * This file is part of OpenGrave
 * 
 * OpenGrave is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * OpenGrave is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with OpenGrave. If not, see <http://www.gnu.org/licenses/>.
 */
package com.opengrave.og;

import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.GL_TRUE;
import static org.lwjgl.system.MemoryUtil.NULL;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.util.ArrayList;
import java.util.Scanner;

import javax.swing.*;
import javax.swing.text.*;

import org.lwjgl.glfw.GLFWFramebufferSizeCallback;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL20;

import com.opengrave.common.*;
import com.opengrave.common.DebugWindowThread.DebugType;
import com.opengrave.common.config.Config;
import com.opengrave.common.event.DebugWindowTextInputEvent;
import com.opengrave.common.event.EventDispatcher;
import com.opengrave.common.packet.Packet;
import com.opengrave.common.xml.HGXMLThread;
import com.opengrave.og.base.Renderable;
import com.opengrave.og.input.InputMain;
import com.opengrave.og.light.CubeData;
import com.opengrave.og.resources.GUIXML;
import com.opengrave.og.resources.Resources;
import com.opengrave.og.states.BaseState;
import com.opengrave.og.states.LoadingState;
import com.opengrave.og.states.ProfileState;
import com.opengrave.og.states.waitables.PreMenuLoader;
import com.opengrave.og.util.Vector3f;

public class MainThread extends Thread implements MainThreadInterface {

    public static String USERNAME, PASSWORD, TOKEN, SERVERLIST;
    public static int hgver = -1, assetver = -1;
    public static boolean running;
    public static MainThread main;
    public static ProfileState profileState;
    private static ServerConnection serverConnectionData = null;

    private static BaseState state = null, nextState = null;

    public InputMain input;
    public static Config config;
    public static File cache;

    private static ArrayList<Runnable> toDoList = new ArrayList<Runnable>();

    public int fpsCap;

    private long lastCheckIn = 0;
    private long checkInInter = 1000 * 60 * 10; // Check in every 10 minutes
    private boolean needsConfigUpdate = false;
    private ModSession session;
    private GLFWFramebufferSizeCallback framebufferSizeCallback;
    // private SoundSystem soundSystem;
    public static long window;
    // private GLFWFramebufferSizeCallback framebufferSizeCallback;

    public static JFrame debugWindow;
    static JTextPane debugText;
    static AttributeSet errStyle, outStyle;
    static JTabbedPane debugTabs;
    public static Object debugLock = new Object();

    public static int lastW = 0;
    public static int lastH = 0;
    public static int SHADOWSIZE = 1024;

    public static PrintStream oldErr;

    public void run() {
        Thread.currentThread().setName("OpenGL and game");
        running = true;
        main = this;
        config = new Config("hg.config");

        // Attempt to get HG and assets build version for reporting bugs
        try (Scanner s = new Scanner(new File(cache, "hgjava.ver"))) {
            hgver = s.nextInt();
        } catch (FileNotFoundException e) {
        }
        try (Scanner s = new Scanner(new File(cache, "hgasset.ver"));) {
            assetver = s.nextInt();
        } catch (FileNotFoundException e) {
        }
        // Set FPS Cap
        if (!config.getBoolean("capfps", true)) {
            fpsCap = -1;
        } else {
            fpsCap = config.getInteger("fpslimit", 60);
        }
        initDebugWindow();
        // Start Event thread
        new LoadingThread();
        EventDispatcher.events.beginEventThread();
        // Prepare known packets for Client->GameServer and opposite
        Packet.init();
        // Prepare OpenGL.
        initGL();
        // initSound();
        // Prepare input handling
        input = new InputMain();
        // Set a first state. Prepare mods and then load menu
        changeState(new LoadingState(new PreMenuLoader()));
        startAuthClient();
        // And we're away!
        gameLoop();
    }

    /*
     * private void initSound() {
     * try {
     * SoundSystemConfig.setCodec("ogg", CodecJOgg.class);
     * } catch (SoundSystemException e) {
     * e.printStackTrace();
     * }
     * try {
     * soundSystem = new SoundSystem(LibraryLWJGLOpenAL.class);
     * soundSystem.switchLibrary(LibraryLWJGLOpenAL.class);
     * } catch (SoundSystemException e) {
     * e.printStackTrace();
     * }
     * 
     * // loadSoundFile("midi/test.mid");
     * // playSoundFile("midi/test.mid");
     * }
     * 
     * public void playSoundFile(String string) {
     * String label = string;
     * label = label.replaceAll("/[^a-zA-Z0-9]/", "");
     * soundSystem.play(label);
     * soundSystem.setVolume(label, 1f);
     * }
     * 
     * public void loadSoundFile(String file) {
     * String label = file;
     * label = label.replaceAll("/[^a-zA-Z0-9]/", "");
     * File f = new File(cache, file);
     * try {
     * soundSystem.newStreamingSource(true, file, new URL("file://" + f.getAbsolutePath()), label, true, 0, 0, 0, SoundSystemConfig.ATTENUATION_NONE, 0);
     * } catch (MalformedURLException e) {
     * e.printStackTrace();
     * }
     * }
     */

    private void startAuthClient() {
        HGXMLThread.requestAuthClient();
        HGXMLThread.authURL = SERVERLIST;
        Thread t = new Thread(new HGXMLThread(), "XML Communication Thread");
        t.start();
    }

    public void startApp(File cache, String userName, String passWord, String serverList) {
        USERNAME = userName;
        PASSWORD = passWord;
        SERVERLIST = serverList;
        MainThread.cache = cache;
        Resources.cache = cache;
        start();
    }

    public void checkIn() {
        long timeNow = System.currentTimeMillis();
        if (lastCheckIn + checkInInter < timeNow) {
            checkInNow();
        }
    }

    public void checkInNow() {
        long timeNow = System.currentTimeMillis();
        lastCheckIn = timeNow;
        HGXMLThread.requestClientCheckIn();
    }

    public void gameLoop() {
        Util.checkErr();
        double lasttime = getTime(), lastFPS = getTime();
        int fps = 0;
        while (running) {
            if (needsConfigUpdate) {
                createConfig();
                needsConfigUpdate = false;
            }
            synchronized (toDoList) {
                while (toDoList.size() > 0) {
                    Runnable runnable = toDoList.remove(0);
                    runnable.run();
                }
            }

            if (nextState != null) {

                // Something has caused us to switch states, stop the last
                // cleanly and start the next
                if (state != null) {
                    state.stop();
                    state.finalise();
                }

                state = nextState;
                state.prestart();
                state.start();

                nextState = null;
            }
            // Update FPS counter in window title TODO: Remove from window title
            // and place into a debug context ingame
            if (getTime() - lastFPS > 1000) {
                // use fps value
                fps = 0;
                lastFPS += 1000;
            }

            fps++;
            // Prepare time delta for updating graphics
            double time = getTime();
            float delta = (float) (time - lasttime); // TODO Change delta to double as a rule?
            lasttime = time;
            input.doEet(state, delta);
            Util.checkErr();
            state.updateGUI(delta);
            Util.checkErr();
            state.update(delta); // Pre-render update
            Util.checkErr();

            // Clear frame ready for next frame
            GL11.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
            GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_COLOR_BUFFER_BIT);
            GL11.glViewport(0, 0, lastW, lastH);

            set2D();
            state.renderGUI();
            if (glfwWindowShouldClose(window) == GL_TRUE) {
                running = false;
            }
            glfwPollEvents();
            // if (fpsCap > 10) {
            glfwSwapBuffers(window);
            // }
            Util.checkErr();

        }

        running = false;
        // soundSystem.cleanup();
        System.exit(1);
    }

    public static void set2D() {
        GL11.glDisable(GL11.GL_CULL_FACE);
        GL11.glEnable(GL11.GL_DEPTH_TEST);
        GL11.glEnable(GL11.GL_BLEND);
        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
        GL11.glDepthFunc(GL11.GL_LEQUAL);

    }

    protected void initGL() {
        if (glfwInit() != GLFW_TRUE) {
            System.exit(1);
        }
        glfwDefaultWindowHints();
        glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE);
        glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
        window = glfwCreateWindow(800, 600, "HiddenGrave", NULL, NULL);
        if (window == NULL) {
            System.exit(1);
        }
        glfwMakeContextCurrent(window);
        glfwSwapInterval(1); // TODO condig of vsync. Enable vsync
        GL.createCapabilities();
        glfwSetFramebufferSizeCallback(window, (framebufferSizeCallback = new GLFWFramebufferSizeCallback() {
            @Override
            public void invoke(long window, int width, int height) {
                onResize(width, height);
            }
        }));
        onResize(800, 600);
        // TODO Check all extensions. TEX 2D ARRAY, GLSL 130
        createConfig();

        Util.initMatrices();
        Renderable.init();
        GUIXML.init();

        // Prepare Lighting
        initLighting();
        // Default Values
        GL11.glClearColor(0.5f, 0.5f, 0.5f, 1.0f); // sets background to grey
        Util.checkErr();
        GL11.glClearDepth(1.0f); // clear depth buffer
        Util.checkErr();
        GL11.glEnable(GL11.GL_DEPTH_TEST); // Enables depth testing
        Util.checkErr();
        GL11.glDepthFunc(GL11.GL_LEQUAL); // sets the type of test to use for
        // depth testing
        GL11.glEnable(GL11.GL_BLEND);
        Resources.loadTextures(); // Reconsider positioning. Other than GUI
        // texture we could offset these in another
        // thread... Possibly?
        Resources.loadModels();
        Resources.loadFonts();

    }

    protected void onResize(int width, int height) {
        DisplayRes dr = DisplayRes.get();
        if (width != lastW || height != lastH) {
            lastH = height;
            lastW = width;
            Util.update2DProjection();
        }
    }

    public void createConfig() {
        boolean isSet = config.getBoolean("set", false);
        System.out.println("Running OpenGL : " + GL11.glGetString(GL11.GL_VERSION));

        String glsl = GL11.glGetString(GL20.GL_SHADING_LANGUAGE_VERSION).split(" ")[0];
        System.out.println(glsl + " " + Float.parseFloat(glsl));
        if (Float.parseFloat(glsl) < 1.299f) { // Stupid float accuracy. < 1.30
            System.out.println("This program requires GLSL 1.30 as an absolute minimum");
            System.exit(130);
        }
        if (!isSet) {
            int textures = GL11.glGetInteger(GL20.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS) - 3;
            System.out.println("Maximum spare textures (minus reserved) : " + textures);
            config.setBoolean("shadows", true);
            if (textures < 4) {
                System.out.println("Setting no point-light shadows");
                config.setInteger("lightCount", 0);
            } else if (textures < 8) {
                System.out.println("Setting 4 point-lights with shadows");
                config.setInteger("lightCount", 4);
            } else if (textures < 16) {
                System.out.println("Setting 8 point-lights with shadows");
                config.setInteger("lightCount", 8);
            } else {
                System.out.println("Setting 16 point-lights with shadows");
                // config.setInteger("lightCount", 16);
                // TODO Create lighting-more
                config.setInteger("lightCount", 8);
            }

            // TODO Check for GL 3.0 and turn off shadows/lightCount if below
            // if (!GLContext.getCapabilities().OpenGL30) {
            // System.out.println("This program does not support OpenGL versions before 3.0. Setting absolute minimum detail");
            // if (!isSet) {
            // // We'll assume the worst.
            // config.setInteger("lightCount", 0);
            // config.setBoolean("shadows", false);
            // }
            // }
            Resources.removeShadersWithLighting();
            config.setBoolean("set", true);
        }
    }

    private void initLighting() {
        CubeData.data.add(new CubeData(GL13.GL_TEXTURE_CUBE_MAP_POSITIVE_X, new Vector3f(1f, 0f, 0f),
                new Vector3f(0f, -1f, 0f)));
        CubeData.data.add(new CubeData(GL13.GL_TEXTURE_CUBE_MAP_NEGATIVE_X, new Vector3f(-1f, 0f, 0f),
                new Vector3f(0f, -1f, 0f)));
        CubeData.data.add(new CubeData(GL13.GL_TEXTURE_CUBE_MAP_POSITIVE_Y, new Vector3f(0f, 1f, 0f),
                new Vector3f(0f, 0f, 1f)));
        CubeData.data.add(new CubeData(GL13.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, new Vector3f(0f, -1f, 0f),
                new Vector3f(0f, 0f, -1f)));
        CubeData.data.add(new CubeData(GL13.GL_TEXTURE_CUBE_MAP_POSITIVE_Z, new Vector3f(0f, 0f, 1f),
                new Vector3f(0f, -1f, 0f)));
        CubeData.data.add(new CubeData(GL13.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, new Vector3f(0f, 0f, -1f),
                new Vector3f(0f, -1f, 0f)));

    }

    public static void changeState(BaseState state) {
        nextState = state;
    }

    public double getTime() {
        double f = (glfwGetTime() * 1000);

        return f;
    }

    public static BaseState getGameState() {
        return state;
    }

    public static void sendPacket(Packet packet) {
        getConnection().sendPacket(packet);
    }

    public static void changeServerConnection(ServerData sd) {
        // TODO Check assumption that
        // "We only run this code when the last connection is completely dead/closed"
        if (serverConnectionData != null) {
            serverConnectionData.killConnection();
        }
        serverConnectionData = new ServerConnection(sd.getIP(), sd.getPort(), sd.getId());
        // Don't connect yet. So far this is only information about how and
        // where to connect
        // We need to make sure we double check asset loading and mod loading
        // before we begin an attempted connect.
    }

    public static void startConnectionThread() {
        serverConnectionData.connect();
    }

    public static ServerConnection getConnection() {
        return serverConnectionData;
    }

    public void newConfig() {
        needsConfigUpdate = true;
    }

    public static void showDebugWindow(boolean b) {
        debugWindow.setVisible(b);
    }

    private void initDebugWindow() {
        debugWindow = new JFrame("HG Debug");

        debugText = new JTextPane();
        debugText.setEditable(false);
        StyleContext sc = StyleContext.getDefaultStyleContext();
        errStyle = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, Color.RED);
        outStyle = sc.addAttribute(SimpleAttributeSet.EMPTY, StyleConstants.Foreground, Color.BLUE);

        final JTextField input = new JTextField();
        input.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent arg0) {
                String text = input.getText();
                EventDispatcher.dispatchEvent(new DebugWindowTextInputEvent(text));
                input.setText("");
            }

        });
        JScrollPane scroll = new JScrollPane(debugText);
        JPanel panel = new JPanel(new BorderLayout());
        panel.add(scroll, BorderLayout.CENTER);
        debugTabs = new JTabbedPane();
        debugTabs.addTab("Debug", panel);
        debugWindow.add(debugTabs, BorderLayout.CENTER);
        debugWindow.add(input, BorderLayout.SOUTH);
        debugWindow.pack();
        debugWindow.setSize(400, 400);
        debugWindow.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        // TODO Also place debug into text file.
        try {
            PipedOutputStream pOut = new PipedOutputStream();
            PipedInputStream pIn = new PipedInputStream(pOut);

            PrintStream oldOut = System.out;
            System.setOut(new PrintStream(pOut, true));

            BufferedReader reader = new BufferedReader(new InputStreamReader(pIn));
            new DebugWindowThread(reader, oldOut, DebugType.OUT);
        } catch (IOException e) {
            new DebugExceptionHandler(e);
        }

        try {
            PipedOutputStream pErr = new PipedOutputStream();
            PipedInputStream pInErr = new PipedInputStream(pErr);

            oldErr = System.err;
            System.setErr(new PrintStream(pErr, true));

            BufferedReader readerErr = new BufferedReader(new InputStreamReader(pInErr));
            new DebugWindowThread(readerErr, oldErr, DebugType.ERROR);
        } catch (IOException e) {
            new DebugExceptionHandler(e);
        }
        Thread.setDefaultUncaughtExceptionHandler(new DebugUncaughtExceptionHandler());
        showDebugWindow(true);
    }

    public static void addDebugLine(String line, DebugType type) {
        AttributeSet x = outStyle;
        if (type == DebugType.ERROR) {
            x = errStyle;
        }
        synchronized (debugText) {
            StyledDocument doc = debugText.getStyledDocument();
            try {
                doc.insertString(doc.getLength(), line + "\n", x);
                debugText.setCaretPosition(doc.getLength());
            } catch (BadLocationException e) {
                new DebugExceptionHandler(e);

            }
        }

    }

    public static void addDebugException(DebugExceptionHandler handler) {
        synchronized (debugTabs) {
            debugTabs.addTab(handler.getName(), handler.makeGUI());
        }
    }

    public static void removeDebugException(DebugExceptionHandler handler) {
        synchronized (debugTabs) {
            debugTabs.remove(handler.makeGUI());
        }
    }

    /**
     * Used to push GL-context-only commands into the GL thread.
     */
    public static void addToGLCommands(Runnable run) {
        synchronized (toDoList) {
            toDoList.add(run);
        }
    }

    public static void newSession() {
        main.session = new ModSession();
    }

    public static ModSession getSession() {
        return main.session;
    }

}