package dangerzone;
/*
 * This code is copyright Richard H. Clark, TheyCallMeDanger, OreSpawn, 2015-2020.
 * You may use this code for reference for modding the DangerZone game program,
 * and are perfectly welcome to cut'n'paste portions for your mod as well.
 * DO NOT USE THIS CODE FOR ANY PURPOSE OTHER THAN MODDING FOR THE DANGERZONE GAME.
 * DO NOT REDISTRIBUTE THIS CODE. 
 * 
 * This copyright remains in effect until January 1st, 2021. 
 * At that time, this code becomes public domain.
 * 
 * WARNING: There are bugs. Big bugs. Little bugs. Every size in-between bugs.
 * This code is NOT suitable for use in anything other than this particular game. 
 * NO GUARANTEES of any sort are given, either express or implied, and Richard H. Clark, 
 * TheyCallMeDanger, OreSpawn are not responsible for any damages, direct, indirect, or otherwise. 
 * You should have made backups. It's your own fault for not making them.
 * 
 * NO ATTEMPT AT SECURITY IS MADE. This code is USE AT YOUR OWN RISK.
 * Regardless of what you may think, the reality is, that the moment you 
 * connected your computer to the Internet, Uncle Sam, among many others, hacked it.
 * DO NOT KEEP VALUABLE INFORMATION ON INTERNET-CONNECTED COMPUTERS.
 * Or your phone...
 * 
 */



import java.awt.Font;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.security.CodeSource;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Random;

import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.openal.AL;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.GLU;
import org.newdawn.slick.Color;
import org.newdawn.slick.TrueTypeFont;
import org.newdawn.slick.openal.SoundStore;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;
import org.newdawn.slick.util.ResourceLoader;

import dangerzone.blocks.BlockRotation;
import dangerzone.blocks.Blocks;
import dangerzone.entities.Entities;
import dangerzone.entities.EntityBlockItem;
import dangerzone.entities.EntityExp;
import dangerzone.entities.ModelEntityBlockItem;
import dangerzone.entities.ModelExp;
import dangerzone.entities.ModelHumanoid;
import dangerzone.gui.GuiInterface;
import dangerzone.gui.OverlayGUI;
import dangerzone.gui.PlayerChatGUI;
import dangerzone.gui.PlayerCommandGUI;
import dangerzone.gui.PlayerEscapeGUI;
import dangerzone.gui.PlayerHelpGUI;
import dangerzone.gui.PlayerInventoryGUI;
import dangerzone.items.Items;
import dangerzone.particles.Particles;
import dangerzone.threads.ChunkWriterThread;
import dangerzone.threads.CleanerThread;
import dangerzone.threads.EntityUpdateLoop;
import dangerzone.threads.LightingThread;
import dangerzone.threads.ParticleUpdateLoop;
import dangerzone.threads.Server;
import dangerzone.threads.ServerConnection;



public class DangerZone {
	

	public static String versionstring = "1.1";
	
