Browse Source

Super-basic actually somewhat working online multiplayer

Tankernn 7 years ago
parent
commit
d631cae565
21 changed files with 593 additions and 310 deletions
  1. 31 25
      src/main/java/eu/tankernn/gameEngine/TankernnGame3D.java
  2. 81 0
      src/main/java/eu/tankernn/gameEngine/World.java
  3. 3 3
      src/main/java/eu/tankernn/gameEngine/animation/model/AnimatedModel.java
  4. 59 66
      src/main/java/eu/tankernn/gameEngine/entities/Entity3D.java
  5. 103 0
      src/main/java/eu/tankernn/gameEngine/entities/EntityState.java
  6. 3 1
      src/main/java/eu/tankernn/gameEngine/entities/ILight.java
  7. 6 0
      src/main/java/eu/tankernn/gameEngine/entities/Light.java
  8. 3 7
      src/main/java/eu/tankernn/gameEngine/entities/Player.java
  9. 1 1
      src/main/java/eu/tankernn/gameEngine/entities/PlayerCamera.java
  10. 2 2
      src/main/java/eu/tankernn/gameEngine/entities/npc/RoamingBehavior.java
  11. 6 6
      src/main/java/eu/tankernn/gameEngine/entities/projectiles/Projectile.java
  12. 4 2
      src/main/java/eu/tankernn/gameEngine/entities/projectiles/TargetedProjectile.java
  13. 3 3
      src/main/java/eu/tankernn/gameEngine/loader/Loader.java
  14. 7 1
      src/main/java/eu/tankernn/gameEngine/loader/models/TexturedModel.java
  15. 17 0
      src/main/java/eu/tankernn/gameEngine/multiplayer/LoginRequest.java
  16. 21 0
      src/main/java/eu/tankernn/gameEngine/multiplayer/LoginResponse.java
  17. 42 0
      src/main/java/eu/tankernn/gameEngine/multiplayer/WorldState.java
  18. 5 0
      src/main/java/eu/tankernn/gameEngine/particles/Sun.java
  19. 3 2
      src/main/java/eu/tankernn/gameEngine/renderEngine/MasterRenderer.java
  20. 4 3
      src/main/java/eu/tankernn/gameEngine/renderEngine/Scene.java
  21. 189 188
      src/main/java/eu/tankernn/gameEngine/util/MousePicker.java

+ 31 - 25
src/main/java/eu/tankernn/gameEngine/TankernnGame3D.java

@@ -1,8 +1,7 @@
 package eu.tankernn.gameEngine;
 
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.Collection;
 
 import org.lwjgl.opengl.Display;
 import org.lwjgl.opengl.GL30;
@@ -10,10 +9,9 @@ import org.lwjgl.util.vector.Vector4f;
 
 import eu.tankernn.gameEngine.entities.Camera;
 import eu.tankernn.gameEngine.entities.Entity3D;
-import eu.tankernn.gameEngine.entities.ILight;
 import eu.tankernn.gameEngine.entities.Player;
-import eu.tankernn.gameEngine.entities.projectiles.Projectile;
 import eu.tankernn.gameEngine.environmentMap.EnvironmentMapRenderer;
+import eu.tankernn.gameEngine.loader.Loader;
 import eu.tankernn.gameEngine.loader.textures.Texture;
 import eu.tankernn.gameEngine.particles.ParticleMaster;
 import eu.tankernn.gameEngine.postProcessing.PostProcessor;
@@ -21,7 +19,6 @@ import eu.tankernn.gameEngine.renderEngine.Fbo;
 import eu.tankernn.gameEngine.renderEngine.MasterRenderer;
 import eu.tankernn.gameEngine.renderEngine.MultisampleMultitargetFbo;
 import eu.tankernn.gameEngine.renderEngine.Scene;
-import eu.tankernn.gameEngine.renderEngine.gui.floating.FloatingTexture;
 import eu.tankernn.gameEngine.renderEngine.gui.floating.FloatingTextureRenderer;
 import eu.tankernn.gameEngine.renderEngine.skybox.Skybox;
 import eu.tankernn.gameEngine.renderEngine.water.WaterMaster;
