com.surgeplay.visage.slave.VisageSlave.java Source code

Java tutorial

Introduction

Here is the source code for com.surgeplay.visage.slave.VisageSlave.java

Source

/*
 * Visage
 * Copyright (c) 2015-2016, Aesen Vismea <aesen@unascribed.com>
 *
 * The MIT License
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package com.surgeplay.visage.slave;

import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;

import org.lwjgl.opengl.ContextAttribs;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GLContext;
import org.lwjgl.opengl.Pbuffer;
import org.lwjgl.opengl.PixelFormat;
import org.spacehq.mc.auth.SessionService;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.io.ByteStreams;
import com.google.gson.Gson;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;
import com.rabbitmq.client.ShutdownSignalException;
import com.rabbitmq.client.QueueingConsumer.Delivery;
import com.surgeplay.visage.Visage;
import com.surgeplay.visage.VisageRunner;
import com.typesafe.config.Config;

public class VisageSlave extends Thread implements VisageRunner {
    public static boolean explodeOnError = false;
    protected Config config;
    protected SessionService session = new SessionService();
    protected Gson gson = new Gson();
    protected String name;
    protected ConnectionFactory factory;
    protected Connection conn;
    protected Channel channel;
    protected List<RenderThread> threads = Lists.newArrayList();
    protected int idx = 0;
    protected final String queue;
    private boolean run = true;

    public VisageSlave(Config config) {
        super("Slave thread");
        this.config = config;
        try {
            name = config.getString("name");
            if (name.startsWith("~")) {
                String command = name.substring(1);
                if (Visage.debug)
                    Visage.log.finer("Running command '" + command + "' to determine the slave's name");
                Process proc = Runtime.getRuntime().exec(command);
                byte[] bys = ByteStreams.toByteArray(proc.getInputStream());
                name = new String(bys).replace("\n", "").replace("\r", "");
            }
        } catch (Exception e) {
            name = "unnamed slave";
        }
        Visage.log.info("Slave name is '" + name + "'");
        queue = config.getString("rabbitmq.queue");
        explodeOnError = config.hasPath("explode-on-error") && config.getBoolean("explode-on-error");
    }

    @Override
    public void run() {
        System.setProperty("org.lwjgl.opengl.Display.allowSoftwareOpenGL",
                Boolean.toString(config.getBoolean("allowSoftware")));
        System.setProperty("org.lwjgl.opengl.Display.noinput", "true");
        try {
            Visage.log.info("Setting up LWJGL");
            Pbuffer test = new Pbuffer(16, 16, new PixelFormat(8, 8, 0), null, null, new ContextAttribs(1, 2));
            test.makeCurrent();
            if (!GLContext.getCapabilities().GL_ARB_vertex_buffer_object) {
                Visage.log.severe(
                        "Your graphics driver does not support ARB_vertex_buffer_object. The slave cannot continue.");
                test.destroy();
                return;
            }
            String glV = GL11.glGetString(GL11.GL_VERSION);
            String os = System.getProperty("os.name");
            Visage.log.info("OpenGL " + glV + " on " + os);
            if (os.contains("Win")) {
                Visage.log.severe("Visage does not support Windows. Continue at your own peril!");
            }
            if (!glV.contains("Mesa")) {
                Visage.log.warning("You are using an unsupported graphics driver.");
            }
            if (os.equals("Linux") && glV.contains("Mesa")) {
                Visage.log.fine("Visage fully supports your OS and graphics driver.");
            }
            test.destroy();
            factory = new ConnectionFactory();
            factory.setHost(config.getString("rabbitmq.host"));
            factory.setPort(config.getInt("rabbitmq.port"));
            factory.setRequestedHeartbeat(10);
            if (config.hasPath("rabbitmq.user")) {
                if (Visage.debug)
                    Visage.log.finer("Using authentication");
                factory.setUsername(config.getString("rabbitmq.user"));
                factory.setPassword(config.getString("rabbitmq.password"));
            }
            reconnect();

            Visage.log.info("Setting up " + config.getInt("renderers") + " render threads");
            for (int i = 0; i < config.getInt("renderers"); i++) {
                RenderThread rt = new RenderThread(this);
                threads.add(rt);
                rt.start();
            }

            QueueingConsumer consumer = new QueueingConsumer(channel);
            Map<String, Object> args = Maps.newHashMap();
            args.put("x-priority", config.getInt("weight"));
            channel.basicConsume(queue, false, args, consumer);
            Visage.log.info("Listening for jobs");
            try {
                while (run) {
                    try {
                        Delivery delivery = consumer.nextDelivery();
                        if (Visage.debug)
                            Visage.log.finer("Received job, passing on to render thread");
                        RenderThread thread = threads.get(idx);
                        thread.process(delivery);
                        idx++;
                        if (idx >= threads.size()) {
                            idx = 0;
                        }
                    } catch (ShutdownSignalException e) {
                        try {
                            conn.close();
                        } catch (Exception ex) {
                        }
                        reconnect();
                    } catch (InterruptedException e) {
                        break;
                    }
                }
            } catch (Exception e) {
                Visage.log.log(Level.SEVERE, "A fatal error has occurred in the slave run loop.", e);
            }
            try {
                Visage.log.info("Shutting down slave");
                for (RenderThread rt : threads) {
                    rt.finish();
                }
                conn.close(5000);
            } catch (Exception e) {
                Visage.log.log(Level.SEVERE, "A fatal error has occurred while shutting down the slave.", e);
            }
        } catch (Exception e) {
            Visage.log.log(Level.SEVERE, "A fatal error has occurred while setting up the slave.", e);
        }
    }

    private void reconnect() throws IOException {
        Visage.log.info("Connecting to RabbitMQ at " + config.getString("rabbitmq.host") + ":"
                + config.getInt("rabbitmq.port"));
        conn = factory.newConnection();
        channel = conn.createChannel();
        if (Visage.debug)
            Visage.log.finer("Setting up queue '" + queue + "'");
        channel.queueDeclare(queue, false, false, true, null);
        int qos = config.getInt("qos");
        if (qos != -1) {
            channel.basicQos(qos);
        }
    }

    @Override
    public void shutdown() {
        run = false;
        interrupt();
    }
}