Java tutorial
/* * This file is part of Spout. * * Copyright (c) 2011-2012, SpoutDev <http://www.spout.org/> * Spout is licensed under the SpoutDev License Version 1. * * Spout is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * In addition, 180 days after any changes are published, you can use the * software, incorporating those changes, under the terms of the MIT license, * as described in the SpoutDev License Version 1. * * Spout 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License, * the MIT license and the SpoutDev License Version 1 along with this program. * If not, see <http://www.gnu.org/licenses/> for the GNU Lesser General Public * License and see <http://www.spout.org/SpoutDevLicenseV1.txt> for the full license, * including the MIT license. */ package org.spout.engine; import java.awt.Color; import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT; import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT; import static org.lwjgl.opengl.GL11.glClear; import java.io.File; import java.io.IOException; import java.net.URISyntaxException; import java.security.CodeSource; import java.util.Collection; import java.util.Collections; import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Level; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.SystemUtils; import org.jboss.netty.bootstrap.ClientBootstrap; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelFactory; import org.jboss.netty.channel.ChannelPipelineFactory; import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; import org.lwjgl.LWJGLException; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; import org.lwjgl.opengl.ContextAttribs; import org.lwjgl.opengl.Display; import org.lwjgl.opengl.DisplayMode; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL30; import org.lwjgl.opengl.PixelFormat; import org.spout.api.Client; import org.spout.api.FileSystem; import org.spout.api.Spout; import org.spout.api.audio.SoundManager; import org.spout.api.command.CommandRegistrationsFactory; import org.spout.api.command.CommandSource; import org.spout.api.command.annotated.AnnotatedCommandRegistrationFactory; import org.spout.api.command.annotated.SimpleInjector; import org.spout.api.component.components.CameraComponent; import org.spout.api.component.components.TransformComponent; import org.spout.api.datatable.SerializableMap; import org.spout.api.entity.state.PlayerInputState.Flags; import org.spout.api.geo.World; import org.spout.api.geo.cuboid.Chunk; import org.spout.api.geo.discrete.Point; import org.spout.api.geo.discrete.Transform; import org.spout.api.math.MathHelper; import org.spout.api.math.Quaternion; import org.spout.api.math.Vector2; import org.spout.api.math.Vector3; import org.spout.api.plugin.Platform; import org.spout.api.plugin.PluginStore; import org.spout.api.protocol.CommonPipelineFactory; import org.spout.api.protocol.PortBinding; import org.spout.api.protocol.Protocol; import org.spout.api.protocol.Session; import org.spout.api.render.Camera; import org.spout.api.render.RenderMaterial; import org.spout.api.render.RenderMode; import org.spout.engine.audio.SpoutSoundManager; import org.spout.engine.batcher.PrimitiveBatch; import org.spout.engine.batcher.SpriteBatch; import org.spout.engine.command.InputManagementCommands; import org.spout.engine.entity.SpoutClientPlayer; import org.spout.engine.entity.SpoutPlayer; import org.spout.engine.filesystem.ClientFileSystem; import org.spout.engine.input.SpoutInput; import org.spout.engine.input.SpoutInputConfiguration; import org.spout.engine.listener.SpoutClientListener; import org.spout.engine.listener.channel.SpoutClientConnectListener; import org.spout.engine.protocol.SpoutClientSession; import org.spout.engine.renderer.WorldRenderer; import org.spout.engine.util.MacOSXUtils; import org.spout.engine.util.thread.threadfactory.NamedThreadFactory; import org.spout.engine.world.SpoutClientWorld; public class SpoutClient extends SpoutEngine implements Client { private final SoundManager soundManager = new SpoutSoundManager(); private final SpoutInput inputManager = new SpoutInput(); private final String name = "Spout Client"; private final Vector2 resolution = new Vector2(640, 480); private final boolean[] sides = { true, true, true, true, true, true }; private final float aspectRatio = resolution.getX() / resolution.getY(); private final FileSystem filesystem; private Camera activeCamera; private WorldRenderer worldRenderer; private final AtomicReference<SpoutClientSession> session = new AtomicReference<SpoutClientSession>(); private SpoutPlayer activePlayer; private final AtomicReference<SpoutClientWorld> activeWorld = new AtomicReference<SpoutClientWorld>(); private final AtomicReference<PortBinding> potentialBinding = new AtomicReference<PortBinding>(); // Handle stopping private volatile boolean rendering = true; private String stopMessage = null; private final ClientBootstrap bootstrap = new ClientBootstrap(); //Test private SpriteBatch gui; public SpoutClient() { this.filesystem = new ClientFileSystem(); } @Override public void init(SpoutApplication args) { boolean inJar = false; try { CodeSource cs = SpoutClient.class.getProtectionDomain().getCodeSource(); inJar = cs.getLocation().toURI().getPath().endsWith(".jar"); } catch (URISyntaxException e) { e.printStackTrace(); } if (inJar) { unpackLwjgl(); } ExecutorService executorBoss = Executors .newCachedThreadPool(new NamedThreadFactory("SpoutServer - Boss", true)); ExecutorService executorWorker = Executors .newCachedThreadPool(new NamedThreadFactory("SpoutServer - Worker", true)); ChannelFactory factory = new NioClientSocketChannelFactory(executorBoss, executorWorker); bootstrap.setFactory(factory); ChannelPipelineFactory pipelineFactory = new CommonPipelineFactory(this, true); bootstrap.setPipelineFactory(pipelineFactory); super.init(args); } @Override public void start() { start(true); } @Override public void start(boolean checkWorlds) { super.start(checkWorlds); getEventManager().registerEvents(new SpoutClientListener(this), this); CommandRegistrationsFactory<Class<?>> commandRegFactory = new AnnotatedCommandRegistrationFactory( new SimpleInjector(this)); // Register commands getRootCommand().addSubCommands(this, InputManagementCommands.class, commandRegFactory); while (super.getDefaultWorld() == null) { try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // TODO : Wait until the world is fully loaded } Transform loc = new Transform(new Point(super.getDefaultWorld(), 0f, 0f, 0f), new Quaternion(0f, 0f, 0f, 0f), Vector3.ONE); activePlayer = new SpoutClientPlayer("Spouty", loc, SpoutConfiguration.VIEW_DISTANCE.getInt() * Chunk.BLOCKS.SIZE); activeCamera = activePlayer.add(CameraComponent.class); System.out.println("activeWorld: " + super.getDefaultWorld().getName()); super.getDefaultWorld().spawnEntity(activePlayer); getScheduler().startRenderThread(); } @Override public SpoutPlayer getActivePlayer() { return activePlayer; } @Override public CommandSource getCommandSource() { if (session.get() != null) { return activePlayer; } else { return super.getCommandSource(); } } @Override public Camera getActiveCamera() { return activeCamera; } @Override public void setActiveCamera(Camera activeCamera) { this.activeCamera = activeCamera; } @Override public PluginStore getPluginStore() { // TODO Auto-generated method stub return null; } @Override public Platform getPlatform() { return Platform.CLIENT; } @Override public RenderMode getRenderMode() { return getArguments().renderMode; } @Override public String getName() { return name; } @Override public SoundManager getSoundManager() { return soundManager; } @Override public SpoutInput getInput() { return inputManager; } public void doInput() { //inputManager.pollInput(); // One Mouse.getDX() to rule them all // TODO move this a plugin if (activePlayer == null) { return; } Transform ts = activePlayer.getTransform().getTransform(); if (Mouse.isGrabbed()) { float pitch = ts.getRotation().getPitch(); float yaw = ts.getRotation().getYaw(); float mouseDX = Mouse.getDX() * 0.16f; float mouseDY = -Mouse.getDY() * 0.16f; if (yaw + mouseDX >= 360) yaw += mouseDX - 360; else if (yaw + mouseDX < 0) yaw += mouseDX + 360; else yaw += mouseDX; if (pitch + mouseDY >= -80 && pitch + mouseDY <= 80) pitch += mouseDY; else if (pitch + mouseDY < -80) pitch = -80; else if (pitch + mouseDY > 80) pitch = 80; //System.out.println("yaw: "+yaw+" pitch: "+pitch); ts.setRotation(MathHelper.rotation(pitch, yaw, ts.getRotation().getRoll())); //System.out.println(activePlayer.getTransform().getTransform().toMatrix().toString()); } if (!Keyboard.isCreated()) return; boolean keyUp = Keyboard.isKeyDown(Keyboard.KEY_UP) || Keyboard.isKeyDown(Keyboard.getKeyIndex(SpoutInputConfiguration.FORWARD.getString())); boolean keyDown = Keyboard.isKeyDown(Keyboard.KEY_DOWN) || Keyboard.isKeyDown(Keyboard.getKeyIndex(SpoutInputConfiguration.BACKWARD.getString())); boolean keyLeft = Keyboard.isKeyDown(Keyboard.KEY_LEFT) || Keyboard.isKeyDown(Keyboard.getKeyIndex(SpoutInputConfiguration.LEFT.getString())); boolean keyRight = Keyboard.isKeyDown(Keyboard.KEY_RIGHT) || Keyboard.isKeyDown(Keyboard.getKeyIndex(SpoutInputConfiguration.RIGHT.getString())); boolean flyUp = Keyboard.isKeyDown(Keyboard.getKeyIndex(SpoutInputConfiguration.UP.getString())); boolean flyDown = Keyboard.isKeyDown(Keyboard.getKeyIndex(SpoutInputConfiguration.DOWN.getString())); Point point = ts.getPosition(); if (keyUp) { point = point.subtract(ts.forwardVector()); } if (keyDown) { point = point.add(ts.forwardVector()); } if (keyLeft) { point = point.subtract(ts.rightVector()); } if (keyRight) { point = point.add(ts.rightVector()); } if (flyUp) { point = point.add(ts.upVector()); } if (flyDown) { point = point.subtract(ts.upVector()); } ts.setPosition(point); activePlayer.getTransform().setTransform(ts); /*if (keyUp || keyDown || keyLeft || keyRight || flyUp || flyDown) { //point = new Point(point.normalize(),activePlayer.getWorld()); System.out.println("Translation : "+point.getX()+"/"+point.getY()+"/"+point.getZ()); System.out.println("Position : "+activePlayer.getTransform().getPosition().getX()+"/"+activePlayer.getTransform().getPosition().getY()+"/"+activePlayer.getTransform().getPosition().getZ()); activePlayer.getTransform().setPosition(activePlayer.getTransform().getPosition().add(point)); }*/ /*for (Flags f : activePlayer.input().getFlagSet()) { switch(f) { case FORWARD: activePlayer.getTransform().setPosition(activePlayer.getTransform().getPosition().add(activePlayer.getTransform().getTransform().forwardVector())); break; case BACKWARD: activePlayer.getTransform().setPosition(activePlayer.getTransform().getPosition().subtract(activePlayer.getTransform().getTransform().forwardVector())); break; case LEFT: activePlayer.getTransform().setPosition(activePlayer.getTransform().getPosition().add(activePlayer.getTransform().getTransform().rightVector())); break; case RIGHT: activePlayer.getTransform().setPosition(activePlayer.getTransform().getPosition().subtract(activePlayer.getTransform().getTransform().rightVector())); break; case CROUCH: case FIRE_1: case FIRE_2: case INTERACT: case JUMP: case SELECT_DOWN: case SELECT_UP: } }*/ } @Override public PortBinding getAddress() { return session.get().getActiveAddress(); } @Override public boolean stop(String message) { if (!super.stop(message, false)) { return false; } rendering = false; stopMessage = message; Runnable finalTask = new Runnable() { @Override public void run() { bootstrap.getFactory().releaseExternalResources(); boundProtocols.clear(); } }; getScheduler().submitFinalTask(finalTask, true); getScheduler().stop(); return true; } public boolean isRendering() { return rendering; } public void stopEngine() { if (rendering) { throw new IllegalStateException("Client is still rendering!"); } super.stop(stopMessage); } @Override public SpoutClientWorld getWorld(String name, boolean exact) { SpoutClientWorld world = activeWorld.get(); if (world == null) { return null; } if ((exact && world.getName().equals(name)) || world.getName().startsWith(name)) { return world; } else { return null; } } @Override public SpoutClientWorld getWorld(UUID uid) { SpoutClientWorld world = activeWorld.get(); if (world != null && world.getUID().equals(uid)) { return world; } else { return null; } } @Override public Collection<World> getWorlds() { return Collections.<World>singletonList(activeWorld.get()); } /*@Override public SpoutClientWorld getDefaultWorld() { return activeWorld.get(); }*/ @Override public SpoutClientWorld worldChanged(String name, UUID uuid, byte[] data) { SpoutClientWorld world = new SpoutClientWorld(name, uuid, this, getEngineItemMap()); SerializableMap map = world.getComponentHolder().getData(); try { map.deserialize(data); } catch (IOException e) { throw new RuntimeException("Unable to deserialize data", e); } SpoutClientWorld oldWorld = activeWorld.getAndSet(world); if (oldWorld != null) { if (!oldWorld.getExecutor().haltExecutor()) { throw new IllegalStateException("Executor was already halted when halting was attempted"); } oldWorld.unload(false); } if (!world.getExecutor().startExecutor()) { activeWorld.compareAndSet(world, null); throw new IllegalStateException("Unable to start executor for new world"); } return world; } public ClientBootstrap getBootstrap() { return bootstrap; } @Override public SpoutClientSession newSession(Channel channel) { Protocol protocol = potentialBinding.getAndSet(null).getProtocol(); return new SpoutClientSession(this, channel, protocol); } public void connect(final PortBinding binding) { potentialBinding.set(binding); getBootstrap().connect(binding.getAddress()).addListener(new SpoutClientConnectListener(this, binding)); } public void disconnected() { Session sess = this.session.getAndSet(null); if (sess != null) { getSessionRegistry().remove(sess); } } public void setSession(SpoutClientSession session) { this.session.set(session); getSessionRegistry().add(session); activePlayer.connect(session, null); session.setPlayer(activePlayer); players.putIfAbsent(activePlayer.getName(), activePlayer); } private PrimitiveBatch renderer; private RenderMaterial mat; public void initRenderer() { createWindow(); //Mouse.setGrabbed(true); getLogger().info("SpoutClient Information"); getLogger().info("Operating System: " + System.getProperty("os.name")); getLogger().info("Renderer Mode: " + this.getRenderMode().toString()); getLogger().info("OpenGL Information"); getLogger().info("Vendor: " + GL11.glGetString(GL11.GL_VENDOR)); getLogger().info("OpenGL Version: " + GL11.glGetString(GL11.GL_VERSION)); getLogger().info("GLSL Version: " + GL11.glGetString(GL20.GL_SHADING_LANGUAGE_VERSION)); getLogger().info("Max Textures: " + GL11.glGetString(GL20.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS)); String extensions = "Extensions Supported: "; if (getArguments().renderMode == RenderMode.GL30) { for (int i = 0; i < GL11.glGetInteger(GL30.GL_NUM_EXTENSIONS); i++) { extensions += GL30.glGetStringi(GL11.GL_EXTENSIONS, i) + " "; } } else { extensions += GL11.glGetString(GL11.GL_EXTENSIONS); } getLogger().info(extensions); soundManager.init(); Spout.getFilesystem().postStartup(); GL11.glEnable(GL11.GL_DEPTH_TEST); GL11.glClearColor((135.f / 255.0f), 206.f / 255.f, 250.f / 255.f, 0); worldRenderer = new WorldRenderer(this); worldRenderer.setup(); renderer = new PrimitiveBatch(); mat = (RenderMaterial) this.getFilesystem() .getResource("material://Spout/resources/resources/materials/BasicMaterial.smt"); renderer.begin(); renderer.addCube(new Vector3(-0.5, -0.5, -0.5), Vector3.ONE, Color.RED, sides); renderer.end(); gui = new SpriteBatch(); } public void render(float dt) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); if (Mouse.isButtonDown(0)) { if (!Mouse.isGrabbed()) Mouse.setGrabbed(true); } else Mouse.setGrabbed(false); worldRenderer.render(); try { //System.out.println("view "+activeCamera.getView().toString()); Transform loc = new Transform(new Point(null, 0f, 0f, 0f), Quaternion.IDENTITY, Vector3.ONE); mat.getShader().setUniform("View", activeCamera.getView()); mat.getShader().setUniform("Projection", activeCamera.getProjection()); mat.getShader().setUniform("Model", loc.toMatrix()); renderer.draw(mat); gui.begin(); gui.draw(mat, .25f, .25f, .25f, .25f); gui.render(); } catch (Exception e) { } } public WorldRenderer getWorldRenderer() { return worldRenderer; } private void createWindow() { try { Display.setDisplayMode(new DisplayMode((int) resolution.getX(), (int) resolution.getY())); if (MacOSXUtils.isMac()) { createMacWindow(); } else { if (getRenderMode() == RenderMode.GL11) { ContextAttribs ca = new ContextAttribs(1, 5); Display.create(new PixelFormat(8, 24, 0), ca); } else if (getRenderMode() == RenderMode.GL20) { ContextAttribs ca = new ContextAttribs(2, 1); Display.create(new PixelFormat(8, 24, 0), ca); } else if (getRenderMode() == RenderMode.GL30) { ContextAttribs ca = new ContextAttribs(3, 2).withForwardCompatible(false); Display.create(new PixelFormat(8, 24, 0), ca); } } Display.setTitle("Spout Client"); } catch (LWJGLException e) { e.printStackTrace(); } } private void createMacWindow() throws LWJGLException { if (getRenderMode() == RenderMode.GL30) { if (MacOSXUtils.getOSXVersion() >= 7) { ContextAttribs ca = new ContextAttribs(3, 2).withProfileCore(true); Display.create(new PixelFormat(8, 24, 0), ca); } else { throw new UnsupportedOperationException("Cannot create a 3.0 context without OSX 10.7_"); } } else { Display.create(); } } private static void unpackLwjgl() { String[] files; String osPath; if (SystemUtils.IS_OS_WINDOWS) { files = new String[] { "jinput-dx8_64.dll", "jinput-dx8.dll", "jinput-raw_64.dll", "jinput-raw.dll", "jinput-wintab.dll", "lwjgl.dll", "lwjgl64.dll", "OpenAL32.dll", "OpenAL64.dll" }; osPath = "windows/"; } else if (SystemUtils.IS_OS_MAC) { files = new String[] { "libjinput-osx.jnilib", "liblwjgl.jnilib", "openal.dylib", }; osPath = "mac/"; } else if (SystemUtils.IS_OS_LINUX) { files = new String[] { "liblwjgl.so", "liblwjgl64.so", "libopenal.so", "libopenal64.so", "libjinput-linux.so", "libjinput-linux64.so" }; osPath = "linux/"; } else { Spout.getEngine().getLogger().log(Level.SEVERE, "Error loading natives of operating system type: " + SystemUtils.OS_NAME); return; } File cacheDir = new File(System.getProperty("user.dir"), "natives/" + osPath); cacheDir.mkdirs(); for (String f : files) { File outFile = new File(cacheDir, f); if (!outFile.exists()) { try { FileUtils.copyInputStreamToFile(SpoutClient.class.getResourceAsStream("/" + f), outFile); } catch (IOException e) { e.printStackTrace(); } } } String nativePath = cacheDir.getAbsolutePath(); System.setProperty("org.lwjgl.librarypath", nativePath); System.setProperty("net.java.games.input.librarypath", nativePath); } @Override public FileSystem getFilesystem() { return filesystem; } }