org.craftmania.game.Game.java Source code

Java tutorial

Introduction

Here is the source code for org.craftmania.game.Game.java

Source

/*******************************************************************************
 * Copyright 2012 Martijn Courteaux <martijn.courteaux@skynet.be>
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package org.craftmania.game;

import static org.lwjgl.opengl.GL11.*;

import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.craftmania.blocks.BlockManager;
import org.craftmania.blocks.BlockXMLLoader;
import org.craftmania.game.PerformanceMonitor.Operation;
import org.craftmania.items.ItemXMLLoader;
import org.craftmania.math.MathHelper;
import org.craftmania.math.Vec3f;
import org.craftmania.recipes.RecipeManager;
import org.craftmania.rendering.BufferManager;
import org.craftmania.rendering.GLFont;
import org.craftmania.rendering.GLUtils;
import org.craftmania.world.World;
import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
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 org.lwjgl.opengl.NVFogDistance;

public class Game {
    private World _world;
    private int _fps;
    private float _step;
    private int _sleepTimeMillis;
    private Configuration _configuration;
    private static Game __instance;
    private int[] _fpsDataBuffer;
    private float _averageFPS;
    private boolean shouldQuitNextTick;

    public static boolean RENDER_INFORMATION_OVERLAY = false;

    private Game() {
        shouldQuitNextTick = false;
    }

    public static Game getInstance() {
        if (__instance == null) {
            __instance = new Game();
        }
        return __instance;
    }

    /**
     * Set the display mode to be used
     * 
     * @param width
     *            The width of the display required
     * @param height
     *            The height of the display required
     * @param fullscreen
     *            True if we want fullscreen mode
     */
    public void setDisplayMode(int width, int height, boolean fullscreen) {

        // return if requested DisplayMode is already set
        if ((Display.getDisplayMode().getWidth() == width) && (Display.getDisplayMode().getHeight() == height)
                && (Display.isFullscreen() == fullscreen)) {
            return;
        }

        try {
            DisplayMode targetDisplayMode = null;

            if (fullscreen) {
                DisplayMode[] modes = Display.getAvailableDisplayModes();
                int freq = 0;

                for (int i = 0; i < modes.length; i++) {
                    DisplayMode current = modes[i];
                    if ((current.getWidth() == width) && (current.getHeight() == height)) {
                        if ((targetDisplayMode == null) || (current.getFrequency() >= freq)) {
                            if ((targetDisplayMode == null)
                                    || (current.getBitsPerPixel() > targetDisplayMode.getBitsPerPixel())) {
                                targetDisplayMode = current;
                                freq = targetDisplayMode.getFrequency();
                            }
                        }

                        // if we've found a match for bpp and frequence against
                        // the
                        // original display mode then it's probably best to go
                        // for this one
                        // since it's most likely compatible with the monitor
                        if ((current.getBitsPerPixel() == Display.getDesktopDisplayMode().getBitsPerPixel())
                                && (current.getFrequency() == Display.getDesktopDisplayMode().getFrequency())) {
                            targetDisplayMode = current;
                            break;
                        }
                    }
                }
            } else {
                targetDisplayMode = new DisplayMode(width, height);
            }

            if (targetDisplayMode == null) {
                System.out.println("Failed to find value mode: " + width + "x" + height + " fs=" + fullscreen);
                return;
            }
            Display.setDisplayMode(targetDisplayMode);
            Display.setFullscreen(fullscreen);

        } catch (LWJGLException e) {
            System.out.println("Unable to setup mode " + width + "x" + height + " fullscreen=" + fullscreen + e);
        }
    }

    public void init() throws IOException {
        loadConfiguration();
        this._fpsDataBuffer = new int[16];
        this._fps = _configuration.getFPS();
        try {
            Display.setTitle("CraftMania");

            setDisplayMode(_configuration.getWidth(), _configuration.getHeight(), _configuration.isFullscreen());
            if (_configuration.getVSync()) {
                Display.setVSyncEnabled(true);
            }
            Display.create();
        } catch (LWJGLException e) {
            e.printStackTrace();
            System.exit(0);
        }

        System.out.println("LWJGL Version: " + Sys.getVersion());
        System.out.println("GPU: " + Display.getAdapter());

        initOpenGL();
        loadTextures();
        loadFonts();
        loadItems();
        loadBlocks();
        RecipeManager.getInstance().loadRecipes();
        loadSpecialStuff();
        Mouse.setGrabbed(true);
    }

    private void loadSpecialStuff() {
        try {
            Class.forName("org.craftmania.rendering.particles.Smoke");
        } catch (Exception e) {
            // TODO: handle exception
        }
    }

    public void initOpenGL() throws IOException {
        // init OpenGL
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrtho(0, 800, 600, 0, 1, 300);
        glMatrixMode(GL_MODELVIEW);

        GL11.glShadeModel(GL11.GL_SMOOTH);

        Vec3f fog = _configuration.getFogColor();

        glClearColor(fog.x(), fog.y(), fog.z(), 1.0f);
        glEnable(GL_TEXTURE_2D);
        glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

        // glEnable(GL_DEPTH_TEST);
        // glDepthFunc(GL_ALWAYS);

        glEnable(GL_CULL_FACE);

        glEnable(GL_FOG);
        glFog(GL_FOG_COLOR, GLUtils.wrapDirect(fog.x(), fog.y(), fog.z(), 1.0f));
        glFogi(GL_FOG_MODE, GL11.GL_LINEAR);
        glFogf(GL_FOG_START, _configuration.getViewingDistance() * 0.55f);
        glFogf(GL_FOG_END, _configuration.getViewingDistance());
        glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, NVFogDistance.GL_EYE_RADIAL_NV);
        glHint(GL_FOG_HINT, GL_NICEST);

        System.out.println("VBO Supported: " + GLUtils.isVBOSupported());

        /* Instantiate the BufferManager */
        BufferManager.getInstance();
    }

    public float getFPS() {
        return _fps;
    }

    public float getStep() {
        return _step;
    }

    public void update() {
        if (_world != null) {
            _world.update();
        }
        BufferManager.getInstance().deleteQueuedBuffers();
    }

    public void render() {
        // Clear the screen and depth buffer
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        if (_world != null) {
            _world.render();
        }

        if (RENDER_INFORMATION_OVERLAY)
            renderOnScreenInfo();
    }

    private void renderOnScreenInfo() {
        GLFont infoFont = FontStorage.getFont("Monospaced_20");
        glColor3f(1, 1, 1);

        /* Top Left Info */
        infoFont.print(4, _configuration.getHeight() - 20, String.format("FPS: %5.1f", getAverageFPS()));
        infoFont.print(4, _configuration.getHeight() - 20 - 15,
                "Sleeping: " + String.format("%4d", Game.getInstance().getSleepTime()));
        infoFont.print(4, _configuration.getHeight() - 20 - 30,
                "Heap Size: " + MathHelper.bytesToMagaBytes(Runtime.getRuntime().totalMemory()) + " MB");
        infoFont.print(4, _configuration.getHeight() - 20 - 45, "Heap Use:  " + MathHelper
                .bytesToMagaBytes(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) + " MB");
        infoFont.print(4, _configuration.getHeight() - 20 - 60,
                "Buffers: " + BufferManager.getInstance().getAliveBuffers());

        Operation[] ops = Operation.values();
        for (int i = 0; i < ops.length; ++i) {
            String name = ops[i].name();
            infoFont.print(_configuration.getWidth() - 200, _configuration.getHeight() - 20 - 15 * i,
                    String.format("%-20s %7.3f", name, PerformanceMonitor.getInstance().get(ops[i])));
        }
    }

    public void initOverlayRendering() {

        Configuration conf = Game.getInstance().getConfiguration();

        glDisable(GL_FOG);

        glClear(GL_DEPTH_BUFFER_BIT);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrtho(0, conf.getWidth(), 0, conf.getHeight(), -100, 100);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();

        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        glEnable(GL_COLOR_MATERIAL);
        glEnable(GL_ALPHA_TEST);
        glDisable(GL_CULL_FACE);
        glDisable(GL_DEPTH_TEST);
    }

    public void initSceneRendering() {

        glDisable(GL_BLEND);
        glEnable(GL_DEPTH_TEST);
        glDepthFunc(GL_LEQUAL);
        glEnable(GL_CULL_FACE);

        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();

        glEnable(GL_FOG);
    }

    public void renderTransculentOverlayLayer() {
        glColor4f(0.0f, 0.0f, 0.0f, 0.4f);
        glDisable(GL_TEXTURE_2D);
        glBegin(GL_QUADS);
        glVertex2i(0, 0);
        glVertex2i(_configuration.getWidth(), 0);
        glVertex2i(_configuration.getWidth(), _configuration.getHeight());
        glVertex2i(0, _configuration.getWidth());
        glEnd();
    }

    public void quit() {
        shouldQuitNextTick = true;
    }

    public void startGameLoop() {

        Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
        while (!Display.isCloseRequested() && !shouldQuitNextTick) {
            long startTiming = System.nanoTime();
            _step = 1.0f / _fps;

            /* Update */
            PerformanceMonitor.getInstance().start(Operation.UPDATE);
            update();
            PerformanceMonitor.getInstance().stop(Operation.UPDATE);

            /* Render */
            PerformanceMonitor.getInstance().start(Operation.RENDER_ALL);
            render();
            Display.update();
            PerformanceMonitor.getInstance().stop(Operation.RENDER_ALL);

            long stopTiming = System.nanoTime();

            long frameTimeNanos = (stopTiming - startTiming);
            long desiredFrameTimeNanos = 1000000000L;
            if (_configuration.getVSync()) {
                // desiredFrameTimeNanos /= 1;
            } else {
                desiredFrameTimeNanos /= _configuration.getFPS();
            }

            long diff = desiredFrameTimeNanos - frameTimeNanos;
            if (frameTimeNanos < desiredFrameTimeNanos) {
                if (!_configuration.getVSync()) {
                    try {
                        _sleepTimeMillis = (int) (diff / 1000000L);
                        Thread.sleep(_sleepTimeMillis, (int) (diff % 1000000L));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            } else {
                _sleepTimeMillis = 0;
            }
            _fps = (int) (1000000000.0f / (frameTimeNanos + (_sleepTimeMillis * 1000000L)));

            /* Average FPS System */
            float fpsSum = _fps;
            for (int i = 0; i < _fpsDataBuffer.length - 1; ++i) {
                _fpsDataBuffer[i] = _fpsDataBuffer[i + 1];
                fpsSum += _fpsDataBuffer[i];
            }
            _fpsDataBuffer[_fpsDataBuffer.length - 1] = _fps;
            fpsSum /= _fpsDataBuffer.length;
            _averageFPS = fpsSum;

        }

        if (_world != null) {
            try {
                getWorld().save();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        BufferManager.getInstance().deleteQueuedBuffers();
        BlockManager.getInstance().release();
        TextureStorage.release();
        FontStorage.release();
        Mouse.setGrabbed(false);
        Display.destroy();
    }

    private void loadTextures() throws IOException {
        TextureStorage.setTexturePack(_configuration.getTexturePack());
        TextureStorage.loadTexture("terrain", "PNG", "terrain.png");
        TextureStorage.loadTexture("particles", "PNG", "particles.png");
        TextureStorage.loadStichedTexture("items", "gui/items.png", "gui/items_custom.png");
        TextureStorage.loadTexture("gui.gui", "PNG", "gui/gui.png");
        TextureStorage.loadTexture("gui.container", "PNG", "gui/container.png");
        TextureStorage.loadTexture("gui.inventory", "PNG", "gui/inventory.png");
        TextureStorage.loadTexture("gui.crafting", "PNG", "gui/crafting.png");
        TextureStorage.loadTexture("environment.clouds", "PNG", "environment/clouds.png");
    }

    private void loadFonts() throws IOException {
        FontStorage.loadFont("Monospaced_20", "novamono.ttf", 22);
        FontStorage.loadFont("InventoryAmount", "visitor1.ttf", 14);
    }

    private void loadItems() {
        try {
            ItemXMLLoader.parseXML();
        } catch (Exception ex) {
            Logger.getLogger(Game.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void loadBlocks() {
        try {
            BlockXMLLoader.parseXML();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void setWorld(World world) {
        this._world = world;
    }

    public World getWorld() {
        return this._world;
    }

    private void loadConfiguration() throws IOException {
        this._configuration = new Configuration();
        this._configuration.loadFromFile("conf/conf.txt");
        this._configuration.setMaximumPlayerEditingDistance(5.0f);
    }

    public Configuration getConfiguration() {
        return _configuration;
    }

    /**
     * Returns the sleeptime in milliseconds for the last frame.
     * 
     * @return
     */
    public int getSleepTime() {
        return _sleepTimeMillis;
    }

    public float getAverageFPS() {
        return _averageFPS;
    }

    public static final int FILE_BASE_APPLICATION = 0x01;
    public static final int FILE_BASE_USER_DATA = 0x02;

    public File getRelativeFile(int fileBase, String string) {
        if (string.contains("${world}")) {
            string = string.replace("${world}", getWorld().getWorldName());
        }
        switch (fileBase) {
        case FILE_BASE_USER_DATA:
            return new File(getUserDataFolder(), string);
        case FILE_BASE_APPLICATION:
        default:
            return new File(string);
        }
    }

    private File getUserHome() {
        return new File(System.getProperty("user.home"));
    }

    private File getUserDataFolder() {
        String os = System.getProperty("os.name").toLowerCase();
        File f = null;
        if (os.contains("mac")) {
            f = new File(getUserHome(), "Library/Application Support/factorybuilder3d");
        } else if (os.contains("inux") || os.contains("nix")) {
            f = new File(getUserHome(), ".factorybuilder3d");
        } else if (os.contains("win")) {
            f = new File(new File(System.getenv("APPDATA")), ".factorybuilder3d");
        }
        f.mkdir();
        return f;
    }
}