	public static int gameover = 0;
	static String windowTitle = "Welcome to the Danger Zone...";
	static float deltaT = 0;
	public static String server_address = null;
	public static int server_port = 0;
	public static ChunkCache client_chunk_cache = null;
	public static ServerChunkCache server_chunk_cache = null;
	public static ChunkWriterThread chunkwriter = null;
	public static int screen_width = 1000;
	public static int screen_height = 600;
	public static Player player = null;
	public static World world = null;
	public static World server_world = null;
	public static String worldname = null; 
	public static String origworldname = null; 
	public static WorldRenderer wr = null;
	public static ServerConnection server_connection = null;
	public static Server server = null;
	public static WorldDecorators wds = null;
	public static Blocks all_the_blocks;
	public static Items all_the_items;
	public static Dimensions all_the_dimensions;
	public static Crafting all_the_recipies;
	public static DeskCrafting all_the_deskrecipies;
	public static Cooking all_the_cooking;
	public static Entities all_the_entities;
	public static Particles all_the_particles;
	public static Ores all_the_ores;
	public static Spawnlist all_the_spawns;
	public static CustomPackets all_the_custompackets;
	public static CommandHandlers all_the_handlers;
	public static Shredding all_the_shreds;
	private static Texture logotexture = null;
	public static int messagetimer = 0;
	public static String messagestring = null;
	public static int hotmessagetimer = 0;
	public static String hotmessagestring = null;
	public static String ghost_string = "Game Mode Ghost";
	public static String creative_string = "Game Mode Creative";
	public static String survival_string = "Game Mode Survival";
	public static int renderdistance = 24; //chunks
	public static int savedrenderdistance = 0;
	public static EntityUpdateLoop entityManager;
	public static ParticleUpdateLoop particleManager;
	public static int entityupdatedist = 16*24; //blocks!
	public static int max_entities = 8000;		//max entities allowed alive at any one time.
	public static DangerZoneBase base;
	public static int entityupdaterate = 16; //ms
	public static int serverentityupdaterate = 100; //ms
	public static boolean f3_on = false;
	public static volatile boolean f5_front = false;
	public static volatile boolean f5_back = false;
	public static boolean view_ores = false;
	public static String blockitemname = "DangerZone:BlockItem"; //Builtin
	public static boolean escape_options_menu = false;
	public static boolean mouse_grabbed = false;
	public static volatile GuiInterface current_gui = null;
	public static PlayerEscapeGUI escapegui = null;
	public static PlayerHelpGUI helpgui = null;
	public static PlayerChatGUI chatgui = null;
	public static PlayerCommandGUI commandgui = null;
	public static DZCommandHandler commandhandler = null;
	public static PlayerInventoryGUI inventorygui = null;
	public static EntityBlockItem blockitem = null;
	public static int packets_per_second = 0;
	public static int chunks_per_second = 0;
	public static int gofasttimer = 0;
	public static int gofast = 0;
	public static int flytimer = 0;
	public static int gofly = 0;
	public static volatile int do_hit_cycle = 0;
	public static volatile int do_food_cycle = 0;
	private static int volume_temp = 0;
	private static int music_volume_temp = 0;
	public static SoundManager soundmangler = null;
	public static boolean start_server = true;
	public static boolean start_client = true;
	private static long nowtime = 0;
	private static long thentime = 0;
	private static int pps = 0;
	public static Font awtfont = null;
	public static TrueTypeFont font = null;
	public static LightingThread lto = null;
	public static CleanerThread tidyup = null;
	public static String playername = "Player";
	public static Random rand = null;
	public static TextureMapper texmap = new TextureMapper();
	public static int fieldOfView;
	public static int fieldOfViewLast;
	public static int mouseSensitivity;
	public static float accel = 0.0f;
	public static volatile boolean doleftclick = false;
	public static volatile boolean dorightclick = false;
	public static boolean keepitsqueakyclean = false;
	public static boolean generatecaves = true;
	public static int mindrawlevel = 0;
	public static ModLoader modloader = null;
	public static String alt_texture_path = "";
	public static List<ModListEntry> all_the_mods = new ArrayList<ModListEntry>();
	public static boolean all_sides = true;
	public static boolean light_speed = true;
	public volatile static int new_dimension = 0;
	public volatile static double new_posx = 0;
	public volatile static double new_posy = 0;
	public volatile static double new_posz = 0;
	private static Chunk currchunk = null;
	private static boolean leftbuttondown = false;
	private static boolean rightbuttondown = false;
	private static int repeatdelay = 0;
	private static int chunkwaitdelay = 0;
	private static boolean nogui = false;
	public static volatile boolean dimension_change_in_progress = false; //because the chunk cleaner can sometimes clean entities while teleporting!
	public static OverlayGUI overlayguis = new OverlayGUI();
	public static boolean playnicely = false;
	public static boolean show_clouds = true;
	public static boolean show_rain = true;
	public volatile static int thundercount = 0;
	public static String crypted_password = null;
	public static boolean require_valid_passwords = true;
	public static int default_privs = 0;
	private static Properties configprop = new Properties();
	public static String nsserver_address = "45.40.134.151";
	public static int nsserver_port = 18669;
	public static int max_players_on_server = 10;
	public static volatile String connection_msg = null;
	public volatile static int connection_error = 0;
	public static float testx, testy, testz, testp, testw, testr;
	public static boolean show_server_stats = false;
	public static boolean bits64mode = true;
	public static boolean fullscreen = true;
	//public static boolean printit = false;


	
	public static void initGL() 
	{
		/* OpenGL */
		screen_width = Display.getWidth();
		screen_height = Display.getHeight();
		GL11.glViewport(0, 0, screen_width, screen_height); // Reset The Current Viewport
		GL11.glMatrixMode(GL11.GL_PROJECTION); // Select The Projection Matrix
		GL11.glLoadIdentity(); // Reset The Projection Matrix
		GLU.gluPerspective((float)fieldOfView, ((float) screen_width / (float) screen_height), 1.0f, 256.0f*renderdistance); // Calculate The Aspect Ratio Of The Window
		GL11.glMatrixMode(GL11.GL_MODELVIEW); // Select The Modelview Matrix
		GL11.glLoadIdentity(); // Reset The Modelview Matrix
//		GL11.glShadeModel(GL11.GL_SMOOTH); // Enables Smooth Shading
		GL11.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Black Background
		GL11.glClearDepth(1.0f); // Depth Buffer Setup
		GL11.glEnable(GL11.GL_DEPTH_TEST); // Enables Depth Testing
		GL11.glDepthFunc(GL11.GL_LEQUAL); // The Type Of Depth Test To Do
		GL11.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_FASTEST); // Fast Perspective Calculations
		GL11.glEnable(GL11.GL_TEXTURE_2D); 
		//transparent pixels do not write to the Z buffer!
		GL11.glAlphaFunc ( GL11.GL_GREATER, 0.1f ) ;
		GL11.glEnable ( GL11.GL_ALPHA_TEST ) ;		
	}
	
	
	/**
	 * @param args
	 */
	public static void main(String[] args) throws InterruptedException {
		long lasttime = System.currentTimeMillis();
		long currtime, tlong;
		
		String curdir = System.getProperty("user.dir");	
		File libfile = new File(curdir + "/DangerZone_lib/native");
		File stopfile = new File(curdir + "/stop.txt");
		if(libfile.exists())System.setProperty("org.lwjgl.librarypath", curdir + "/DangerZone_lib/native");
		
		
		CodeSource codeSource = DangerZone.class.getProtectionDomain().getCodeSource();
		try {
			File myfile = new File(codeSource.getLocation().toURI().getPath());
			//System.out.printf("Adding self as: %s\n",  myfile.getAbsolutePath());
			ModLoader.addFile(myfile); //add self to path so it will search here for resources!
		} catch (URISyntaxException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		bits64mode = (System.getProperty("os.arch").indexOf("64") != -1);
		
		if(args.length > 1){
			int arglen = args.length;
			int i;
			for(i=0;i<arglen;i++){
				//System.out.printf("arg %d = :%s\n", i, args[i]);
				if(args[i].contains("client")){
					start_server = false;
				}
				if(args[i].contains("server")){
					start_client = false;
				}
				if(args[i].contains("nogui")){
					nogui = true;
					start_client = false;
				}
				if(args[i].contains("stats")){
					show_server_stats = true;
				}
			}
			if(!start_client && !start_server){
				System.out.printf("Bad args?\n");
				System.exit(1);			
			}
		}else{
			start_client = true;
			start_server = true;
		}
		
		
		rand = new Random(lasttime);
		
		readConfig();
		
		if(!bits64mode){
			if(renderdistance > 16)renderdistance = 16;
			DangerZone.all_sides = false;
			System.out.printf("Aw man... 32-bit Java detected!\n");
		}
		
		if(!start_server){
			if(renderdistance > 20)renderdistance = 20;
		}
		
		if(start_server && !start_client){
			screen_width = 800;
			screen_height = 400;
		}

		/*
		DisplayMode originalmode = Display.getDesktopDisplayMode();
		try {
			Display.setDisplayMode(originalmode);
		} catch (LWJGLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		try {
			Display.setFullscreen(true);
		} catch (LWJGLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		*/
		
		if(!nogui){
			//System.out.printf("I am here!\n");
			try {				
				if(!fullscreen){
					Display.setDisplayMode(new DisplayMode(screen_width, screen_height));
					Display.setResizable(true);
				}else{
					DisplayMode originalmode = Display.getDesktopDisplayMode();
					Display.setDisplayMode(originalmode);
					Display.setFullscreen(true);
					screen_width = Display.getWidth();
					screen_height = Display.getHeight();
				}
				//Display.setVSyncEnabled(true);// -- CPU MONSTER!!!
				Display.setVSyncEnabled(false);
				Display.setTitle(windowTitle);				
				Display.create();
				Keyboard.create(); //Keyboard only works in same context as Display, because it needs Display.update()!!!
				Mouse.create(); //Same with mouse!
				SoundStore.get().init();
				initGL();
				
			} catch (LWJGLException e1) {
				System.out.printf("Something went horribly wrong with initialization!\n");
				e1.printStackTrace();
				System.exit(1);
			}

			showLogo(false);
			Display.update();


			if(font == null){
				awtfont = new Font("Times New Roman", Font.PLAIN, 24);
				font = new TrueTypeFont(awtfont, false);
			}
		}
		
		if(start_server){
			if(start_client){
				doStartSinglePlayer(); //fire it ALL up!
			}else{
				doStartServer(); //just the server stuff
			}
		}else{
			if(start_client){
				doStartClient(); //just the client stuff
			}else{
				System.out.printf("Hunh?\n");
				System.exit(1);		
			}
		}
		
		
		lasttime = System.currentTimeMillis();
		
		/*
		 * And finally, the main server/client loop...
		 */
		while(DangerZone.gameover == 0){
			
			if(stopfile.exists()){
				stopfile.delete();
				DangerZone.gameover = 1;
			}
			
			if(thundercount > 0){
				thundercount--;
			}
			
			if(start_client){				
								
				//TODO FIXME - find an interrupt/event vsync, not polled!
				//DO NOT use Display.sync()!!!
				currtime = System.currentTimeMillis();
				tlong = currtime - lasttime;
				tlong = 16 - tlong;
				
				if(wr.fps < 40){
					if(tlong < 4)tlong = 4; //give others at least a small chance to run!
				}
				if(wr.fps < 20){
					//Slower machines NEED this to process network packets!
					if(tlong < 8)tlong = 8; //give others at least a small chance to run!
				}

				if(tlong > 16)tlong = 16;

				if(tlong > 0)Thread.sleep(tlong);
				
				
								
				//Display.sync(60); //CPU MONSTER!

				//Fixed time loop 16ms ~= 60 frames per second
				currtime = System.currentTimeMillis();
				tlong = currtime - lasttime;
				lasttime = currtime;
				deltaT = tlong;
				if(deltaT < 8)deltaT = 8;
				if(deltaT > 256)deltaT = 256;
				deltaT = deltaT / 16; //Scale! - 16ms loops (60 hz)
				
				//System.out.printf("Delta = %f\n", deltaT);				

				/*
				 * Keep safe from teleports by making sure we have the current chunk!
				 */
				if(new_dimension != 0){
					player.posx = new_posx;
					player.posy = new_posy;
					player.posz = new_posz;
					player.dimension = new_dimension;
					new_dimension = 0;
				}
				chunkwaitdelay = 0;
				currchunk = world.chunkcache.getDecoratedChunk(player.dimension, (int)player.posx, 100, (int)player.posz); 
				while(chunkwaitdelay < 50 && currchunk == null){
					chunkwaitdelay++; //just in case it gets confused. timeout.
					Thread.sleep(100);
					currchunk = world.chunkcache.getDecoratedChunk(player.dimension, (int)player.posx, 100, (int)player.posz); 
				}
				currchunk = null;
				
				//Have to do player updates here. Seems to cause screen glitches if not...
				player.update(deltaT);
				if(gofasttimer > 0)gofasttimer--;
				if(flytimer > 0)flytimer--;

				//Draw the world!!!
				wr.renderWorld(world);

				if(current_gui != null){
					if(mouse_grabbed){
						Mouse.setGrabbed(false); //We be un-cat-like
						mouse_grabbed = false;
					}

					/*
					 * Get into menu mode...
					 */
					GL11.glDisable(GL11.GL_DEPTH_TEST);
					GL11.glMatrixMode(GL11.GL_PROJECTION);
					GL11.glPushMatrix();
					GL11.glLoadIdentity();
					//puts 0,0 (x,y) at lower left of screen!
					GL11.glOrtho(0,DangerZone.screen_width,0,DangerZone.screen_height,-320,320);
					GL11.glMatrixMode(GL11.GL_MODELVIEW);
					GL11.glPushMatrix();
					GL11.glLoadIdentity();
					GL11.glEnable(GL11.GL_TEXTURE_2D);
					GL11.glEnable(GL11.GL_BLEND);
					GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
					GL11.glColor3f(1.0f, 1.0f, 1.0f); //brighten things up a bit!

					current_gui.process();

					//back out of menu mode
					GL11.glDisable(GL11.GL_BLEND);
					GL11.glMatrixMode(GL11.GL_PROJECTION);
					GL11.glPopMatrix();
					GL11.glMatrixMode(GL11.GL_MODELVIEW);
					GL11.glPopMatrix();
					GL11.glEnable(GL11.GL_DEPTH_TEST);

				}else{
					if(OverlayGUI.guiarray[1] != null){
						/*
						 * Get into menu mode...
						 */
						GL11.glDisable(GL11.GL_DEPTH_TEST);
						GL11.glMatrixMode(GL11.GL_PROJECTION);
						GL11.glPushMatrix();
						GL11.glLoadIdentity();
						//puts 0,0 (x,y) at lower left of screen!
						GL11.glOrtho(0,DangerZone.screen_width,0,DangerZone.screen_height,-320,320);
						GL11.glMatrixMode(GL11.GL_MODELVIEW);
						GL11.glPushMatrix();
						GL11.glLoadIdentity();
						GL11.glEnable(GL11.GL_TEXTURE_2D);
						GL11.glEnable(GL11.GL_BLEND);
						GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
						GL11.glColor3f(1.0f, 1.0f, 1.0f); //brighten things up a bit!
						
						int ig;
						for(ig = 1;ig<OverlayGUI.guiMAX;ig++){
							if(OverlayGUI.guiarray[ig] != null){
								OverlayGUI.guiarray[ig].process();
							}
						}
						
						//back out of menu mode
						GL11.glDisable(GL11.GL_BLEND);
						GL11.glMatrixMode(GL11.GL_PROJECTION);
						GL11.glPopMatrix();
						GL11.glMatrixMode(GL11.GL_MODELVIEW);
						GL11.glPopMatrix();
						GL11.glEnable(GL11.GL_DEPTH_TEST);
					}
					if(!mouse_grabbed){
						Mouse.setGrabbed(true); //We be cat-like
						mouse_grabbed = true;
					}
					//Get some Keyboard and Mouse inputs
					doNormalInput();
				}

				// Force output to screen and fetch any new inputs....
				Display.update();

				//Close clicked?
				if (Display.isCloseRequested()) {
					DangerZone.gameover = 1;
				}
				//Someone resized the window!
				if(Display.wasResized() || fieldOfView != fieldOfViewLast){
					initGL();
					fieldOfViewLast = fieldOfView;
				}

				//And lastly, kick the sound effects into gear...
				soundmangler.playMusic();
				SoundStore.get().poll(0);
				
				if(player.deadflag){
					break; //will stop things slowly, later
				}
				
			}else{
				//is a server!
				Thread.sleep(100);
				nowtime = System.currentTimeMillis();
				if(nowtime - thentime > 1000){				
					thentime = nowtime;
					pps = DangerZone.packets_per_second;
					DangerZone.packets_per_second = 0;
				}
				
				if(!nogui){
					
					showLogo(false);

					/*
					 * Get into menu mode...
					 */
					GL11.glDisable(GL11.GL_DEPTH_TEST);
					GL11.glMatrixMode(GL11.GL_PROJECTION);
					GL11.glPushMatrix();
					GL11.glLoadIdentity();
					//puts 0,0 (x,y) at lower left of screen!
					GL11.glOrtho(0,DangerZone.screen_width,0,DangerZone.screen_height,-320,320);
					GL11.glMatrixMode(GL11.GL_MODELVIEW);
					GL11.glPushMatrix();
					GL11.glLoadIdentity();
					GL11.glEnable(GL11.GL_TEXTURE_2D);
					GL11.glColor3f(1.0f, 1.0f, 1.0f); //brighten things up a bit!

					GL11.glPushMatrix();
					GL11.glTranslatef(10, screen_height-10, 0f); 
					GL11.glRotatef(180.0f, 1.0f, 0.0f, 0.0f); // Don't ask me why, but the text is upside down! Flip it!		
					font.drawString(0, 0, String.format("Players: %d", server.server_thread_list.size()), Color.gray);		
					GL11.glPopMatrix();

					GL11.glPushMatrix();
					GL11.glTranslatef(10, screen_height-60, 0f); 
					GL11.glRotatef(180.0f, 1.0f, 0.0f, 0.0f); // Don't ask me why, but the text is upside down! Flip it!		
					font.drawString(0, 0, String.format("Packets per Second: %d", pps), Color.gray);		
					GL11.glPopMatrix();

					GL11.glPushMatrix();
					GL11.glTranslatef(10, screen_height-110, 0f); 
					GL11.glRotatef(180.0f, 1.0f, 0.0f, 0.0f); // Don't ask me why, but the text is upside down! Flip it!		
					font.drawString(0, 0, String.format("Entities: %d", DangerZone.server.entityManager.entity_list.size()), Color.gray);		
					GL11.glPopMatrix();

					//back out of menu mode
					GL11.glMatrixMode(GL11.GL_PROJECTION);
					GL11.glPopMatrix();
					GL11.glMatrixMode(GL11.GL_MODELVIEW);
					GL11.glPopMatrix();
					GL11.glEnable(GL11.GL_DEPTH_TEST);


					Display.update();

					//Close clicked?
					if (Display.isCloseRequested()) {
						DangerZone.gameover = 1;
					}
					//Someone resized the window!
					if(Display.wasResized()){
						initGL();
					}	

					while (Keyboard.next()) {	
						//System.out.printf("Key!\n");
						if (Keyboard.getEventKey() == Keyboard.KEY_ESCAPE && Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)){
							if(Keyboard.getEventKeyState()){
								DangerZone.gameover = 1;
							}
						}
					}
				}
			}
			
		}

		/*
		 * Game is over... shut down gracefully and save things properly...
		 */
		if(start_client){
			
			//Player died??????
			if(player.deadflag){
				String oops = "Oops. You died...";
				player.setHealth(-1.1f);
				/*
				 * Get into menu mode...
				 */
				GL11.glDisable(GL11.GL_DEPTH_TEST);
				GL11.glMatrixMode(GL11.GL_PROJECTION);
				GL11.glPushMatrix();
				GL11.glLoadIdentity();
				//puts 0,0 (x,y) at lower left of screen!
				GL11.glOrtho(0,DangerZone.screen_width,0,DangerZone.screen_height,-320,320);
				GL11.glMatrixMode(GL11.GL_MODELVIEW);
				GL11.glPushMatrix();
				GL11.glLoadIdentity();
				GL11.glEnable(GL11.GL_TEXTURE_2D);
				GL11.glColor3f(1.0f, 1.0f, 1.0f); //brighten things up a bit!
				
				GL11.glPushMatrix();
				GL11.glTranslatef((screen_width/2)-(oops.length()*5), screen_height/2, 0f); 
				GL11.glRotatef(180.0f, 1.0f, 0.0f, 0.0f); // Don't ask me why, but the text is upside down! Flip it!		
				font.drawString(0, 0, oops, Color.white);		
				GL11.glPopMatrix();
				
				//back out of menu mode
				GL11.glMatrixMode(GL11.GL_PROJECTION);
				GL11.glPopMatrix();
				GL11.glMatrixMode(GL11.GL_MODELVIEW);
				GL11.glPopMatrix();
				GL11.glEnable(GL11.GL_DEPTH_TEST);
				
				Display.update(); //draw! Duh!
				
				Thread.sleep(2000);
		
			}	
			//Tell the server!
			player.server_connection.sendDisconnect();
			DangerZone.gameover = 1; //start shutting things down!		
			showLogo(player.deadflag);
			Display.update();
			writeConfig(player.deadflag);
			Thread.sleep(2000);

		}
		
		//Say bye bye!		
		Thread.sleep(500);	//Pretend we're busy doing real work.
		
		if(start_server){
			Thread.sleep(100);
			saveWorld(server_world);

			while(chunkwriter.getSize() > 0){
				Thread.sleep(100);
			}
			server_world.serverchunkcache.shutdown();
			Thread.sleep(200);
			
			//Close and save all the players!		
			server.savePlayers();
			Thread.sleep(200);
			
		}
		Thread.sleep(500);	//Pretend we're busy doing real work.

		if(!nogui){
			Mouse.destroy();
			Keyboard.destroy();
			Display.destroy();
			AL.destroy();
		}

		//System.out.printf("I am NOT here!\n");
		System.exit(0);
	}	
	
	public static boolean K_next(){
		return Keyboard.next();
	}
	
	public static int K_getEventKey(){
		return Keyboard.getEventKey();
	}
	
	public static boolean K_getEventKeyState(){
		return Keyboard.getEventKeyState();
	}
	
	public static boolean K_isKeyDown(int key){
		return Keyboard.isKeyDown(key);
	}
	
	public static boolean M_next(){
		return Mouse.next();
	}
	
	public static int M_getEventButton(){
		return Mouse.getEventButton();
	}
	
	public static boolean M_getEventButtonState(){
		return Mouse.getEventButtonState();
	}
	
	public static int M_getEventX(){
		return Mouse.getEventX();
	}
	
	public static int M_getEventY(){
		return Mouse.getEventY();
	}
	
	public static int M_getX(){
		return Mouse.getX();
	}
	
	public static int M_getY(){
		return Mouse.getY();
	}
	
	
	public static void setActiveGui(GuiInterface g){
		if(current_gui == null){
			current_gui = g;
			do_hit_cycle = 0;
			do_food_cycle = 0;
			repeatdelay = 0;
			doleftclick = false;
			dorightclick = false;
			leftbuttondown = false;
			rightbuttondown = false;
		}
	}
	
	public static void clearActiveGui(){
			current_gui = null;
	}
	
	private static void doNormalInput(){
		float dx, dy;
		int i;
		accel -= 0.05f;
		if(accel < 0.1f)accel = 0.1f;
		
		//Keyboard input checks
		while (Keyboard.next()) {	
			//System.out.printf("Key!\n");
			if (Keyboard.getEventKey() == Keyboard.KEY_ESCAPE && Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)){
				if(Keyboard.getEventKeyState()){
					setActiveGui(escapegui);
				}
			}
			if (Keyboard.getEventKey() == Keyboard.KEY_H && Keyboard.isKeyDown(Keyboard.KEY_H)){
				if(Keyboard.getEventKeyState()){
					setActiveGui(helpgui);
				}
			}
			if (Keyboard.getEventKey() == Keyboard.KEY_E && Keyboard.isKeyDown(Keyboard.KEY_E)){
				if(Keyboard.getEventKeyState()){
					setActiveGui(inventorygui);
				}
			}
			if (Keyboard.getEventKey() == Keyboard.KEY_T && Keyboard.isKeyDown(Keyboard.KEY_T)){
				if(Keyboard.getEventKeyState()){
					setActiveGui(chatgui);
				}
			}
			if (Keyboard.getEventKey() == Keyboard.KEY_SLASH && Keyboard.isKeyDown(Keyboard.KEY_SLASH)){
				if(Keyboard.getEventKeyState()){
					setActiveGui(commandgui);
				}
			}
			if (Keyboard.getEventKey() == Keyboard.KEY_G && Keyboard.isKeyDown(Keyboard.KEY_G)){
				//change gamemode
				if(player.getGameMode() == GameModes.SURVIVAL){
					player.server_connection.changeGameMode(GameModes.GHOST);
					//player.setGameMode(GameModes.GHOST);
					//DangerZone.messagetimer = 100;
					//DangerZone.messagestring = ghost_string;
				}else if(player.getGameMode() == GameModes.CREATIVE){
					player.server_connection.changeGameMode(GameModes.SURVIVAL);
					//player.setGameMode(GameModes.SURVIVAL);
					//DangerZone.messagetimer = 100;
					//DangerZone.messagestring = survival_string;	
				}else{
					player.server_connection.changeGameMode(GameModes.CREATIVE);
					//player.setGameMode(GameModes.CREATIVE);
					//DangerZone.messagetimer = 100;
					//DangerZone.messagestring = creative_string;					
				}
			}

			if (Keyboard.getEventKey() == Keyboard.KEY_X && Keyboard.isKeyDown(Keyboard.KEY_X)){
				if(WorldRenderer.focus_x != 0 && WorldRenderer.focus_y != 0 && WorldRenderer.focus_z != 0){
					int bid = world.getblock(player.dimension, WorldRenderer.focus_x, WorldRenderer.focus_y, WorldRenderer.focus_z);
					int meta = world.getblockmeta(player.dimension, WorldRenderer.focus_x, WorldRenderer.focus_y, WorldRenderer.focus_z);
					if(!Blocks.isLiquid(bid)){
						if((meta&BlockRotation.X_MASK) == BlockRotation.X_ROT_0){//around
							meta = (meta & BlockRotation.NOT_X_MASK) | BlockRotation.X_ROT_90;
						}else if((meta&BlockRotation.X_MASK) == BlockRotation.X_ROT_90){//around
							meta = (meta & BlockRotation.NOT_X_MASK) | BlockRotation.X_ROT_180;
						}else if((meta&BlockRotation.X_MASK) == BlockRotation.X_ROT_180){//around
							meta = (meta & BlockRotation.NOT_X_MASK) | BlockRotation.X_ROT_270;
						}else if((meta&BlockRotation.X_MASK) == BlockRotation.X_ROT_270){//around
							meta = (meta & BlockRotation.NOT_X_MASK) | BlockRotation.X_ROT_0;
						}
						//System.out.printf("meta now %x\n", meta);
						world.setblockandmeta(player.dimension, WorldRenderer.focus_x, WorldRenderer.focus_y, WorldRenderer.focus_z, bid, meta);
					}
				}
			}
			if (Keyboard.getEventKey() == Keyboard.KEY_C && Keyboard.isKeyDown(Keyboard.KEY_C)){
				if(WorldRenderer.focus_x != 0 && WorldRenderer.focus_y != 0 && WorldRenderer.focus_z != 0){
					int bid = world.getblock(player.dimension, WorldRenderer.focus_x, WorldRenderer.focus_y, WorldRenderer.focus_z);
					int meta = world.getblockmeta(player.dimension, WorldRenderer.focus_x, WorldRenderer.focus_y, WorldRenderer.focus_z);
					if(!Blocks.isLiquid(bid)){
						if((meta&BlockRotation.Y_MASK) == BlockRotation.Y_ROT_0){//around
							meta = (meta & BlockRotation.NOT_Y_MASK) | BlockRotation.Y_ROT_90;
						}else if((meta&BlockRotation.Y_MASK) == BlockRotation.Y_ROT_90){//around
							meta = (meta & BlockRotation.NOT_Y_MASK) | BlockRotation.Y_ROT_180;
						}else if((meta&BlockRotation.Y_MASK) == BlockRotation.Y_ROT_180){//around
							meta = (meta & BlockRotation.NOT_Y_MASK) | BlockRotation.Y_ROT_270;
						}else if((meta&BlockRotation.Y_MASK) == BlockRotation.Y_ROT_270){//around
							meta = (meta & BlockRotation.NOT_Y_MASK) | BlockRotation.Y_ROT_0;
						}
						//System.out.printf("meta now %x\n", meta);
						world.setblockandmeta(player.dimension, WorldRenderer.focus_x, WorldRenderer.focus_y, WorldRenderer.focus_z, bid, meta);
					}
				}
			}
			if (Keyboard.getEventKey() == Keyboard.KEY_V && Keyboard.isKeyDown(Keyboard.KEY_V)){
				if(WorldRenderer.focus_x != 0 && WorldRenderer.focus_y != 0 && WorldRenderer.focus_z != 0){
					int bid = world.getblock(player.dimension, WorldRenderer.focus_x, WorldRenderer.focus_y, WorldRenderer.focus_z);
					int meta = world.getblockmeta(player.dimension, WorldRenderer.focus_x, WorldRenderer.focus_y, WorldRenderer.focus_z);
					if(!Blocks.isLiquid(bid)){
						if((meta&BlockRotation.Z_MASK) == BlockRotation.Z_ROT_0){//around
							meta = (meta & BlockRotation.NOT_Z_MASK) | BlockRotation.Z_ROT_90;
						}else if((meta&BlockRotation.Z_MASK) == BlockRotation.Z_ROT_90){//around
							meta = (meta & BlockRotation.NOT_Z_MASK) | BlockRotation.Z_ROT_180;
						}else if((meta&BlockRotation.Z_MASK) == BlockRotation.Z_ROT_180){//around
							meta = (meta & BlockRotation.NOT_Z_MASK) | BlockRotation.Z_ROT_270;
						}else if((meta&BlockRotation.Z_MASK) == BlockRotation.Z_ROT_270){//around
							meta = (meta & BlockRotation.NOT_Z_MASK) | BlockRotation.Z_ROT_0;
						}
						//System.out.printf("meta now %x\n", meta);
						world.setblockandmeta(player.dimension, WorldRenderer.focus_x, WorldRenderer.focus_y, WorldRenderer.focus_z, bid, meta);
					}
				}
			}
			if (Keyboard.getEventKey() == Keyboard.KEY_Y && Keyboard.isKeyDown(Keyboard.KEY_Y)){
				//Find the next existing dimension!
				if(player.getGameMode() != GameModes.SURVIVAL){					
					if(Keyboard.isKeyDown(Keyboard.KEY_LCONTROL)||Keyboard.isKeyDown(Keyboard.KEY_RCONTROL)){
						Dimensions.DimensionArray[1].teleportToDimension(player, world, 1, (int)player.posx, (int)player.posy, (int)player.posz);
					}else{
						if(Keyboard.isKeyDown(Keyboard.KEY_LSHIFT)||Keyboard.isKeyDown(Keyboard.KEY_RSHIFT)){
							for(i=0;i<Dimensions.dimensionsMAX;i++){
								int id = (player.dimension-i-1)%Dimensions.dimensionsMAX;
								while(id<0)id += Dimensions.dimensionsMAX;
								if(Dimensions.DimensionArray[id] != null){
									//System.out.printf("requesting switch to %d:%s\n", id, Dimensions.DimensionArray[id].uniquename);
									Dimensions.DimensionArray[id].teleportToDimension(player, world, id, (int)player.posx, (int)player.posy, (int)player.posz);
									break;
								}
							}
						}else{
							for(i=0;i<Dimensions.dimensionsMAX;i++){
								int id = (player.dimension+i+1)%Dimensions.dimensionsMAX;
								if(Dimensions.DimensionArray[id] != null){
									//System.out.printf("requesting switch to %d:%s\n", id, Dimensions.DimensionArray[id].uniquename);
									Dimensions.DimensionArray[id].teleportToDimension(player, world, id, (int)player.posx, (int)player.posy, (int)player.posz);
									break;
								}
							}
						}
					}
				}
			}
			if (Keyboard.getEventKey() == Keyboard.KEY_Q && Keyboard.isKeyDown(Keyboard.KEY_Q)){
				//Tell the server to spawn an entity!
				InventoryContainer ic = player.getHotbar(player.gethotbarindex());
				if(ic != null){
					if(ic.count > 0){
						if(player.getGameMode() == GameModes.SURVIVAL)ic.count--;
						player.setHotbarChanged(player.gethotbarindex());
						/*
						//Lol! :)
						*/
						EntityBlockItem e = (EntityBlockItem)player.world.createEntityByName(blockitemname, 
								player.dimension, 
								player.posx+(float)Math.cos(Math.toRadians(player.rotation_yaw_head-90))*2*(float)Math.cos(Math.toRadians(player.rotation_pitch_head)),
								player.posy+(player.getHeight()*7/8) - (float)Math.sin(Math.toRadians(player.rotation_pitch_head))*2,
								player.posz+(float)Math.sin(Math.toRadians(player.rotation_yaw_head-90))*2*(float)Math.cos(Math.toRadians(player.rotation_pitch_head)));
						if(e != null){							
							e.fill(ic.bid, ic.iid, 1, ic.currentuses, ic.attributes, ic.icmeta);							
							e.rotation_pitch = player.world.rand.nextInt(360);
							e.rotation_yaw = player.world.rand.nextInt(360);
							e.rotation_roll = player.world.rand.nextInt(360);
							e.motionx = (float)Math.cos(Math.toRadians(player.rotation_yaw_head-90))*(float)Math.cos(Math.toRadians(player.rotation_pitch_head))*2; 
							e.motiony = -(float)Math.sin(Math.toRadians(player.rotation_pitch_head));
							e.motionz = (float)Math.sin(Math.toRadians(player.rotation_yaw_head-90))*(float)Math.cos(Math.toRadians(player.rotation_pitch_head))*2;
							player.world.spawnEntityInWorld(e);
						}
					}
					if(ic.count <= 0){
						player.setHotbar(player.gethotbarindex(), null);
					}
				}
			}
			if (Keyboard.getEventKey() == Keyboard.KEY_F2 && Keyboard.isKeyDown(Keyboard.KEY_F2)){
				Utils.screenshot(screen_width, screen_height);
				messagestring = "Screenshot!";
				messagetimer = 60;
			}
			if (Keyboard.getEventKey() == Keyboard.KEY_F3 && Keyboard.isKeyDown(Keyboard.KEY_F3)){
				//show some stats
				if(f3_on){
					f3_on = false;
				}else{
					f3_on = true;
				}
			}
			if (Keyboard.getEventKey() == Keyboard.KEY_F6 && Keyboard.isKeyDown(Keyboard.KEY_F6)){
				if(player.getGameMode() != GameModes.SURVIVAL){
					if(view_ores){
						view_ores = false;
					}else{
						view_ores = true;
					}
				}
			}
			if (Keyboard.getEventKey() == Keyboard.KEY_F5 && Keyboard.isKeyDown(Keyboard.KEY_F5)){
				//show me!!!
				if(f5_front){
					f5_front = false;
					f5_back = true;
				}else{
					if(f5_back){
						f5_back = false;
					}else{
						f5_front = true;
					}
				}
			}
			if (Keyboard.getEventKey() == Keyboard.KEY_1 && Keyboard.isKeyDown(Keyboard.KEY_1)){
				show_selection(0);
			}
			if (Keyboard.getEventKey() == Keyboard.KEY_2 && Keyboard.isKeyDown(Keyboard.KEY_2)){
				show_selection(1);
			}
			if (Keyboard.getEventKey() == Keyboard.KEY_3 && Keyboard.isKeyDown(Keyboard.KEY_3)){
				show_selection(2);
			}
			if (Keyboard.getEventKey() == Keyboard.KEY_4 && Keyboard.isKeyDown(Keyboard.KEY_4)){
				show_selection(3);
			}
			if (Keyboard.getEventKey() == Keyboard.KEY_5 && Keyboard.isKeyDown(Keyboard.KEY_5)){
				show_selection(4);
			}
			if (Keyboard.getEventKey() == Keyboard.KEY_6 && Keyboard.isKeyDown(Keyboard.KEY_6)){
				show_selection(5);
			}
			if (Keyboard.getEventKey() == Keyboard.KEY_7 && Keyboard.isKeyDown(Keyboard.KEY_7)){
				show_selection(6);
			}
			if (Keyboard.getEventKey() == Keyboard.KEY_8 && Keyboard.isKeyDown(Keyboard.KEY_8)){
				show_selection(7);
			}
			if (Keyboard.getEventKey() == Keyboard.KEY_9 && Keyboard.isKeyDown(Keyboard.KEY_9)){
				show_selection(8);
			}
			if (Keyboard.getEventKey() == Keyboard.KEY_0 && Keyboard.isKeyDown(Keyboard.KEY_0)){
				show_selection(9);
			}
			
			if (Keyboard.getEventKey() == Keyboard.KEY_W && Keyboard.isKeyDown(Keyboard.KEY_W)){
				if(gofasttimer != 0)gofast = 1;
				gofasttimer = 20;
			}
			
			if (Keyboard.getEventKey() == Keyboard.KEY_SPACE && Keyboard.isKeyDown(Keyboard.KEY_SPACE)){
				if(flytimer != 0)gofly = 1;
				flytimer = 20;
			}
		}

		if (Keyboard.isKeyDown(Keyboard.KEY_SPACE)){
			if(player.getGameMode() == GameModes.GHOST || player.isFlying()){
				player.motiony += 0.055f*deltaT*accel;
				accel += 0.1f;
				if(accel > 1)accel = 1;
			}else{
				if(player.getOnGround() || player.getInLiquid()){
					player.jump();
				}
				if(gofly != 0)player.tryfly();
			}
			player.setUp(true);
		}else{
			player.setUp(false);
			gofly = 0;
		}
		if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT)){
			if(player.getGameMode() == GameModes.GHOST || player.isFlying()){
				player.motiony -= 0.055f*deltaT*accel;
				accel += 0.1f;
				if(accel > 1)accel = 1;
			}
			player.setDown(true);
		}else{
			player.setDown(false);
		}
		if (Keyboard.isKeyDown(Keyboard.KEY_W)){
			float speed = 0.042f*accel;
			if(player.getGameMode() != GameModes.GHOST && 
					(world.getblock(player.dimension, (int)player.posx, (int)player.posy+2, (int)player.posz) != 0
			      || world.getblock(player.dimension, (int)player.posx, (int)player.posy+3, (int)player.posz) != 0 ))speed *= 0.70f;
			accel += 0.2f;
			if(accel > 1)accel = 1;
			if(player.getGameMode() == GameModes.GHOST || player.isFlying()){
				speed *= 4.0f;
			}else{
				if(gofast != 0)speed *= 2.5f;
			}
	    	float effectspeed;
	    	effectspeed = player.getTotalEffect(Effects.SPEED);
    		if(effectspeed > 1){
    			speed *= effectspeed;
    		}
    		effectspeed = player.getTotalEffect(Effects.SLOWNESS);
    		if(effectspeed > 1){
    			speed /= effectspeed;
    		}
			player.motionx += speed*Math.cos(Math.toRadians(player.rotation_yaw_head-90))*deltaT;
			player.motionz += speed*Math.sin(Math.toRadians(player.rotation_yaw_head-90))*deltaT;
			player.setForward(true);
		}else{
			gofast = 0;
			player.setForward(false);
		}
		if (Keyboard.isKeyDown(Keyboard.KEY_S)){
			float speed = 0.030f*accel;
			accel += 0.1f;
			if(accel > 1)accel = 1;
			if(player.getGameMode() == GameModes.GHOST)speed *= 2.5f;
			float effectspeed;
	    	effectspeed = player.getTotalEffect(Effects.SPEED);
    		if(effectspeed > 1){
    			speed *= effectspeed;
    		}
    		effectspeed = player.getTotalEffect(Effects.SLOWNESS);
    		if(effectspeed > 1){
    			speed /= effectspeed;
    		}
			player.motionx -= speed*Math.cos(Math.toRadians(player.rotation_yaw_head-90))*deltaT;
			player.motionz -= speed*Math.sin(Math.toRadians(player.rotation_yaw_head-90))*deltaT;
			player.setBackward(true);
		}else{
			player.setBackward(false);
		}
		if (Keyboard.isKeyDown(Keyboard.KEY_A)){
			float speed = 0.030f*accel;
			accel += 0.1f;
			if(accel > 1)accel = 1;
			if(player.getGameMode() == GameModes.GHOST)speed *= 2.5f;
			float effectspeed;
	    	effectspeed = player.getTotalEffect(Effects.SPEED);
    		if(effectspeed > 1){
    			speed *= effectspeed;
    		}
    		effectspeed = player.getTotalEffect(Effects.SLOWNESS);
    		if(effectspeed > 1){
    			speed /= effectspeed;
    		}
			player.motionx += speed*Math.cos(Math.toRadians(player.rotation_yaw_head-180))*deltaT;
			player.motionz += speed*Math.sin(Math.toRadians(player.rotation_yaw_head-180))*deltaT;
			player.setLeft(true);
		}else{
			player.setLeft(false);
		}
		if (Keyboard.isKeyDown(Keyboard.KEY_D)){
			float speed = 0.030f*accel;
			accel += 0.1f;
			if(accel > 1)accel = 1;
			if(player.getGameMode() == GameModes.GHOST)speed *= 2.5f;
			float effectspeed;
	    	effectspeed = player.getTotalEffect(Effects.SPEED);
    		if(effectspeed > 1){
    			speed *= effectspeed;
    		}
    		effectspeed = player.getTotalEffect(Effects.SLOWNESS);
    		if(effectspeed > 1){
    			speed /= effectspeed;
    		}
			player.motionx -= speed*Math.cos(Math.toRadians(player.rotation_yaw_head-180))*deltaT;
			player.motionz -= speed*Math.sin(Math.toRadians(player.rotation_yaw_head-180))*deltaT;
			player.setRight(true);
		}else{
			player.setRight(false);
		}
		
		//if (Keyboard.isKeyDown(Keyboard.KEY_P)){
		//	printit = true;
		//}
		
		
		/*
		if (Keyboard.isKeyDown(Keyboard.KEY_I)){
			if(Keyboard.isKeyDown(Keyboard.KEY_LSHIFT)){
				testx -= 0.1f;
			}else{
				testx += 0.1f;
			}
			System.out.printf("xyz, pwr = %f, %f, %f and %f, %f, %f\n", testx, testy, testz, testp, testw, testr);
		}
		if (Keyboard.isKeyDown(Keyboard.KEY_O)){
			if(Keyboard.isKeyDown(Keyboard.KEY_LSHIFT)){
				testy -= 0.1f;
			}else{
				testy += 0.1f;
			}
			System.out.printf("xyz, pwr = %f, %f, %f and %f, %f, %f\n", testx, testy, testz, testp, testw, testr);
		}
		if (Keyboard.isKeyDown(Keyboard.KEY_P)){
			if(Keyboard.isKeyDown(Keyboard.KEY_LSHIFT)){
				testz -= 0.1f;
			}else{
				testz += 0.1f;
			}
			System.out.printf("xyz, pwr = %f, %f, %f and %f, %f, %f\n", testx, testy, testz, testp, testw, testr);
		}
		if (Keyboard.isKeyDown(Keyboard.KEY_J)){
			if(Keyboard.isKeyDown(Keyboard.KEY_LSHIFT)){
				testp -= 0.1f;
			}else{
				testp += 0.1f;
			}
			System.out.printf("xyz, pwr = %f, %f, %f and %f, %f, %f\n", testx, testy, testz, testp, testw, testr);
		}
		if (Keyboard.isKeyDown(Keyboard.KEY_K)){
			if(Keyboard.isKeyDown(Keyboard.KEY_LSHIFT)){
				testw -= 0.1f;
			}else{
				testw += 0.1f;
			}
			System.out.printf("xyz, pwr = %f, %f, %f and %f, %f, %f\n", testx, testy, testz, testp, testw, testr);
		}
		if (Keyboard.isKeyDown(Keyboard.KEY_L)){
			if(Keyboard.isKeyDown(Keyboard.KEY_LSHIFT)){
				testr -= 0.1f;
			}else{
				testr += 0.1f;
			}
			System.out.printf("xyz, pwr = %f, %f, %f and %f, %f, %f\n", testx, testy, testz, testp, testw, testr);
		}
		*/
		

		//if (Keyboard.isKeyDown(Keyboard.KEY_Z)){
		//	player.rotation_roll -= 1;
		//	player.rotation_roll = player.rotation_roll%360;
		//}
		//if (Keyboard.isKeyDown(Keyboard.KEY_C)){
		//	player.rotation_roll += 1;
		//	player.rotation_roll = player.rotation_roll%360;
		//}
		
				
		while(Mouse.next()){
			//System.out.printf("Mouse!\n");
			dx = Mouse.getDX();
			dx *= 0.1f + (0.01f * (float)mouseSensitivity);
			dy = Mouse.getDY();
			dy *= 0.1f + (0.01f * (float)mouseSensitivity);

			player.rotation_yaw_head += dx;
			if(player.rotation_yaw_head < 0)player.rotation_yaw_head += 360;
			player.rotation_yaw_head = player.rotation_yaw_head%360;		
			player.rotation_pitch_head -= dy;
			player.rotation_pitch_head = player.rotation_pitch_head%360;
			//System.out.printf("pitch = %f\n", player.rotation_pitch_head);
			if(player.rotation_pitch_head > 180){
				if(player.rotation_pitch_head < 270)player.rotation_pitch_head = 270;
			}else{
				if(player.rotation_pitch_head > 90)player.rotation_pitch_head = 90;
			}
			

			if(Mouse.getEventButtonState()){	
				//System.out.printf("DOWN: %d\n", Mouse.getEventButton());
				//clicking is hard work! :)
				if(player.getGameMode() == GameModes.SURVIVAL)player.setHunger(player.getHunger()-0.01f);
				
				if(Mouse.getEventButton() == 0){
					leftbuttondown = true;	
					rightbuttondown = false;
				}	
				if(Mouse.getEventButton() == 1){
					rightbuttondown = true;
					leftbuttondown = false;	
				}
				if(Mouse.getEventButton() == 2)player.middleclick(world, WorldRenderer.focus_x, WorldRenderer.focus_y, WorldRenderer.focus_z, 0);

			}else{
				//Mouse button released!
				if(Mouse.getEventButton() >= 0){
					//System.out.printf("UP: %d\n", Mouse.getEventButton());
					do_hit_cycle = 0;
					do_food_cycle = 0;
					leftbuttondown = false;
					rightbuttondown = false;
					repeatdelay = 0;
				}
			}
				
			i = Mouse.getDWheel();
			if(i != 0){
				i = -i/120; //why why why????
				//System.out.printf("Mouse wheel %d\n", i);
				i += player.gethotbarindex();
				while(i < 0)i += 10;
				i %= 10;			
				show_selection(i);
			}

		}
		
		if(leftbuttondown || rightbuttondown){			
			if(repeatdelay == 0 || repeatdelay > 30){
				if(leftbuttondown){
					do_hit_cycle = 1;
					do_food_cycle = 0;
					doleftclick = true;
					dorightclick = false;
				}	
				if(rightbuttondown){
					InventoryContainer ic = player.getHotbar(player.gethotbarindex());
					if(WorldRenderer.focus_entity != null){
						do_hit_cycle = 1;
						do_food_cycle = 0;
						dorightclick = true;
						doleftclick = false;
					}else{
						if(ic != null && Items.isFood(ic.iid) && WorldRenderer.focus_x == 0 && WorldRenderer.focus_z == 0){
							do_food_cycle = 1;
							do_hit_cycle = 0;
							doleftclick = false;
							dorightclick = false;
						}else{
							do_hit_cycle = 1;
							do_food_cycle = 0;
							dorightclick = true;
							doleftclick = false;
						}
					}
				}				
			}
			repeatdelay++;
		}else{
			repeatdelay = 0;
		}

	}
	
	private static void showLogo(boolean redcolor){
		float x, y;
		
		if(logotexture == null){
			try {
				logotexture = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream("res/menus/logo.png"));
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		if(redcolor){
			GL11.glClearColor(1.0f, 0.0f, 0.0f, 0.0f);
		}else{
			GL11.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
		}
		GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer
		GL11.glLoadIdentity(); // Reset The View
		
		if(redcolor){
			GL11.glColor3f(1.0f, 0.2f, 0.2f);
		}else{			
			GL11.glColor3f(1.0f, 1.0f, 1.0f);
		}
		logotexture.bind();
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
		
		x = 200;
		y = 190;
		
		GL11.glTranslatef(0, 0, -600);
		
		GL11.glBegin(GL11.GL_QUADS);	
		GL11.glTexCoord2f(1,0);
		GL11.glVertex3f(x, y, 0.0f); // Top Right Of The Quad (Front)
		GL11.glTexCoord2f(0,0);
		GL11.glVertex3f(-x, y, 0.0f); // Top Left Of The Quad (Front)
		GL11.glTexCoord2f(0,1);
		GL11.glVertex3f(-x, -y, 0.0f); // Bottom Left Of The Quad (Front)
		GL11.glTexCoord2f(1,1);
		GL11.glVertex3f(x, -y, 0.0f); // Bottom Right Of The Quad (Front)
		GL11.glEnd(); // Done Drawing The Quad
		
		
		if(connection_msg != null){
			/*
			 * Get into menu mode...
			 */
			GL11.glDisable(GL11.GL_DEPTH_TEST);
			GL11.glMatrixMode(GL11.GL_PROJECTION);
			GL11.glPushMatrix();
			GL11.glLoadIdentity();
			//puts 0,0 (x,y) at lower left of screen!
			GL11.glOrtho(0,DangerZone.screen_width,0,DangerZone.screen_height,-320,320);
			GL11.glMatrixMode(GL11.GL_MODELVIEW);
			GL11.glPushMatrix();
			GL11.glLoadIdentity();
			GL11.glEnable(GL11.GL_TEXTURE_2D);
			GL11.glColor3f(1.0f, 1.0f, 1.0f); //brighten things up a bit!
			
			GL11.glPushMatrix();
			GL11.glTranslatef((screen_width/2)-(connection_msg.length()*5), screen_height/2, 0f); 
			GL11.glRotatef(180.0f, 1.0f, 0.0f, 0.0f); // Don't ask me why, but the text is upside down! Flip it!		
			font.drawString(0, 0, connection_msg, Color.white);		
			GL11.glPopMatrix();
			
			//back out of menu mode
			GL11.glMatrixMode(GL11.GL_PROJECTION);
			GL11.glPopMatrix();
			GL11.glMatrixMode(GL11.GL_MODELVIEW);
			GL11.glPopMatrix();
			GL11.glEnable(GL11.GL_DEPTH_TEST);
		}
		
	}
	

	
	private static void readConfig(){
		InputStream input = null;
		
		//defaults for when the file doesn't exist!
		server_address = "127.0.0.1";
		server_port = 18668;
		screen_width = 1024;
		screen_height = 600;
		renderdistance = 24;
		volume_temp = 10;
		music_volume_temp = 10;
		worldname = "my_world";
		playername ="Player";
		fieldOfView = 45;
		mouseSensitivity = 0;
		mindrawlevel = 0;
		all_sides = true;
		light_speed = true;
		keepitsqueakyclean = false;
		show_clouds = true;
		show_rain = true;
		crypted_password = "";
		fullscreen = true;
		
		try {	 
			input = new FileInputStream("DangerZone.properties");
	 
			// load a properties file
			configprop.load(input);
	 
			screen_width = Utils.getPropertyInt(configprop, "ScreenWidth", 500, 4000, 1024); //min, max, default
			screen_height = Utils.getPropertyInt(configprop, "ScreenHeight", 300, 3000, 600); //min, max, default
			renderdistance = Utils.getPropertyInt(configprop, "RenderDistance", 4, 24, 16); //min, max, default
			volume_temp = Utils.getPropertyInt(configprop, "Volume", 0, 10, 10); //min, max, default
			music_volume_temp = Utils.getPropertyInt(configprop, "MusicVolume", 0, 10, 10); //min, max, default
			server_address = Utils.getPropertyString(configprop, "ServerAddress", "127.0.0.1"); //
			server_port = Utils.getPropertyInt(configprop, "ServerPort", 0, Integer.MAX_VALUE, 18668); //min, max, default
			worldname = Utils.getPropertyString(configprop, "WorldName", "my_world"); 
			origworldname = worldname;
			playername = Utils.getPropertyString(configprop, "Playername", "Player");
			fieldOfView = Utils.getPropertyInt(configprop, "FieldOfView", 30, 80, 45); //min, max, default
			mouseSensitivity = Utils.getPropertyInt(configprop, "MouseSensitivity", -9, 9, 0); //min, max, default
			keepitsqueakyclean = Utils.getPropertyBoolean(configprop, "RunFinalization", false);
			mindrawlevel = Utils.getPropertyInt(configprop, "MinDrawLevel", 0, 40, 0); //min, max, default
			all_sides = Utils.getPropertyBoolean(configprop, "MaxGraphics", true);
			light_speed = Utils.getPropertyBoolean(configprop, "FastLighting", true);
			show_clouds = Utils.getPropertyBoolean(configprop, "ShowClouds", true);
			show_rain = Utils.getPropertyBoolean(configprop, "ShowRain", true);
			crypted_password = Utils.getPropertyString(configprop, "CryptedPassword", "");
			fullscreen = Utils.getPropertyBoolean(configprop, "FullScreen", true);


		} catch (IOException ex) {
			//ex.printStackTrace();
		}
		if (input != null) {
			try {
				input.close();
			} catch (IOException e) {
				//e.printStackTrace();
			}
		}	
		
		//Throttle for real server. Takes a huge load off the network.
		if(!start_server){
			if(renderdistance > 20){
				savedrenderdistance = renderdistance;
				renderdistance = 20;
			}
		}
		
	}
	
	private static void writeConfig(boolean pld){
		FileOutputStream output = null;
	 
		try {	 
			output = new FileOutputStream("DangerZone.properties");	 
			// set the properties value
			configprop.setProperty("ScreenWidth", String.format("%d", screen_width));
			configprop.setProperty("ScreenHeight", String.format("%d", screen_height));
			if(savedrenderdistance > renderdistance)renderdistance = savedrenderdistance; //restore
			configprop.setProperty("RenderDistance", String.format("%d", renderdistance));
			if(soundmangler != null){
				configprop.setProperty("Volume", String.format("%d", soundmangler.master_volume));
				configprop.setProperty("MusicVolume", String.format("%d", soundmangler.music_master_volume));
			}
			configprop.setProperty("ServerAddress", server_address);
			configprop.setProperty("ServerPort", String.format("%d", server_port));
			String strippedname = worldname;
			if(!start_server)strippedname = origworldname; //restore. don't save server world name!
			if(strippedname.contains("/")){
				int is = strippedname.lastIndexOf("/");
				strippedname = strippedname.substring(is+1);
			}
			configprop.setProperty("WorldName", strippedname);
			configprop.setProperty("Playername", playername);
			configprop.setProperty("FieldOfView", String.format("%d", fieldOfView));
			configprop.setProperty("MouseSensitivity", String.format("%d", mouseSensitivity));
			configprop.setProperty("RunFinalization", String.format("%s", keepitsqueakyclean?"true":"false"));
			configprop.setProperty("MinDrawLevel", String.format("%d", mindrawlevel));
			configprop.setProperty("MaxGraphics", String.format("%s", all_sides?"true":"false"));
			configprop.setProperty("FastLighting", String.format("%s", light_speed?"true":"false"));
			configprop.setProperty("ShowClouds", String.format("%s", show_clouds?"true":"false"));
			configprop.setProperty("ShowRain", String.format("%s", show_rain?"true":"false"));
			configprop.setProperty("PlayerDied", String.format("%s", pld?"true":"false"));
			configprop.setProperty("CryptedPassword", crypted_password);
			configprop.setProperty("FullScreen", String.format("%s", fullscreen?"true":"false"));
	 
			// save properties to project root folder
			configprop.store(output, null);
	 
		} catch (IOException io) {
			io.printStackTrace();
		}
		if (output != null) {
			try {
				output.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}		
	}
	
	

	
	public static void loadWorld(World w){
		InputStream input = null;
		Properties worldprop = new Properties();
		String filepath = new String();		
		filepath = String.format("worlds/%s/world.dat", DangerZone.worldname);	
		
		generatecaves = true;
		w.timetimer = 0;
		playnicely = false;
		require_valid_passwords = true;
		default_privs = 0; //
		max_players_on_server = 10;
		w.worldseed = System.currentTimeMillis();
		 
		try {	 
			input = new FileInputStream(filepath);	 
			// load a properties file
			worldprop.load(input);
			
			w.timetimer = Utils.getPropertyInt(worldprop, "TimeOfDay", 0, Integer.MAX_VALUE, 0); //min, max, default
			generatecaves = Utils.getPropertyBoolean(worldprop, "CaveGeneration", true);
			playnicely = Utils.getPropertyBoolean(worldprop, "PlayNicely", false);
			require_valid_passwords = Utils.getPropertyBoolean(worldprop, "RequireValidPassword", true);
			default_privs = Utils.getPropertyInt(worldprop, "DefaultPrivs", Integer.MIN_VALUE, Integer.MAX_VALUE, 0); //min, max, default
			max_players_on_server = Utils.getPropertyInt(worldprop, "MaxPlayers", 2, 255, 10); //min, max, default
			w.worldseed = Utils.getPropertyLong(worldprop, "WorldSeed", Long.MIN_VALUE, Long.MAX_VALUE, w.worldseed); //min, max, default

		} catch (IOException ex) {
			//ex.printStackTrace();
			input = null;
			//new world?

			FileOutputStream privoutput = null;
			filepath = String.format("worlds/%s/player_privs.dat", DangerZone.worldname);	
			File f = new File(filepath);		
			f.getParentFile().mkdirs();	
			Properties privprop = new Properties();
			privprop.setProperty(playername.toLowerCase(), String.format("%d", 0xffffffff)); //world creator gets all privs
			try {
				privoutput = new FileOutputStream(filepath);			 
				// save properties
				privprop.store(privoutput, null);	
				privoutput.close();
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}	

		}
		if (input != null) {
			try {
				input.close();
			} catch (IOException e) {
				//e.printStackTrace();
			}
		}
		
	}
	
	public static void saveWorld(World w){
		Properties worldprop = new Properties();
		FileOutputStream output = null;
		String filepath = new String();		
		filepath = String.format("worlds/%s/world.dat", DangerZone.worldname);	
		File f = new File(filepath);		
		f.getParentFile().mkdirs();	
		
		try {	
					
			worldprop.setProperty("TimeOfDay", String.format("%d", w.timetimer));
			worldprop.setProperty("CaveGeneration", String.format("%s", generatecaves?"true":"false"));
			worldprop.setProperty("PlayNicely", String.format("%s", playnicely?"true":"false"));
			worldprop.setProperty("RequireValidPassword", String.format("%s", require_valid_passwords?"true":"false"));
			worldprop.setProperty("DefaultPrivs", String.format("%d", default_privs));
			worldprop.setProperty("MaxPlayers", String.format("%d", max_players_on_server));
			worldprop.setProperty("WorldSeed", String.format("%d", w.worldseed));
			
			output = new FileOutputStream(filepath);	 
			// save properties
			worldprop.store(output, null);			
	 
		} catch (IOException io) {
			io.printStackTrace();
		}
		if (output != null) {
			try {
				output.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}		
	}
	
	public static String getWorldDirectoryPath()
	{
		return String.format("worlds/%s/", DangerZone.worldname);
	}
	
	public static String getAltTexturePath(){
		return alt_texture_path;
	}

	private static void doStartSinglePlayer(){
		
		connection_msg = "Starting a few things...";
		showLogo(false);
		Display.update();
		
		//SOUNDS!!! Woohoo!
		soundmangler = new SoundManager();
		soundmangler.master_volume = volume_temp;
		soundmangler.music_master_volume = music_volume_temp;

		//Make a place for chunks (client)		
		client_chunk_cache = new ChunkCache();  		//This caching mechanism is entirely different from...
		client_chunk_cache.passthru = true; //ONLY ONE ACTIVE CACHE!

		server_chunk_cache = new ServerChunkCache();	//this caching mechanism!
		chunkwriter = new ChunkWriterThread();
		Thread cwt = new Thread(chunkwriter);
		cwt.start();
		
		wds = new WorldDecorators();

		player = new Player(null);	

		player.model = new ModelHumanoid(); //Set renderer! (by hand)
		player.myname = playername;
		player.setPetName(playername);
		world = new World(client_chunk_cache, null);
		player.world = world; 


		//And a server world!
		server_world = new World(null, server_chunk_cache); //world for the server to use the server chunk cache!
		server_world.isServer = true; //yes we are!
		server_world.timetimer = 0;
		
		loadWorld(server_world);

		//Make a world renderer
		wr = new WorldRenderer();

		all_the_blocks = new Blocks();
		Blocks.load(); //Grab the known list from disk
		all_the_items = new Items();
		Items.load(); //Grab the known list from disk
		all_the_dimensions = new Dimensions();
		Dimensions.load(); //Grab the known list from disk

		//custom packets!
		all_the_custompackets = new CustomPackets();

		//make the crafting list
		all_the_recipies = new Crafting();
		all_the_deskrecipies = new DeskCrafting();

		//make the cooking list
		all_the_cooking = new Cooking();

		//make ore list holder
		all_the_ores = new Ores();

		//make spawn list holder
		all_the_spawns = new Spawnlist();
		
		//make command handler list
		all_the_handlers = new CommandHandlers();

		//particles!
		all_the_particles = new Particles();
		
		//make the shredding list
		all_the_shreds = new Shredding();

		//make a couple internal things we can't live without!
		all_the_entities = new Entities();
		blockitem = new EntityBlockItem(world);
		Entities.registerEntity(EntityBlockItem.class, blockitem.uniquename, new ModelEntityBlockItem());	//SPECIAL! Internal entity
		Entities.registerEntity(Player.class, "DangerZone:Player", new ModelHumanoid());					//SPECIAL! Internal entity
		Entities.registerEntity(EntityExp.class, "DangerZone:Experience", new ModelExp());					//SPECIAL! Internal entity

		//Load up the default dimensions, creatures, blocks, items, etc!
		base = new DangerZoneBase();
		base.registerThings();

		/*
		 * LOAD ALL OTHER MODS HERE!
		 */	
		connection_msg = "Loading mods...";
		showLogo(false);
		Display.update();
		
		modloader = new ModLoader();
		modloader.loadmods();

		base.postLoadProcessing();

		//FIXME TODO! If new world, copy mods to the created world directory.
		// If existing world, load mods from the world directory.
		//
		//FIXME TODO!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
		// 


		//Save all the new blocks if any
		Blocks.save();
		//Save all the new items if any
		Items.save();
		//Save all the new Dimensions if any
		Dimensions.save();


		try {
			Thread.sleep(200);
		} catch (InterruptedException e2) {
			e2.printStackTrace();
		} //No reason, just make it seem like we're doing something...

		connection_msg = "Firing up some threads...";
		showLogo(false);
		Display.update();

		entityManager = new EntityUpdateLoop();
		Thread it = new Thread(entityManager);	//Use to do gc and run updates to the entities...
		//it.setPriority(Thread.MAX_PRIORITY);
		it.start();	

		particleManager = new ParticleUpdateLoop();
		Thread pit = new Thread(particleManager);	//Use to do gc and run updates to the entities...
		//it.setPriority(Thread.MAX_PRIORITY);
		pit.start();	

		commandhandler = new DZCommandHandler();

		server = new Server();
		Thread st = new Thread(server);	//Fire up the actual SERVER side
		st.start();	
		
		while(server.entityManager == null){
			Thread.yield();
			try {
				Thread.sleep(500);
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			} //Let it all catch up a little...
		}

		player.getTexture(); //pre-load this here! It doesn't work in the server_connection thread!
		server_connection = new ServerConnection(player, false);
		Thread ct = new Thread(server_connection);	//Fire up server comms for the player
		ct.setPriority(Thread.MAX_PRIORITY); //Don't bog down the network!
		ct.start();	

		escapegui = new PlayerEscapeGUI();
		inventorygui = new PlayerInventoryGUI();
		chatgui = new PlayerChatGUI();
		helpgui = new PlayerHelpGUI();
		commandgui = new PlayerCommandGUI();
//		chestgui = new PlayerChestGUI();
//		furnacegui = new PlayerFurnaceGUI();

		int i = 0;
		while(server_connection.connected == 0 && i < 10){
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			} //Wait for the two to connect!
			i++;
		}
		if(i >= 10){
			System.exit(1);
		}

		//Lighting!!!
		lto = new LightingThread();
		Thread lt = new Thread(lto);
		lt.start();

		//What a MESS!!!
		tidyup = new CleanerThread();
		Thread clt = new Thread(tidyup);
		clt.setPriority(Thread.MAX_PRIORITY); //YES! Needed!!! Especially for slower machines to keep from running out of memory...
		clt.start();
		
		connection_msg = "Loading textures...";
		showLogo(false);
		Display.update();
		
		//load ALL block textures
		wr.loadBlockTextures();


		//refresh hotbar and inventory contents!!!!!
		for(i=0;i<10;i++){			
			player.setHotbarChanged(i);
		}
		for(i=0;i<50;i++){
			player.setInventoryChanged(i);
		}
		for(i=0;i<4;i++){
			player.setArmorChanged(i);
		}		
		//send current item held too!
		DangerZone.server_connection.inventoryUpdate(2, player.gethotbarindex(), null);
		
		connection_msg = "Waiting for first chunk...";
		showLogo(false);
		Display.update();
		
		//Wait for at least the chunk I am in before continuing!
		//tell it what dimension we are in, or it won't send us the chunk we are waiting for.		
		DangerZone.server_connection.sendPlayerEntityUpdate(player);
		Chunk c = null;
		while(c == null){
			c = player.world.chunkcache.getDecoratedChunk(player.dimension, (int)player.posx, 100, (int)player.posz);
			if(c != null)break;
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		c = null;

		//set this so the wr knows what dimension we are in. it is a flag to clear all VBOs when changed!
		wr.lastplayerdimension = player.dimension;

		//And finally... grab the mouse pointer!
		Mouse.setGrabbed(true); //We be cat-like
		mouse_grabbed = true;

		//we have to do this to help smooth the mouse motion...
		Thread thisthread = Thread.currentThread();
		thisthread.setPriority(Thread.MAX_PRIORITY);

		player.setRenderDist(renderdistance);
		connection_msg = null;
	}

	private static void doStartServer(){
		
		//SOUNDS!!! Woohoo! We don't play any in the server, just registration...
		soundmangler = new SoundManager();
		soundmangler.master_volume = volume_temp;
		soundmangler.music_master_volume = music_volume_temp;

		server_chunk_cache = new ServerChunkCache();
		chunkwriter = new ChunkWriterThread();
		Thread cwt = new Thread(chunkwriter);
		cwt.start();
		
		wds = new WorldDecorators();	

		//player = new Player(null);	
		
		//And a server world!
		server_world = new World(null, server_chunk_cache); //world for the server to use the server chunk cache!
		server_world.isServer = true; //yes we are!
		server_world.timetimer = 0;
		//player.world = server_world; 
		
		loadWorld(server_world);

		all_the_blocks = new Blocks();
		Blocks.load(); //Grab the known list from disk
		all_the_items = new Items();
		Items.load(); //Grab the known list from disk
		all_the_dimensions = new Dimensions();
		Dimensions.load(); //Grab the known list from disk

		//custom packets!
		all_the_custompackets = new CustomPackets();

		//make the crafting list
		all_the_recipies = new Crafting();
		all_the_deskrecipies = new DeskCrafting();

		//make the cooking list
		all_the_cooking = new Cooking();

		//make ore list holder
		all_the_ores = new Ores();

		//make spawn list holder
		all_the_spawns = new Spawnlist();
		
		//make command handler list
		all_the_handlers = new CommandHandlers();

		//particles!
		all_the_particles = new Particles();
		
		//make the shredding list
		all_the_shreds = new Shredding();

		//make a couple internal things we can't live without!
		all_the_entities = new Entities();
		blockitem = new EntityBlockItem(world);
		Entities.registerEntity(EntityBlockItem.class, blockitem.uniquename, new ModelEntityBlockItem());	//SPECIAL! Internal entity
		Entities.registerEntity(Player.class, "DangerZone:Player", new ModelHumanoid());						//SPECIAL! Internal entity
		Entities.registerEntity(EntityExp.class, "DangerZone:Experience", new ModelExp());					//SPECIAL! Internal entity

		//Load up the default dimensions, creatures, blocks, items, etc!
		base = new DangerZoneBase();
		base.registerThings();

		/*
		 * LOAD ALL OTHER MODS HERE!
		 */	
		modloader = new ModLoader();
		modloader.loadmods();

		base.postLoadProcessing();
		

		//Save all the new blocks if any
		Blocks.save();
		//Save all the new items if any
		Items.save();
		//Save all the new Dimensions if any
		Dimensions.save();

		try {
			Thread.sleep(200);
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		} //No reason, just make it seem like we're doing something...

		commandhandler = new DZCommandHandler();

		server = new Server();
		Thread st = new Thread(server);	//Fire up the actual SERVER side
		st.start();	

		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} //Let it all catch up a little...

		//What a MESS!!!
		tidyup = new CleanerThread();
		Thread clt = new Thread(tidyup);
		clt.setPriority(Thread.MAX_PRIORITY); //YES! Needed!!! Especially for slower machines to keep from running out of memory...
		clt.start();
		
	}

	private static void doStartClient(){
		
		connection_msg = "Starting a few things...";
		showLogo(false);
		Display.update();
		
		//SOUNDS!!! Woohoo!
		soundmangler = new SoundManager();
		soundmangler.master_volume = volume_temp;
		soundmangler.music_master_volume = music_volume_temp;

		//Make a place for chunks (client)		
		client_chunk_cache = new ChunkCache();  		//This caching mechanism is entirely different from...

		player = new Player(null);	
		player.model = new ModelHumanoid(); //Set renderer! (by hand)
		player.myname = playername;
		player.setPetName(playername);

		//Make a world!
		world = new World(client_chunk_cache, null);
		player.world = world; 

		//Make a world renderer
		wr = new WorldRenderer();
		//blocks!
		all_the_blocks = new Blocks();
		//items!
		all_the_items = new Items();
		//dimensions!
		all_the_dimensions = new Dimensions();
		//custom packets!
		all_the_custompackets = new CustomPackets();
		//make the crafting list
		all_the_recipies = new Crafting();
		all_the_deskrecipies = new DeskCrafting();
		//make the cooking list
		all_the_cooking = new Cooking();
		//make ore list holder
		all_the_ores = new Ores();
		//make spawn list holder
		all_the_spawns = new Spawnlist();
		//make command handler list
		all_the_handlers = new CommandHandlers();
		//particles!
		all_the_particles = new Particles();
		//make the shredding list
		all_the_shreds = new Shredding();

		//make a couple internal things we can't live without!
		all_the_entities = new Entities();
		blockitem = new EntityBlockItem(world);
		Entities.registerEntity(EntityBlockItem.class, blockitem.uniquename, new ModelEntityBlockItem());	//SPECIAL! Internal entity
		Entities.registerEntity(Player.class, "DangerZone:Player", new ModelHumanoid());						//SPECIAL! Internal entity
		Entities.registerEntity(EntityExp.class, "DangerZone:Experience", new ModelExp());					//SPECIAL! Internal entity

		escapegui = new PlayerEscapeGUI();
		inventorygui = new PlayerInventoryGUI();
		chatgui = new PlayerChatGUI();
		helpgui = new PlayerHelpGUI();
		commandgui = new PlayerCommandGUI();
//		chestgui = new PlayerChestGUI();
//		furnacegui = new PlayerFurnaceGUI();

		entityManager = new EntityUpdateLoop();
		Thread it = new Thread(entityManager);	//Use to do gc and run updates to the entities...
		//it.setPriority(Thread.MAX_PRIORITY);
		it.start();	

		particleManager = new ParticleUpdateLoop();
		Thread pit = new Thread(particleManager);	//Use to do gc and run updates to the entities...
		//it.setPriority(Thread.MAX_PRIORITY);
		pit.start();	


		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} //Let it all catch up a little...

		connection_msg = "Connecting to Server...";
		showLogo(false);
		Display.update();
		/*
		 * Connect to the server!
		 */
		player.getTexture(); //pre-load this here! It doesn't work in the server_connection thread!
		server_connection = new ServerConnection(player, true);
		Thread ct = new Thread(server_connection);	//Fire up server comms for the player
		ct.setPriority(Thread.MAX_PRIORITY); //Don't bog down the network!
		ct.start();	

		//we establish a partial connection with server to get world name and mods
		int i = 0;
		while(!server_connection.connectionInProgress && i < 20 && connection_error == 0){
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			} //Wait for the two to connect!
			i++;
		}
		if(i >= 20 || connection_error != 0){
			if(connection_error == 0)connection_msg = "Oops. Connection timed out.";
			showLogo(false);
			Display.update();
			try {
				Thread.sleep(4000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			} 
			System.exit(1);
		}
		
		DangerZone.worldname = server_connection.worldname;
						
		//Load up the default dimensions, creatures, blocks, items, etc!
		base = new DangerZoneBase();
		base.registerThings();
		
		connection_msg = "Loading Mods...";
		showLogo(false);
		Display.update();

		/*
		 * LOAD ALL OTHER MODS HERE!
		 */	
		modloader = new ModLoader();
		if(!modloader.loadmods(server_connection.modnames)){
			System.out.printf("Failed to load mods specified by server.\n");
			System.exit(1);
		}

		base.postLoadProcessing();
		
		//re-sync with server HERE!!!
		//the connection is waiting for this flag to receive the re-synced IDs, which we couldn't do because we need to load mods first.
		server_connection.waitformodstoload = false;
		
		//Now we wait for the resyncs to happen. Then we're done!
		i = 0;
		while(server_connection.connected == 0 && i < 20  && connection_error == 0){
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			} //Wait for the two to connect!
			i++;
		}
		if(i >= 20 || connection_error != 0){
			if(connection_error == 0)connection_msg = "Oops. Connection timed out.";
			showLogo(false);
			Display.update();
			try {
				Thread.sleep(4000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			} 
			System.exit(1);
		}

		connection_msg = "Loading textures...";
		showLogo(false);
		Display.update();
		
		//Now it's safe to load up ALL the block textures!	
		//We had to wait until server and client sync'ed up blockids!
		wr.loadBlockTextures();


		//refresh hotbar and inventory contents!!!!!
		for(i=0;i<10;i++){			
			player.setHotbarChanged(i);
		}
		for(i=0;i<50;i++){
			player.setInventoryChanged(i);
		}
		for(i=0;i<4;i++){
			player.setArmorChanged(i);
		}		
		//send current item held too!
		DangerZone.server_connection.inventoryUpdate(2, player.gethotbarindex(), null);
		
		//Wait for at least the chunk I am in before continuing!
		//tell it what dimension we are in, or it won't send us the chunk we are waiting for.
		connection_msg = "Waiting for first chunk...";
		showLogo(false);
		Display.update();
		
		DangerZone.server_connection.sendPlayerEntityUpdate(player);
		
		Chunk c = null;
		while(c == null){
			c = player.world.chunkcache.getDecoratedChunk(player.dimension, (int)player.posx, 100, (int)player.posz);
			if(c != null)break;
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		c = null;
		
		connection_msg = "Almost ready...";
		showLogo(false);
		Display.update();
		
		//Lighting!!!
		lto = new LightingThread();
		Thread lt = new Thread(lto);
		lt.start();

		//What a MESS!!!
		tidyup = new CleanerThread();
		Thread clt = new Thread(tidyup);
		clt.setPriority(Thread.MAX_PRIORITY); //YES! Needed!!! Especially for slower machines to keep from running out of memory...
		clt.start();

		//set this so the wr knows what dimension we are in. it is a flag to clear all VBOs when changed!
		wr.lastplayerdimension = player.dimension;

		//And finally... grab the mouse pointer!
		Mouse.setGrabbed(true); //We be cat-like
		mouse_grabbed = true;

		//we have to do this to help smooth the mouse motion...
		Thread thisthread = Thread.currentThread();
		thisthread.setPriority(Thread.MAX_PRIORITY);
		
		connection_msg = null;
		player.setRenderDist(renderdistance);
	}
	
	public static void show_selection(int i){
		
		player.sethotbarindex(i);
		InventoryContainer ic = player.getHotbar(i);
		if(ic != null && ic.count > 0){
			String hotstring = null;
			String[] hss = null;
			
			if(ic.bid != 0){
				hotstring = Blocks.getUniqueName(ic.bid);
			}
			if(ic.iid != 0){
				hotstring = Items.getUniqueName(ic.iid);
				int attackstrength = Items.getAttackStrength(ic.iid);
				if(attackstrength > 1){
					hotstring += String.format(" +%d", attackstrength);
				}					
			}
			if(hotstring != null){
				hss = hotstring.split(":");
				if(hss.length >= 2){
					hotmessagestring = hss[1];
					hotmessagetimer = 60;
				}
			}
		}else{
			hotmessagestring = null;
			hotmessagetimer = 0;
		}
	}
	
	public static void setMusicVolume(float newvol){
		SoundStore.get().setMusicVolume(newvol);
	}

}
