	public void startTickRun(int stage, long delta) throws InterruptedException {
		switch (stage) {
			case 0: {
				if(!spawnQueue.isEmpty()) {
					SpoutEntity e;
					while ((e = (SpoutEntity)spawnQueue.poll()) != null) {
						this.allocate(e);
						EntitySpawnEvent event = new EntitySpawnEvent(e, e.getPoint());
						Spout.getGame().getEventManager().callEvent(event);
						if (event.isCancelled()) {
							this.deallocate((SpoutEntity) e);
						}
					}
				}
				if(!removeQueue.isEmpty()) {
					SpoutEntity e;
					while ((e = (SpoutEntity)removeQueue.poll()) != null) {
						this.deallocate(e);
					}
				}
				float dt = delta / 1000.f;
				for (SpoutEntity ent : entityManager) {
					try {
						ent.onTick(dt);
					} catch (Exception e) {
						Spout.getGame().getLogger().severe("Unhandled exception during tick for " + ent.toString());
						e.printStackTrace();
					}
				}
				World world = getWorld();
				int[] updates;
				synchronized (queuedPhysicsUpdates) {
					updates = queuedPhysicsUpdates.toArray();
					queuedPhysicsUpdates.clear();
				}
				for (int key : updates) {
					int x = TByteTripleHashSet.key1(key);
					int y = TByteTripleHashSet.key2(key);
					int z = TByteTripleHashSet.key3(key);
					Chunk chunk = chunks[x >> Chunk.CHUNK_SIZE_BITS][y >> Chunk.CHUNK_SIZE_BITS][z >> Chunk.CHUNK_SIZE_BITS].get();
					if (chunk != null) {
						BlockMaterial material = chunk.getBlockMaterial(x, y, z);
						if (material.hasPhysics()) {
							material.onUpdate(world, x + (this.x << blockShifts), y + (this.y << blockShifts), z + (this.z << blockShifts));
						}
					}
				}
				for(int i = 0; i < LIGHT_PER_TICK; i++) {
					SpoutChunk toLight = lightingQueue.poll();
					if (toLight == null) break;
					if (toLight.isLoaded()) {
						toLight.processQueuedLighting();
					}
				}
				for(int i = 0; i < POPULATE_PER_TICK; i++) {
					Chunk toPopulate = populationQueue.poll();
					if (toPopulate == null) break;
					if (toPopulate.isLoaded()) {
						toPopulate.populate();
					}
				}
				Chunk toUnload = unloadQueue.poll();
				if (toUnload != null) {
					toUnload.unload(true);
				}
				break;
			}
			case 1: {
				for (SpoutEntity ent : entityManager) {
					try {
						ent.resolve();
					} catch (Exception e) {
						Spout.getGame().getLogger().severe("Unhandled exception during tick resolution for " + ent.toString());
						e.printStackTrace();
					}
				}
				break;
			}
			default: {
				throw new IllegalStateException("Number of states exceeded limit for SpoutRegion");
			}
		}
	}

	public void startTickRun(int stage, long delta) throws InterruptedException {
		switch (stage) {
			case 0: {
				if(!spawnQueue.isEmpty()) {
					SpoutEntity e;
					while ((e = (SpoutEntity)spawnQueue.poll()) != null) {
						this.allocate(e);
						EntitySpawnEvent event = new EntitySpawnEvent(e, e.getPosition());
						Spout.getGame().getEventManager().callEvent(event);
						if (event.isCancelled()) {
							this.deallocate((SpoutEntity) e);
						}
					}
				}
				if(!removeQueue.isEmpty()) {
					SpoutEntity e;
					while ((e = (SpoutEntity)removeQueue.poll()) != null) {
						this.deallocate(e);
					}
				}
				float dt = delta / 1000.f;
				for (SpoutEntity ent : entityManager) {
					try {
						ent.onTick(dt);
					} catch (Exception e) {
						Spout.getGame().getLogger().severe("Unhandled exception during tick for " + ent.toString());
						e.printStackTrace();
					}
				}
				World world = getWorld();
				int[] updates;
				synchronized (queuedPhysicsUpdates) {
					updates = queuedPhysicsUpdates.toArray();
					queuedPhysicsUpdates.clear();
				}
				for (int key : updates) {
					int x = TByteTripleHashSet.key1(key);
					int y = TByteTripleHashSet.key2(key);
					int z = TByteTripleHashSet.key3(key);
					Chunk chunk = chunks[x >> Chunk.CHUNK_SIZE_BITS][y >> Chunk.CHUNK_SIZE_BITS][z >> Chunk.CHUNK_SIZE_BITS].get();
					if (chunk != null) {
						BlockMaterial material = chunk.getBlockMaterial(x, y, z);
						if (material.hasPhysics()) {
							material.onUpdate(world, x + (this.x << blockShifts), y + (this.y << blockShifts), z + (this.z << blockShifts));
						}
					}
				}
				for(int i = 0; i < LIGHT_PER_TICK; i++) {
					SpoutChunk toLight = lightingQueue.poll();
					if (toLight == null) break;
					if (toLight.isLoaded()) {
						toLight.processQueuedLighting();
					}
				}
				for(int i = 0; i < POPULATE_PER_TICK; i++) {
					Chunk toPopulate = populationQueue.poll();
					if (toPopulate == null) break;
					if (toPopulate.isLoaded()) {
						toPopulate.populate();
					}
				}
				Chunk toUnload = unloadQueue.poll();
				if (toUnload != null) {
					toUnload.unload(true);
				}
				break;
			}
			case 1: {
				for (SpoutEntity ent : entityManager) {
					try {
						ent.resolve();
					} catch (Exception e) {
						Spout.getGame().getLogger().severe("Unhandled exception during tick resolution for " + ent.toString());
						e.printStackTrace();
					}
				}
				break;
			}
			default: {
				throw new IllegalStateException("Number of states exceeded limit for SpoutRegion");
			}
		}
	}
