Java tutorial
/* * 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(); } }