@@ -39,12 +36,8 @@ public class TankernnGame3D extends TankernnGame {
 	protected Camera camera;
 	protected Skybox sky;
 	protected MousePicker picker;
-
-	protected List<Entity3D> entities = new ArrayList<>();
-	protected List<Projectile> projectiles = new ArrayList<>();
-	protected List<ILight> lights = new ArrayList<>();
-	protected List<FloatingTexture> floatTextures = new ArrayList<>();
-	protected TerrainPack terrainPack;
+	
+	protected World world;
 	protected Player player;
 	
 	private MultisampleMultitargetFbo multisampleFbo = new MultisampleMultitargetFbo(Display.getWidth(),
@@ -54,6 +47,7 @@ public class TankernnGame3D extends TankernnGame {
 	
 	public TankernnGame3D(String name, String[] dayTextures, String[] nightTextures) {
 		super(name);
+		world = new World(loader);
 		try {
 			loader.readModelSpecification(new InternalFile("models.json"));
 		} catch (IOException e) {
@@ -65,19 +59,15 @@ public class TankernnGame3D extends TankernnGame {
 
 	public void update() {
 		super.update();
-		entities.forEach(Entity3D::update);
-		entities.removeIf(Entity3D::isDead);
 		
-		projectiles.forEach(Projectile::update);
-		projectiles.removeIf(Projectile::isDead);
-		projectiles.forEach((p) -> p.checkCollision(entities));
+		world.update();
 		
 		player.move();
-		picker.update(terrainPack, entities, guiMaster.getGuis());
+		picker.update(world.getTerrainPack(), world.getEntities().values(), guiMaster.getGuis());
 		camera.update();
-		terrainPack.update(player);
+		world.getTerrainPack().update(player);
 		particleMaster.update(camera);
-		DistanceSorter.sort(lights, camera);
+		DistanceSorter.sort(world.getLights(), camera);
 		
 		audioMaster.setListenerPosition(player.getPosition());
 	}
@@ -87,7 +77,7 @@ public class TankernnGame3D extends TankernnGame {
 	}
 	
 	protected void render() {
-		Scene scene = new Scene(entities, terrainPack, lights, camera, sky);
+		Scene scene = new Scene(world.getEntities().values(), world.getTerrainPack(), world.getLights(), camera, sky);
 
 		EnvironmentMapRenderer.renderEnvironmentMap(scene.getEnvironmentMap(), scene, player.getPosition(), renderer);
 
@@ -96,9 +86,9 @@ public class TankernnGame3D extends TankernnGame {
 		multisampleFbo.bindFrameBuffer();
 
 		renderer.renderScene(scene, new Vector4f(0, 1, 0, Float.MAX_VALUE));
-		waterMaster.renderWater(camera, lights);
+		waterMaster.renderWater(camera, world.getLights());
 		particleMaster.renderParticles(camera);
-		floatingRenderer.render(floatTextures, camera);
+		floatingRenderer.render(world.getFloatTextures(), camera);
 	}
 	
 	protected void postRender() {
@@ -119,7 +109,7 @@ public class TankernnGame3D extends TankernnGame {
 
 	public void cleanUp() {
 		super.cleanUp();
-		terrainPack.finalize();
+		world.finalize();
 		particleMaster.finalize();
 		postProcessor.finalize();
 		waterMaster.finalize();
@@ -129,7 +119,23 @@ public class TankernnGame3D extends TankernnGame {
 		renderer.finalize();
 	}
 
-	public List<Entity3D> getEntities() {
-		return entities;
+	public Collection<Entity3D> getEntities() {
+		return world.getEntities().values();
+	}
+
+	public Loader getLoader() {
+		return loader;
+	}
+
+	public TerrainPack getTerrain() {
+		return world.getTerrainPack();
+	}
+
+	public World getWorld() {
+		return world;
+	}
+
+	public Player getPlayer() {
+		return player;
 	}
 }

+ 81 - 0
src/main/java/eu/tankernn/gameEngine/World.java

@@ -0,0 +1,81 @@
+package eu.tankernn.gameEngine;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import eu.tankernn.gameEngine.entities.Entity3D;
+import eu.tankernn.gameEngine.entities.EntityState;
+import eu.tankernn.gameEngine.entities.ILight;
+import eu.tankernn.gameEngine.entities.projectiles.Projectile;
+import eu.tankernn.gameEngine.loader.Loader;
+import eu.tankernn.gameEngine.multiplayer.WorldState;
+import eu.tankernn.gameEngine.renderEngine.gui.floating.FloatingTexture;
+import eu.tankernn.gameEngine.terrains.TerrainPack;
+
+public class World {
+	private Map<Integer, Entity3D> entities = new HashMap<>();
+	private List<Projectile> projectiles = new ArrayList<>();
+	private List<ILight> lights = new ArrayList<>();
+	private List<FloatingTexture> floatTextures = new ArrayList<>();
+	private TerrainPack terrainPack;
+
+	private final Loader loader;
+
+	public World(Loader loader) {
+		this.loader = loader;
+	}
+
+	public void update() {
+		entities.values().forEach(Entity3D::update);
+		entities.values().removeIf(Entity3D::isDead);
+
+		projectiles.forEach(Projectile::update);
+		projectiles.removeIf(Projectile::isDead);
+		projectiles.forEach((p) -> p.checkCollision(entities.values()));
+	}
+
+	public Map<Integer, Entity3D> getEntities() {
+		return entities;
+	}
+
+	public List<Projectile> getProjectiles() {
+		return projectiles;
+	}
+
+	public List<ILight> getLights() {
+		return lights;
+	}
+
+	public List<FloatingTexture> getFloatTextures() {
+		return floatTextures;
+	}
+
+	public TerrainPack getTerrainPack() {
+		return terrainPack;
+	}
+
+	public void finalize() {
+		getTerrainPack().finalize();
+	}
+
+	public void setTerrainPack(TerrainPack terrainPack) {
+		this.terrainPack = terrainPack;
+	}
+
+	public void setState(WorldState state) {
+		for (EntityState s : state.getEntities()) {
+			updateEntityState(s);
+		}
+		this.lights = state.getLights();
+	}
+
+	public void updateEntityState(EntityState s) {
+		if (entities.containsKey(s.getId()))
+			entities.get(s.getId()).setState(s);
+		else
+			entities.put(s.getId(), new Entity3D(s, loader, terrainPack));
+	}
+
+}

+ 3 - 3
src/main/java/eu/tankernn/gameEngine/animation/model/AnimatedModel.java

@@ -54,8 +54,8 @@ public class AnimatedModel extends TexturedModel {
 	 *            this entity.
 	 * 
 	 */
-	public AnimatedModel(Vao model, ModelTexture texture, Joint rootJoint, int jointCount) {
-		super(model, texture);
+	public AnimatedModel(int id, Vao model, ModelTexture texture, Joint rootJoint, int jointCount) {
+		super(id, model, texture);
 		this.rootJoint = rootJoint;
 		this.jointCount = jointCount;
 		this.animator = new Animator(this);
@@ -63,7 +63,7 @@ public class AnimatedModel extends TexturedModel {
 	}
 
 	public AnimatedModel(AnimatedModel model) {
-		this(model.getModel(), model.getTexture(), new Joint(model.rootJoint), model.jointCount);
+		this(model.getId(), model.getModel(), model.getTexture(), new Joint(model.rootJoint), model.jointCount);
 		this.animations = new HashMap<>(model.animations);
 	}
 

+ 59 - 66
src/main/java/eu/tankernn/gameEngine/entities/Entity3D.java

@@ -1,131 +1,124 @@
 package eu.tankernn.gameEngine.entities;
 
-import java.util.concurrent.atomic.AtomicInteger;
-
 import org.lwjgl.util.vector.Vector3f;
 
 import eu.tankernn.gameEngine.animation.model.AnimatedModel;
 import eu.tankernn.gameEngine.audio.Source;
+import eu.tankernn.gameEngine.loader.Loader;
 import eu.tankernn.gameEngine.loader.models.AABB;
 import eu.tankernn.gameEngine.loader.models.TexturedModel;
-import eu.tankernn.gameEngine.renderEngine.DisplayManager;
-import eu.tankernn.gameEngine.settings.Physics;
 import eu.tankernn.gameEngine.terrains.TerrainPack;
 import eu.tankernn.gameEngine.util.IPositionable;
 
 public class Entity3D implements IPositionable {
-	private static final AtomicInteger ID_GEN = new AtomicInteger();
-	
+
 	private TexturedModel model;
-	protected Vector3f position, velocity = new Vector3f(0, 0, 0);
-	private Vector3f rotation;
-	private Vector3f scale;
+	private EntityState state;
 	private AABB boundingBox;
 	protected boolean dead;
 	protected TerrainPack terrain;
 	protected Source source = new Source();
-	private final int id;
-	
+
 	public Entity3D(TexturedModel model, Vector3f position, AABB boundingBox, TerrainPack terrain) {
 		this(model, position, new Vector3f(0, 0, 0), new Vector3f(0, 0, 0), boundingBox, terrain);
 	}
-	
-	public Entity3D(TexturedModel model, Vector3f position, Vector3f rotation, Vector3f scale, AABB boundingBox, TerrainPack terrain) {
+
+	public Entity3D(TexturedModel model, Vector3f position, Vector3f rotation, Vector3f scale, AABB boundingBox,
+			TerrainPack terrain) {
 		this.model = model;
-		this.position = position;
-		this.rotation = rotation;
-		this.scale = scale;
+		this.state = new EntityState(model == null ? -1 : model.getId(), position, rotation, new Vector3f(0, 0, 0), scale);
 		this.boundingBox = boundingBox;
 		this.boundingBox.updatePosition(position);
 		this.terrain = terrain;
-		this.id = ID_GEN.incrementAndGet();
-	}
-	
-	/**
-	 * Moves this entity in the direction of its rotation.
-	 * 
-	 * @param speed The speed with which to move the entity
-	 */
-	public void generateVelocity(float speed) {
-		velocity.x = (float) (speed * Math.sin(Math.toRadians(rotation.y)));
-		velocity.z = (float) (speed * Math.cos(Math.toRadians(rotation.y)));
-		velocity.y += Physics.GRAVITY * DisplayManager.getFrameTimeSeconds();
-	}
-	
-	public void increaseRotation(Vector3f deltaRotation) {
-		Vector3f.add(this.rotation, deltaRotation, this.rotation);
-	}
-	
+	}
+
+	public Entity3D(EntityState state, Loader loader, TerrainPack terrain) {
+		this.state = state;
+		this.model = loader.getModel(state.getModelId());
+		this.boundingBox = loader.getBoundingBox(model.getModel().id);
+		this.boundingBox.updatePosition(state.getPosition());
+		this.terrain = terrain;
+	}
+
 	public void update() {
-		Vector3f.add(position, (Vector3f) new Vector3f(velocity).scale(DisplayManager.getFrameTimeSeconds()), position);
-		
-		float terrainHeight = terrain.getTerrainHeightByWorldPos(position.x, position.z);
-		
-		if (position.y < terrainHeight) {
-			velocity.y = 0;
-			position.y = terrainHeight;
-		}
-		
-		source.setPosition(this.position);
-		source.setVelocity(this.velocity);
-		this.boundingBox.updatePosition(this.position);
+		state.update(terrain::getTerrainHeightByWorldPos);
+
+		source.setPosition(getPosition());
+		source.setVelocity(getVelocity());
+		this.boundingBox.updatePosition(getPosition());
 		if (model instanceof AnimatedModel)
 			((AnimatedModel) model).update();
 	}
-	
+
+	public boolean isInAir() {
+		return getPosition().y > terrain.getTerrainHeightByWorldPos(getPosition().x, getPosition().z);
+	}
+
 	public TexturedModel getModel() {
 		return model;
 	}
-	
+
 	public void setModel(TexturedModel model) {
 		this.model = model;
 	}
-	
+
 	public Vector3f getPosition() {
-		return position;
+		return state.getPosition();
 	}
-	
+
 	public void setPosition(Vector3f position) {
-		this.position = position;
+		state.getPosition().set(position);
+	}
+
+	public Vector3f getVelocity() {
+		return state.getVelocity();
 	}
-	
+
 	public Vector3f getRotation() {
-		return rotation;
+		return state.getRotation();
 	}
-	
+
 	public Vector3f getScale() {
-		return scale;
+		return state.getScale();
 	}
-	
+
 	public void setScale(Vector3f scale) {
-		this.scale = scale;
+		state.getScale().set(scale);
 	}
-	
+
 	public AABB getBoundingBox() {
 		return boundingBox;
 	}
-	
+
 	public boolean isDead() {
 		return dead;
 	}
-	
+
 	@Override
 	public boolean equals(Object obj) {
 		if (obj == null)
 			return false;
 		if (obj instanceof Entity3D) {
-			return this.id == ((Entity3D) obj).id;
+			return this.getId() == ((Entity3D) obj).getId();
 		}
 		return super.equals(obj);
 	}
-	
+
 	@Override
 	public int hashCode() {
-		return id;
+		return state.getId();
 	}
 
 	public int getId() {
-		return id;
+		return state.getId();
+	}
+
+	public EntityState getState() {
+		return state;
 	}
-	
+
+	public void setState(EntityState state) {
+		this.state = state;
+	}
+
 }

+ 103 - 0
src/main/java/eu/tankernn/gameEngine/entities/EntityState.java

@@ -0,0 +1,103 @@
+package eu.tankernn.gameEngine.entities;
+
+import java.io.Serializable;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.BiFunction;
+
+import org.lwjgl.util.vector.Vector3f;
+
+import eu.tankernn.gameEngine.renderEngine.DisplayManager;
+import eu.tankernn.gameEngine.settings.Physics;
+
+public class EntityState implements Serializable {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -3294516796577777079L;
+
+	private static final AtomicInteger ID_GEN = new AtomicInteger();
+
+	private final int id;
+	private int modelId;
+	private final Vector3f position;
+	private final Vector3f rotation;
+	private final Vector3f velocity;
+	private final Vector3f scale;
+
+	public EntityState(int id, int modelId, Vector3f position, Vector3f rotation, Vector3f velocity, Vector3f scale) {
+		this.id = id;
+		this.modelId = modelId;
+		this.position = position;
+		this.rotation = rotation;
+		this.velocity = velocity;
+		this.scale = scale;
+	}
+
+	public EntityState(int modelId, Vector3f position, Vector3f rotation, Vector3f velocity, Vector3f scale) {
+		this(ID_GEN.getAndIncrement(), modelId, position, rotation, velocity, scale);
+	}
+
+	public EntityState() {
+		this(-1, new Vector3f(0, 0, 0), new Vector3f(0, 0, 0), new Vector3f(0, 0, 0), new Vector3f(0, 0, 0));
+	}
+
+	/**
+	 * Moves this entity in the direction of its rotation.
+	 * 
+	 * @param speed
+	 *            The speed with which to move the entity
+	 */
+	public void generateVelocity(float speed) {
+		getVelocity().x = (float) (speed * Math.sin(Math.toRadians(getRotation().y)));
+		getVelocity().z = (float) (speed * Math.cos(Math.toRadians(getRotation().y)));
+		getVelocity().y += Physics.GRAVITY * DisplayManager.getFrameTimeSeconds();
+	}
+
+	public void increaseRotation(Vector3f deltaRotation) {
+		Vector3f.add(getRotation(), deltaRotation, getRotation());
+	}
+
+	public void update() {
+		Vector3f.add(position, (Vector3f) new Vector3f(velocity).scale(DisplayManager.getFrameTimeSeconds()), position);
+	}
+
+	public void update(BiFunction<Float, Float, Float> terrainHeightByPos) {
+		this.update();
+
+		float terrainHeight = terrainHeightByPos.apply(getPosition().x, getPosition().z);
+
+		if (position.y < terrainHeight) {
+			velocity.y = 0;
+			position.y = terrainHeight;
+		}
+	}
+
+	public int getId() {
+		return id;
+	}
+
+	public int getModelId() {
+		return modelId;
+	}
+
+	public void setModelId(int modelId) {
+		this.modelId = modelId;
+	}
+
+	public Vector3f getPosition() {
+		return position;
+	}
+
+	public Vector3f getRotation() {
+		return rotation;
+	}
+
+	public Vector3f getVelocity() {
+		return velocity;
+	}
+
+	public Vector3f getScale() {
+		return scale;
+	}
+
+}

+ 3 - 1
src/main/java/eu/tankernn/gameEngine/entities/ILight.java

@@ -1,10 +1,12 @@
 package eu.tankernn.gameEngine.entities;
 
+import java.io.Serializable;
+
 import org.lwjgl.util.vector.Vector3f;
 
 import eu.tankernn.gameEngine.util.IPositionable;
 
-public interface ILight extends IPositionable {
+public interface ILight extends IPositionable, Serializable {
 	Vector3f getColor();
 	Vector3f getAttenuation();
 }

+ 6 - 0
src/main/java/eu/tankernn/gameEngine/entities/Light.java

@@ -5,6 +5,12 @@ import org.lwjgl.util.vector.Vector3f;
 import eu.tankernn.gameEngine.util.IPositionable;
 
 public class Light implements ILight, IPositionable {
+	
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 1324162074890620897L;
+	
 	private Vector3f position;
 	private Vector3f color;
 	private Vector3f attenuation = new Vector3f(1, 0, 0);

+ 3 - 7
src/main/java/eu/tankernn/gameEngine/entities/Player.java

@@ -30,13 +30,13 @@ public class Player extends Entity3D {
 
 	public void move() {
 		checkInputs();
-		super.increaseRotation(new Vector3f(0, currentTurnSpeed * DisplayManager.getFrameTimeSeconds(), 0));
-		super.generateVelocity(currentSpeed);
+		super.getState().increaseRotation(new Vector3f(0, currentTurnSpeed * DisplayManager.getFrameTimeSeconds(), 0));
+		super.getState().generateVelocity(currentSpeed);
 	}
 	
 	private void jump() {
 		if (!this.isInAir()) {
-			this.velocity.y = JUMP_POWER;
+			getVelocity().y = JUMP_POWER;
 			
 			// source.play(jumpSoundBuffer);
 		}
@@ -72,10 +72,6 @@ public class Player extends Entity3D {
 		}
 	}
 	
-	public boolean isInAir() {
-		return this.position.y > terrain.getTerrainHeightByWorldPos(this.position.x, this.position.z);
-	}
-	
 	public float getHeight() {
 		return getBoundingBox().getSize().y;
 	}

+ 1 - 1
src/main/java/eu/tankernn/gameEngine/entities/PlayerCamera.java

@@ -46,7 +46,7 @@ public class PlayerCamera extends Camera {
 			if (Mouse.isButtonDown(1)) { // Right click
 				float targetRot = this.angleAroundPlayer + this.lockedPosition;
 				float delta = targetRot - rot.y;
-				player.increaseRotation(new Vector3f(0, delta, 0));
+				player.getState().increaseRotation(new Vector3f(0, delta, 0));
 			}
 		} else {
 			if (this.isLocked) {

+ 2 - 2
src/main/java/eu/tankernn/gameEngine/entities/npc/RoamingBehavior.java

@@ -18,7 +18,7 @@ public class RoamingBehavior extends Behavior {
 	@Override
 	public void update() {
 		if (targetPosition == null) {
-			entity.generateVelocity(0);
+			entity.getState().generateVelocity(0);
 			if (standbyTime > 0f)
 				standbyTime -= DisplayManager.getFrameTimeSeconds();
 			else
@@ -30,7 +30,7 @@ public class RoamingBehavior extends Behavior {
 				standbyTime = (float) (Math.random() * 10);
 			}
 			entity.getRotation().y = (float) Math.toDegrees(Math.atan2(direction.x, direction.y));
-			entity.generateVelocity(speed);
+			entity.getState().generateVelocity(speed);
 		}
 	}
 	

+ 6 - 6
src/main/java/eu/tankernn/gameEngine/entities/projectiles/Projectile.java

@@ -1,6 +1,6 @@
 package eu.tankernn.gameEngine.entities.projectiles;
 
-import java.util.List;
+import java.util.Collection;
 
 import org.lwjgl.util.vector.Vector3f;
 
@@ -19,7 +19,7 @@ public abstract class Projectile extends Entity3D {
 	public Projectile(TerrainPack terrain, TexturedModel model, Vector3f position, Vector3f velocity, float range, AABB boundingBox, ParticleSystem particleSystem) {
 		super(model, position, boundingBox, terrain);
 		this.particleSystem = particleSystem;
-		this.velocity = velocity;
+		this.getVelocity().set(velocity);
 		this.range = range;
 		this.startPosition = new Vector3f(position);
 	}
@@ -29,17 +29,17 @@ public abstract class Projectile extends Entity3D {
 		particleSystem.setPosition(this.getPosition());
 		
 		if (this.terrain != null) {
-			this.position.y = Math.max(this.position.y, 5 + terrain.getTerrainHeightByWorldPos(position.x, position.z));
+			getPosition().y = Math.max(getPosition().y, 5 + terrain.getTerrainHeightByWorldPos(getPosition().x, getPosition().z));
 		}
 		
-		Vector3f distance = Vector3f.sub(position, startPosition, null);
+		Vector3f distance = Vector3f.sub(getPosition(), startPosition, null);
 		if (distance.length() > range) {
 			kill();
 		}
 	}
 	
-	public void checkCollision(List<Entity3D> entities) {
-		entities.stream().filter((e) -> AABB.collides(e.getBoundingBox(), Projectile.this.getBoundingBox())).forEach(this::onCollision);
+	public void checkCollision(Collection<Entity3D> collection) {
+		collection.stream().filter((e) -> AABB.collides(e.getBoundingBox(), Projectile.this.getBoundingBox())).forEach(this::onCollision);
 	}
 
 	protected void kill() {

+ 4 - 2
src/main/java/eu/tankernn/gameEngine/entities/projectiles/TargetedProjectile.java

@@ -22,8 +22,10 @@ public class TargetedProjectile extends Projectile {
 	
 	@Override
 	public void update() {
-		Vector3f direction = (Vector3f) Vector3f.sub(target.getPosition(), this.getPosition(), null).normalise();
-		this.velocity = (Vector3f) direction.scale(speed);
+		// Store direction in velocity vector
+		Vector3f.sub(target.getPosition(), this.getPosition(), this.getVelocity());
+		// Normalize and scale velocity vector
+		getVelocity().normalise().scale(speed);
 		super.update();
 	}
 	

+ 3 - 3
src/main/java/eu/tankernn/gameEngine/loader/Loader.java

@@ -169,7 +169,7 @@ public class Loader {
 		boundingBoxes.put(model.id, new AABB(entityData.getMeshData()));
 		JointsData skeletonData = entityData.getJointsData();
 		Joint headJoint = new Joint(skeletonData.headJoint);
-		return new AnimatedModel(model, texture, headJoint, skeletonData.jointCount);
+		return new AnimatedModel(model.id, model, texture, headJoint, skeletonData.jointCount);
 	}
 	
 	public void readModelSpecification(InternalFile file) throws IOException {
@@ -227,13 +227,13 @@ public class Loader {
 			
 			if (cachedRawModels.containsKey(modelFile)) {
 				model = cachedRawModels.get(modelFile);
-				models.put(id, new TexturedModel(model, modelTexture));
+				models.put(id, new TexturedModel(id, model, modelTexture));
 			} else {
 				switch (modelFile.getExtension()) {
 				case "obj":
 					model = loadOBJ(modelFile);
 					cachedRawModels.put(modelFile, model);
-					models.put(id, new TexturedModel(model, modelTexture));
+					models.put(id, new TexturedModel(id, model, modelTexture));
 					break;
 				case "dae":
 					AnimatedModel animatedModel = loadDAE(modelFile, modelTexture);

+ 7 - 1
src/main/java/eu/tankernn/gameEngine/loader/models/TexturedModel.java

@@ -4,10 +4,12 @@ import eu.tankernn.gameEngine.loader.textures.ModelTexture;
 import eu.tankernn.gameEngine.renderEngine.Vao;
 
 public class TexturedModel {
+	private final int id;
 	private Vao rawModel;
 	private ModelTexture texture;
 	
-	public TexturedModel(Vao rawModel, ModelTexture texture) {
+	public TexturedModel(int id, Vao rawModel, ModelTexture texture) {
+		this.id = id;
 		this.rawModel = rawModel;
 		this.texture = texture;
 	}
@@ -19,4 +21,8 @@ public class TexturedModel {
 	public ModelTexture getTexture() {
 		return texture;
 	}
+
+	public int getId() {
+		return id;
+	}
 }

+ 17 - 0
src/main/java/eu/tankernn/gameEngine/multiplayer/LoginRequest.java

@@ -0,0 +1,17 @@
+package eu.tankernn.gameEngine.multiplayer;
+
+import java.io.Serializable;
+
+public class LoginRequest implements Serializable {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 2245699524120454081L;
+	
+	public final String username;
+
+	public LoginRequest(String username) {
+		this.username = username;
+	}
+
+}

+ 21 - 0
src/main/java/eu/tankernn/gameEngine/multiplayer/LoginResponse.java

@@ -0,0 +1,21 @@
+package eu.tankernn.gameEngine.multiplayer;
+
+import java.io.Serializable;
+
+import eu.tankernn.gameEngine.entities.EntityState;
+
+public class LoginResponse implements Serializable{
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -2261275286169003120L;
+	
+	public final boolean success;
+	public final EntityState playerState;
+
+	public LoginResponse(boolean success, EntityState playerState) {
+		this.success = success;
+		this.playerState = playerState;
+	}
+
+}

+ 42 - 0
src/main/java/eu/tankernn/gameEngine/multiplayer/WorldState.java

@@ -0,0 +1,42 @@
+package eu.tankernn.gameEngine.multiplayer;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import eu.tankernn.gameEngine.entities.EntityState;
+import eu.tankernn.gameEngine.entities.ILight;
+
+public class WorldState implements Serializable {
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 4777990467291918367L;
+	
+	private final int seed;
+	private List<ILight> lights = new ArrayList<>();
+	private List<EntityState> entities = new ArrayList<>();
+
+	public WorldState(int seed) {
+		this.seed = seed;
+	}
+	
+	public WorldState(int seed, List<ILight> lights, List<EntityState> entities) {
+		this(seed);
+		this.lights = lights;
+		this.entities = entities;
+	}
+
+	public int getSeed() {
+		return seed;
+	}
+
+	public List<ILight> getLights() {
+		return lights;
+	}
+
+	public List<EntityState> getEntities() {
+		return entities;
+	}
+
+}

+ 5 - 0
src/main/java/eu/tankernn/gameEngine/particles/Sun.java

@@ -8,6 +8,11 @@ import eu.tankernn.gameEngine.entities.ILight;
 
 public class Sun implements IParticle, ILight {
 
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = -4224790312871439692L;
+
 	private static final float SUN_DIS = 50;// fairly arbitrary - but make sure
 											// it doesn't go behind skybox
 

+ 3 - 2
src/main/java/eu/tankernn/gameEngine/renderEngine/MasterRenderer.java

@@ -7,6 +7,7 @@ import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
 import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -155,8 +156,8 @@ public class MasterRenderer {
 		terrains.add(terrain);
 	}
 
-	public void renderShadowMap(List<Entity3D> entityList, ILight sun) {
-		for (Entity3D e : entityList) {
+	public void renderShadowMap(Collection<Entity3D> collection, ILight sun) {
+		for (Entity3D e : collection) {
 			processEntity(e);
 		}
 		shadowMapRenderer.render(entities, sun);

+ 4 - 3
src/main/java/eu/tankernn/gameEngine/renderEngine/Scene.java

@@ -1,5 +1,6 @@
 package eu.tankernn.gameEngine.renderEngine;
 
+import java.util.Collection;
 import java.util.List;
 
 import eu.tankernn.gameEngine.entities.Camera;
@@ -10,7 +11,7 @@ import eu.tankernn.gameEngine.renderEngine.skybox.Skybox;
 import eu.tankernn.gameEngine.terrains.TerrainPack;
 
 public class Scene {
-	private List<Entity3D> entities;
+	private Collection<Entity3D> entities;
 	private TerrainPack terrainPack;
 	private List<ILight> lights;
 	private Camera camera;
@@ -18,7 +19,7 @@ public class Scene {
 	
 	private Texture environmentMap;
 	
-	public Scene(List<Entity3D> entities, TerrainPack terrainPack, List<ILight> lights, Camera camera, Skybox sky) {
+	public Scene(Collection<Entity3D> entities, TerrainPack terrainPack, List<ILight> lights, Camera camera, Skybox sky) {
 		this.entities = entities;
 		this.terrainPack = terrainPack;
 		this.lights = lights;
@@ -27,7 +28,7 @@ public class Scene {
 		this.environmentMap = Texture.newEmptyCubeMap(128);
 	}
 
-	public List<Entity3D> getEntities() {
+	public Collection<Entity3D> getEntities() {
 		return entities;
 	}
 

+ 189 - 188
src/main/java/eu/tankernn/gameEngine/util/MousePicker.java

@@ -1,188 +1,189 @@
-package eu.tankernn.gameEngine.util;
-
-import java.util.List;
-
-import org.lwjgl.input.Mouse;
-import org.lwjgl.opengl.Display;
-import org.lwjgl.util.vector.Matrix4f;
-import org.lwjgl.util.vector.Vector2f;
-import org.lwjgl.util.vector.Vector3f;
-import org.lwjgl.util.vector.Vector4f;
-
-import eu.tankernn.gameEngine.entities.Entity3D;
-import eu.tankernn.gameEngine.loader.models.AABB;
-import eu.tankernn.gameEngine.renderEngine.gui.GuiTexture;
-import eu.tankernn.gameEngine.terrains.Terrain;
-import eu.tankernn.gameEngine.terrains.TerrainPack;
-
-public class MousePicker {
-	private static final int RECURSION_COUNT = 200;
-	private static final float RAY_RANGE = 600;
-	
-	private Vector3f currentRay = new Vector3f();
-	
-	private ICamera camera;
-	
-	private Vector3f currentTerrainPoint;
-	private Entity3D currentEntity;
-	
-	private GuiTexture currentGui;
-	
-	public MousePicker(ICamera cam) {
-		camera = cam;
-	}
-	
-	public Entity3D getCurrentEntity() {
-		return currentEntity;
-	}
-	
-	public Vector3f getCurrentTerrainPoint() {
-		return currentTerrainPoint;
-	}
-	
-	public Vector3f getCurrentRay() {
-		return currentRay;
-	}
-	
-	public GuiTexture getCurrentGui() {
-		return currentGui;
-	}
-	
-	public void update(TerrainPack terrains, List<Entity3D> entities, List<GuiTexture> guis) {
-		currentRay = calculateMouseRay();
-		currentGui = calculateGuiTexture(guis);
-		if (intersectionInRange(terrains, 0, RAY_RANGE, currentRay)) {
-			currentTerrainPoint = binarySearch(terrains, 0, 0, RAY_RANGE, currentRay);
-		} else {
-			currentTerrainPoint = null;
-		}
-		
-		for (Entity3D e: entities) {
-			if (entityInstersect(e)) {
-				currentEntity = e;
-				return;
-			}
-		}
-		currentEntity = null;
-	}
-	
-	private Vector3f calculateMouseRay() {
-		float mouseX = Mouse.getX();
-		float mouseY = Mouse.getY();
-		Vector2f normalizedCoords = getNormalisedDeviceCoordinates(mouseX, mouseY);
-		Vector4f clipCoords = new Vector4f(normalizedCoords.x, normalizedCoords.y, -1.0f, 1.0f);
-		Vector4f eyeCoords = toEyeCoords(clipCoords);
-		Vector3f worldRay = toWorldCoords(eyeCoords);
-		return worldRay;
-	}
-	
-	private Vector3f toWorldCoords(Vector4f eyeCoords) {
-		Matrix4f invertedView = Matrix4f.invert(camera.getViewMatrix(), null);
-		Vector4f rayWorld = Matrix4f.transform(invertedView, eyeCoords, null);
-		Vector3f mouseRay = new Vector3f(rayWorld.x, rayWorld.y, rayWorld.z);
-		mouseRay.normalise();
-		return mouseRay;
-	}
-	
-	private Vector4f toEyeCoords(Vector4f clipCoords) {
-		Matrix4f invertedProjection = Matrix4f.invert(camera.getProjectionMatrix(), null);
-		Vector4f eyeCoords = Matrix4f.transform(invertedProjection, clipCoords, null);
-		return new Vector4f(eyeCoords.x, eyeCoords.y, -1f, 0f);
-	}
-	
-	private Vector2f getNormalisedDeviceCoordinates(float mouseX, float mouseY) {
-		float x = (2.0f * mouseX) / Display.getWidth() - 1f;
-		float y = (2.0f * mouseY) / Display.getHeight() - 1f;
-		return new Vector2f(x, y);
-	}
-	
-	// GUI Intersect
-	
-	private GuiTexture calculateGuiTexture(List<GuiTexture> guis) {
-		float mouseX = Mouse.getX();
-		float mouseY = Mouse.getY();
-		Vector2f mouseCoords = getNormalisedDeviceCoordinates(mouseX, mouseY);
-		
-		for (GuiTexture gui: guis) {
-			float posX = gui.getPosition().x;
-			float posY = gui.getPosition().y;
-			float scaleX = gui.getSize().x;
-			float scaleY = gui.getSize().y;
-			
-			if (mouseCoords.x > posX - scaleX && mouseCoords.x < posX + scaleX && mouseCoords.y > posY - scaleY && mouseCoords.y < posY + scaleY) {
-				return gui;
-			}
-		}
-		return null;
-	}
-	
-	// #### Entity intersect ####
-	
-	public boolean entityInstersect(Entity3D entity) {
-		AABB box = entity.getBoundingBox();
-		Vector3f dirfrac = new Vector3f();
-		
-		dirfrac.x = 1.0f / currentRay.x;
-		dirfrac.y = 1.0f / currentRay.y;
-		dirfrac.z = 1.0f / currentRay.z;
-		// lb is the corner of AABB with minimal coordinates - left bottom, rt
-		// is maximal corner
-		// camera.getPosition() is origin of ray
-		
-		float t1 = (box.getLb().x - camera.getPosition().x) * dirfrac.x;
-		float t2 = (box.getRt().x - camera.getPosition().x) * dirfrac.x;
-		float t3 = (box.getLb().y - camera.getPosition().y) * dirfrac.y;
-		float t4 = (box.getRt().y - camera.getPosition().y) * dirfrac.y;
-		float t5 = (box.getLb().z - camera.getPosition().z) * dirfrac.z;
-		float t6 = (box.getRt().z - camera.getPosition().z) * dirfrac.z;
-		
-		float tmin = Math.max(Math.max(Math.min(t1, t2), Math.min(t3, t4)), Math.min(t5, t6));
-		float tmax = Math.min(Math.min(Math.max(t1, t2), Math.max(t3, t4)), Math.max(t5, t6));
-		
-		// if tmax < 0, ray (line) is intersecting AABB, but whole AABB is
-		// behind us
-		if (tmax < 0) {
-			return false;
-		}
-		
-		// if tmin > tmax, ray doesn't intersect AABB
-		if (tmin > tmax) {
-			return false;
-		}
-		
-		return true;
-	}
-	
-	// #### Terrain intersect ####
-	
-	private Vector3f getPointOnRay(Vector3f ray, float distance) {
-		Vector3f camPos = camera.getPosition();
-		Vector3f start = new Vector3f(camPos.x, camPos.y, camPos.z);
-		Vector3f scaledRay = new Vector3f(ray.x * distance, ray.y * distance, ray.z * distance);
-		return Vector3f.add(start, scaledRay, null);
-	}
-	
-	private Vector3f binarySearch(TerrainPack terrains, int count, float start, float finish, Vector3f ray) {
-		float half = start + ((finish - start) / 2f);
-		if (count >= RECURSION_COUNT) {
-			Vector3f endPoint = getPointOnRay(ray, half);
-			Terrain terrain = terrains.getTerrainByWorldPos(endPoint.getX(), endPoint.getZ());
-			return terrain != null ? endPoint : null;
-		}
-		if (intersectionInRange(terrains, start, half, ray)) {
-			return binarySearch(terrains, count + 1, start, half, ray);
-		} else {
-			return binarySearch(terrains, count + 1, half, finish, ray);
-		}
-	}
-	
-	private boolean intersectionInRange(TerrainPack terrains, float start, float finish, Vector3f ray) {
-		Vector3f startPoint = getPointOnRay(ray, start);
-		Vector3f endPoint = getPointOnRay(ray, finish);
-		return !isUnderGround(terrains, startPoint) && isUnderGround(terrains, endPoint);
-	}
-	
-	private boolean isUnderGround(TerrainPack terrains, Vector3f testPoint) {
-		return testPoint.y < terrains.getTerrainHeightByWorldPos(testPoint.x, testPoint.z);
-	}
-}
+package eu.tankernn.gameEngine.util;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.lwjgl.input.Mouse;
+import org.lwjgl.opengl.Display;
+import org.lwjgl.util.vector.Matrix4f;
+import org.lwjgl.util.vector.Vector2f;
+import org.lwjgl.util.vector.Vector3f;
+import org.lwjgl.util.vector.Vector4f;
+
+import eu.tankernn.gameEngine.entities.Entity3D;
+import eu.tankernn.gameEngine.loader.models.AABB;
+import eu.tankernn.gameEngine.renderEngine.gui.GuiTexture;
+import eu.tankernn.gameEngine.terrains.Terrain;
+import eu.tankernn.gameEngine.terrains.TerrainPack;
+
+public class MousePicker {
+	private static final int RECURSION_COUNT = 200;
+	private static final float RAY_RANGE = 600;
+	
+	private Vector3f currentRay = new Vector3f();
+	
+	private ICamera camera;
+	
+	private Vector3f currentTerrainPoint;
+	private Entity3D currentEntity;
+	
+	private GuiTexture currentGui;
+	
+	public MousePicker(ICamera cam) {
+		camera = cam;
+	}
+	
+	public Entity3D getCurrentEntity() {
+		return currentEntity;
+	}
+	
+	public Vector3f getCurrentTerrainPoint() {
+		return currentTerrainPoint;
+	}
+	
+	public Vector3f getCurrentRay() {
+		return currentRay;
+	}
+	
+	public GuiTexture getCurrentGui() {
+		return currentGui;
+	}
+	
+	public void update(TerrainPack terrains, Collection<Entity3D> collection, List<GuiTexture> guis) {
+		currentRay = calculateMouseRay();
+		currentGui = calculateGuiTexture(guis);
+		if (intersectionInRange(terrains, 0, RAY_RANGE, currentRay)) {
+			currentTerrainPoint = binarySearch(terrains, 0, 0, RAY_RANGE, currentRay);
+		} else {
+			currentTerrainPoint = null;
+		}
+		
+		for (Entity3D e: collection) {
+			if (entityInstersect(e)) {
+				currentEntity = e;
+				return;
+			}
+		}
+		currentEntity = null;
+	}
+	
+	private Vector3f calculateMouseRay() {
+		float mouseX = Mouse.getX();
+		float mouseY = Mouse.getY();
+		Vector2f normalizedCoords = getNormalisedDeviceCoordinates(mouseX, mouseY);
+		Vector4f clipCoords = new Vector4f(normalizedCoords.x, normalizedCoords.y, -1.0f, 1.0f);
+		Vector4f eyeCoords = toEyeCoords(clipCoords);
+		Vector3f worldRay = toWorldCoords(eyeCoords);
+		return worldRay;
+	}
+	
+	private Vector3f toWorldCoords(Vector4f eyeCoords) {
+		Matrix4f invertedView = Matrix4f.invert(camera.getViewMatrix(), null);
+		Vector4f rayWorld = Matrix4f.transform(invertedView, eyeCoords, null);
+		Vector3f mouseRay = new Vector3f(rayWorld.x, rayWorld.y, rayWorld.z);
+		mouseRay.normalise();
+		return mouseRay;
+	}
+	
+	private Vector4f toEyeCoords(Vector4f clipCoords) {
+		Matrix4f invertedProjection = Matrix4f.invert(camera.getProjectionMatrix(), null);
+		Vector4f eyeCoords = Matrix4f.transform(invertedProjection, clipCoords, null);
+		return new Vector4f(eyeCoords.x, eyeCoords.y, -1f, 0f);
+	}
+	
+	private Vector2f getNormalisedDeviceCoordinates(float mouseX, float mouseY) {
+		float x = (2.0f * mouseX) / Display.getWidth() - 1f;
+		float y = (2.0f * mouseY) / Display.getHeight() - 1f;
+		return new Vector2f(x, y);
+	}
+	
+	// GUI Intersect
+	
+	private GuiTexture calculateGuiTexture(List<GuiTexture> guis) {
+		float mouseX = Mouse.getX();
+		float mouseY = Mouse.getY();
+		Vector2f mouseCoords = getNormalisedDeviceCoordinates(mouseX, mouseY);
+		
+		for (GuiTexture gui: guis) {
+			float posX = gui.getPosition().x;
+			float posY = gui.getPosition().y;
+			float scaleX = gui.getSize().x;
+			float scaleY = gui.getSize().y;
+			
+			if (mouseCoords.x > posX - scaleX && mouseCoords.x < posX + scaleX && mouseCoords.y > posY - scaleY && mouseCoords.y < posY + scaleY) {
+				return gui;
+			}
+		}
+		return null;
+	}
+	
+	// #### Entity intersect ####
+	
+	public boolean entityInstersect(Entity3D entity) {
+		AABB box = entity.getBoundingBox();
+		Vector3f dirfrac = new Vector3f();
+		
+		dirfrac.x = 1.0f / currentRay.x;
+		dirfrac.y = 1.0f / currentRay.y;
+		dirfrac.z = 1.0f / currentRay.z;
+		// lb is the corner of AABB with minimal coordinates - left bottom, rt
+		// is maximal corner
+		// camera.getPosition() is origin of ray
+		
+		float t1 = (box.getLb().x - camera.getPosition().x) * dirfrac.x;
+		float t2 = (box.getRt().x - camera.getPosition().x) * dirfrac.x;
+		float t3 = (box.getLb().y - camera.getPosition().y) * dirfrac.y;
+		float t4 = (box.getRt().y - camera.getPosition().y) * dirfrac.y;
+		float t5 = (box.getLb().z - camera.getPosition().z) * dirfrac.z;
+		float t6 = (box.getRt().z - camera.getPosition().z) * dirfrac.z;
+		
+		float tmin = Math.max(Math.max(Math.min(t1, t2), Math.min(t3, t4)), Math.min(t5, t6));
+		float tmax = Math.min(Math.min(Math.max(t1, t2), Math.max(t3, t4)), Math.max(t5, t6));
+		
+		// if tmax < 0, ray (line) is intersecting AABB, but whole AABB is
+		// behind us
+		if (tmax < 0) {
+			return false;
+		}
+		
+		// if tmin > tmax, ray doesn't intersect AABB
+		if (tmin > tmax) {
+			return false;
+		}
+		
+		return true;
+	}
+	
+	// #### Terrain intersect ####
+	
+	private Vector3f getPointOnRay(Vector3f ray, float distance) {
+		Vector3f camPos = camera.getPosition();
+		Vector3f start = new Vector3f(camPos.x, camPos.y, camPos.z);
+		Vector3f scaledRay = new Vector3f(ray.x * distance, ray.y * distance, ray.z * distance);
+		return Vector3f.add(start, scaledRay, null);
+	}
+	
+	private Vector3f binarySearch(TerrainPack terrains, int count, float start, float finish, Vector3f ray) {
+		float half = start + ((finish - start) / 2f);
+		if (count >= RECURSION_COUNT) {
+			Vector3f endPoint = getPointOnRay(ray, half);
+			Terrain terrain = terrains.getTerrainByWorldPos(endPoint.getX(), endPoint.getZ());
+			return terrain != null ? endPoint : null;
+		}
+		if (intersectionInRange(terrains, start, half, ray)) {
+			return binarySearch(terrains, count + 1, start, half, ray);
+		} else {
+			return binarySearch(terrains, count + 1, half, finish, ray);
+		}
+	}
+	
+	private boolean intersectionInRange(TerrainPack terrains, float start, float finish, Vector3f ray) {
+		Vector3f startPoint = getPointOnRay(ray, start);
+		Vector3f endPoint = getPointOnRay(ray, finish);
+		return !isUnderGround(terrains, startPoint) && isUnderGround(terrains, endPoint);
+	}
+	
+	private boolean isUnderGround(TerrainPack terrains, Vector3f testPoint) {
+		return testPoint.y < terrains.getTerrainHeightByWorldPos(testPoint.x, testPoint.z);
+	}
+}