class MinecraftServer:
#     implements Runnable, ICommandListener, IServer
# {
# 
    logger = logger
#     public static HashMap field_6037_b = new HashMap();
#     private String hostname;
#     private int serverPort;
#     public NetworkListenThread networkServer;
#     public PropertyManager propertyManagerObj;
#     public WorldServer worldMngr[];
#     public long field_40027_f[];
#     public long field_40028_g[][];
#     public ServerConfigurationManager configManager;
#     private ConsoleCommandHandler commandHandler;
#     private boolean serverRunning;
#     public boolean serverStopped;
#     int deathTime;
#     public String currentTask;
#     public int percentDone;
#     private List playersOnline;
#     private List commands;
#     public EntityTracker entityTracker[];
#     public boolean onlineMode;
#     public boolean spawnPeacefulMobs;
#     public boolean pvpOn;
#     public boolean allowFlight;
#     public String motd;
#     private RConThreadQuery rconQueryThread;
#     private RConThreadMain rconMainThread;
# 
    def __init__(self):

#     {
        self.field_40027_f = [long(0)]*100;
        self.serverRunning = True;
        self.serverStopped = False;
        self.deathTime = 0;
        self.playersOnline = [];
        self.commands = []
        #self.entityTracker = new EntityTracker[3];
#     }
# 
    def startServer(self):
#         throws UnknownHostException
#     {
#         commandHandler = new ConsoleCommandHandler(this);
#         ThreadCommandReader threadcommandreader = new ThreadCommandReader(this);
#         threadcommandreader.setDaemon(true);
#         threadcommandreader.start();
#         ConsoleLogManager.init();
        self.logger.info("Starting minecraft server version 1.0.1")
#         if(Runtime.getRuntime().maxMemory() / 1024L / 1024L < 512L)
#         {
#             logger.warning("**** NOT ENOUGH RAM!");
#             logger.warning("To start the server with more ram, launch it as \"java -Xmx1024M -Xms1024M -jar minecraft_server.jar\"");
#         }
        self.logger.info("Loading properties")
        self.propertyManagerObj = PropertyManager("server.properties");
        hostname = self.propertyManagerObj.getStringProperty("server-ip", "")
        onlineMode = self.propertyManagerObj.getBooleanProperty("online-mode", True);
        spawnPeacefulMobs = self.propertyManagerObj.getBooleanProperty("spawn-animals", True);
        pvpOn = self.propertyManagerObj.getBooleanProperty("pvp", True);
        allowFlight = self.propertyManagerObj.getBooleanProperty("allow-flight", False);
        motd = self.propertyManagerObj.getStringProperty("motd", "A Minecraft Server");
        motd = motd.replace('\247', '$');

        inetaddress = None

        if len(hostname) > 0:
            inetaddress = socket.gethostbyname(hostname)

        serverPort = self.propertyManagerObj.getIntProperty("server-port", 25565)
        hnp = "*"
        if len(hostname) > 0:
            hnp = hostname
        self.logger.info("Starting Minecraft server on {}:{}".format(hnp,serverPort))
        try:
            self.networkServer = NetworkListenThread(self,inetaddress,serverPort)
        except IOError as ioexception:
            self.logger.warn("**** FAILED TO BIND TO PORT!");
            self.logger.warn("The exception was: %s" % ioexception);
            self.logger.warn("Perhaps a server is already running on that port?");
            return False;           

        if( not onlineMode):
            self.logger.warning("**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!");
            self.logger.warning("The server will make no attempt to authenticate usernames. Beware.");
            self.logger.warning("While this makes the game possible to play without internet access, it also opens up the ability for hackers to connect with any username they choose.");
            self.logger.warning("To change this, set \"online-mode\" to \"true\" in the server.settings file.");
#         configManager = new ServerConfigurationManager(this);
#         entityTracker[0] = new EntityTracker(this, 0);
#         entityTracker[1] = new EntityTracker(this, -1);
#         entityTracker[2] = new EntityTracker(this, 1);
#         long l = System.nanoTime();
#         String s = propertyManagerObj.getStringProperty("level-name", "world");
#         String s1 = propertyManagerObj.getStringProperty("level-seed", "");
#         long l1 = (new Random()).nextLong();
#         if(s1.length() > 0)
#         {
#             try
#             {
#                 long l2 = Long.parseLong(s1);
#                 if(l2 != 0L)
#                 {
#                     l1 = l2;
#                 }
#             }
#             catch(NumberFormatException numberformatexception)
#             {
#                 l1 = s1.hashCode();
#             }
#         }
#         logger.info((new StringBuilder()).append("Preparing level \"").append(s).append("\"").toString());
#         initWorld(new SaveConverterMcRegion(new File(".")), s, l1);
#         logger.info((new StringBuilder()).append("Done (").append(System.nanoTime() - l).append("ns)! For help, type \"help\" or \"?\"").toString());
#         if(propertyManagerObj.getBooleanProperty("enable-query", false))
#         {
#             logger.info("Starting GS4 status listener");
#             rconQueryThread = new RConThreadQuery(this);
#             rconQueryThread.startThread();
#         }
#         if(propertyManagerObj.getBooleanProperty("enable-rcon", false))
#         {
#             logger.info("Starting remote control listener");
#             rconMainThread = new RConThreadMain(this);
#             rconMainThread.startThread();
#         }
        return True
#     }
# 
#     private void initWorld(ISaveFormat isaveformat, String s, long l)
#     {
#         if(isaveformat.isOldSaveType(s))
#         {
#             logger.info("Converting map!");
#             isaveformat.convertMapFormat(s, new ConvertProgressUpdater(this));
#         }
#         worldMngr = new WorldServer[3];
#         field_40028_g = new long[worldMngr.length][100];
#         int i = propertyManagerObj.getIntProperty("gamemode", 0);
#         i = WorldSettings.validGameType(i);
#         logger.info((new StringBuilder()).append("Default game type: ").append(i).toString());
#         WorldSettings worldsettings = new WorldSettings(l, i, true, false);
#         SaveOldDir saveolddir = new SaveOldDir(new File("."), s, true);
#         for(int j = 0; j < worldMngr.length; j++)
#         {
#             byte byte0 = 0;
#             if(j == 1)
#             {
#                 byte0 = -1;
#             }
#             if(j == 2)
#             {
#                 byte0 = 1;
#             }
#             if(j == 0)
#             {
#                 worldMngr[j] = new WorldServer(this, saveolddir, s, byte0, worldsettings);
#             } else
#             {
#                 worldMngr[j] = new WorldServerMulti(this, saveolddir, s, byte0, worldsettings, worldMngr[0]);
#             }
#             worldMngr[j].addWorldAccess(new WorldManager(this, worldMngr[j]));
#             worldMngr[j].difficultySetting = propertyManagerObj.getIntProperty("difficulty", 1);
#             worldMngr[j].setAllowedSpawnTypes(propertyManagerObj.getBooleanProperty("spawn-monsters", true), spawnPeacefulMobs);
#             worldMngr[j].getWorldInfo().setGameType(i);
#             configManager.setPlayerManager(worldMngr);
#         }
# 
#         char c = '\304';
#         long l1 = System.currentTimeMillis();
# label0:
#         for(int k = 0; k < 1; k++)
#         {
#             logger.info((new StringBuilder()).append("Preparing start region for level ").append(k).toString());
#             if(k != 0 && !propertyManagerObj.getBooleanProperty("allow-nether", true))
#             {
#                 continue;
#             }
#             WorldServer worldserver = worldMngr[k];
#             ChunkCoordinates chunkcoordinates = worldserver.getSpawnPoint();
#             int i1 = -c;
#             do
#             {
#                 if(i1 > c || !serverRunning)
#                 {
#                     continue label0;
#                 }
#                 for(int j1 = -c; j1 <= c && serverRunning; j1 += 16)
#                 {
#                     long l2 = System.currentTimeMillis();
#                     if(l2 < l1)
#                     {
#                         l1 = l2;
#                     }
#                     if(l2 > l1 + 1000L)
#                     {
#                         int k1 = (c * 2 + 1) * (c * 2 + 1);
#                         int i2 = (i1 + c) * (c * 2 + 1) + (j1 + 1);
#                         outputPercentRemaining("Preparing spawn area", (i2 * 100) / k1);
#                         l1 = l2;
#                     }
#                     worldserver.chunkProviderServer.loadChunk(chunkcoordinates.posX + i1 >> 4, chunkcoordinates.posZ + j1 >> 4);
#                     while(worldserver.updatingLighting() && serverRunning) ;
#                 }
# 
#                 i1 += 16;
#             } while(true);
#         }
# 
#         clearCurrentTask();
#     }
# 
#     private void outputPercentRemaining(String s, int i)
#     {
#         currentTask = s;
#         percentDone = i;
#         logger.info((new StringBuilder()).append(s).append(": ").append(i).append("%").toString());
#     }
# 
#     private void clearCurrentTask()
#     {
#         currentTask = null;
#         percentDone = 0;
#     }
# 
#     private void saveServerWorld()
#     {
#         logger.info("Saving chunks");
#         for(int i = 0; i < worldMngr.length; i++)
#         {
#             WorldServer worldserver = worldMngr[i];
#             worldserver.saveWorld(true, null);
#             worldserver.func_30006_w();
#         }
# 
#     }
# 
    def stopServer(self):
        self.logger.info("Stopping server")
        self.serverRunning = False

        self.networkServer.shutdown()
#     private void stopServer()
#     {
#         logger.info("Stopping server");
#         if(configManager != null)
#         {
#             configManager.savePlayerStates();
#         }
#         for(int i = 0; i < worldMngr.length; i++)
#         {
#             WorldServer worldserver = worldMngr[i];
#             if(worldserver != null)
#             {
#                 saveServerWorld();
#             }
#         }
# 
#     }
# 
#     public void initiateShutdown()
#     {
#         serverRunning = false;
#     }
# 
    def run(self):
#        self.logger.info("I got called by {}".format(inspect.stack()))
        self.logger.info("running")
        try:
            if self.startServer():
                self.logger.info("after startserver")
                curt = time.clock()
                l1 = 0
                while self.serverRunning:
                    l2 = time.clock()
                    l3 = l2- curt
                    if l3 > 0.2:
                        self.logger.warn("Can't keep up! Did the system time change, or is the server overloaded?")
                        l3 = 0.2
                    elif l3 < 0:
                        self.logger.warn("Time ran backwards! Did the system time change?")
                        l3 = 0
                    l1 += l3
                    curt = l2
#                     if(worldMngr[0].isAllPlayersFullyAsleep())
#                     {
#                         doTick();
#                         l1 = 0L;
#                     } else
#                     {
                    while l1 > 0.005:
                        l1 -= 0.005
                        self.doTick()

#                     }
                    time.sleep(0.01)
#                     Thread.sleep(1L);
#                 }
#             } else
#             {
#                 while(serverRunning) 
#                 {
#                     commandLineParser();
#                     try
#                     {
#                         Thread.sleep(10L);
#                     }
#                     catch(InterruptedException interruptedexception)
#                     {
#                         interruptedexception.printStackTrace();
#                     }
#                 }
#             }
#         }
        except Exception as ex:
            self.logger.critical(traceback.format_exc())
#         catch(Throwable throwable1)
#         {
#             throwable1.printStackTrace();
#             logger.log(Level.SEVERE, "Unexpected exception", throwable1);
#             while(serverRunning) 
#             {
#                 commandLineParser();
#                 try
#                 {
#                     Thread.sleep(10L);
#                 }
#                 catch(InterruptedException interruptedexception1)
#                 {
#                     interruptedexception1.printStackTrace();
#                 }
#             }
#         }
#         finally
#         {
#             try
#             {
#                 stopServer();
#                 serverStopped = true;
#             }
#             catch(Throwable throwable2)
#             {
#                 throwable2.printStackTrace();
#             }
#             finally
#             {
#                 System.exit(0);
#             }
#         }
#     }

    def doTick(self):
        self.logger.info("ticking")
    def startServer(self):
#         throws UnknownHostException
#     {
#         commandHandler = new ConsoleCommandHandler(this);
#         ThreadCommandReader threadcommandreader = new ThreadCommandReader(this);
#         threadcommandreader.setDaemon(true);
#         threadcommandreader.start();
#         ConsoleLogManager.init();
        self.logger.info("Starting minecraft server version 1.0.1")
#         if(Runtime.getRuntime().maxMemory() / 1024L / 1024L < 512L)
#         {
#             logger.warning("**** NOT ENOUGH RAM!");
#             logger.warning("To start the server with more ram, launch it as \"java -Xmx1024M -Xms1024M -jar minecraft_server.jar\"");
#         }
        self.logger.info("Loading properties")
        self.propertyManagerObj = PropertyManager("server.properties");
        hostname = self.propertyManagerObj.getStringProperty("server-ip", "")
        onlineMode = self.propertyManagerObj.getBooleanProperty("online-mode", True);
        spawnPeacefulMobs = self.propertyManagerObj.getBooleanProperty("spawn-animals", True);
        pvpOn = self.propertyManagerObj.getBooleanProperty("pvp", True);
        allowFlight = self.propertyManagerObj.getBooleanProperty("allow-flight", False);
        motd = self.propertyManagerObj.getStringProperty("motd", "A Minecraft Server");
        motd = motd.replace('\247', '$');

        inetaddress = None

        if len(hostname) > 0:
            inetaddress = socket.gethostbyname(hostname)

        serverPort = self.propertyManagerObj.getIntProperty("server-port", 25565)
        hnp = "*"
        if len(hostname) > 0:
            hnp = hostname
        self.logger.info("Starting Minecraft server on {}:{}".format(hnp,serverPort))
        try:
            self.networkServer = NetworkListenThread(self,inetaddress,serverPort)
        except IOError as ioexception:
            self.logger.warn("**** FAILED TO BIND TO PORT!");
            self.logger.warn("The exception was: %s" % ioexception);
            self.logger.warn("Perhaps a server is already running on that port?");
            return False;           

        if( not onlineMode):
            self.logger.warning("**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!");
            self.logger.warning("The server will make no attempt to authenticate usernames. Beware.");
            self.logger.warning("While this makes the game possible to play without internet access, it also opens up the ability for hackers to connect with any username they choose.");
            self.logger.warning("To change this, set \"online-mode\" to \"true\" in the server.settings file.");
#         configManager = new ServerConfigurationManager(this);
#         entityTracker[0] = new EntityTracker(this, 0);
#         entityTracker[1] = new EntityTracker(this, -1);
#         entityTracker[2] = new EntityTracker(this, 1);
#         long l = System.nanoTime();
#         String s = propertyManagerObj.getStringProperty("level-name", "world");
#         String s1 = propertyManagerObj.getStringProperty("level-seed", "");
#         long l1 = (new Random()).nextLong();
#         if(s1.length() > 0)
#         {
#             try
#             {
#                 long l2 = Long.parseLong(s1);
#                 if(l2 != 0L)
#                 {
#                     l1 = l2;
#                 }
#             }
#             catch(NumberFormatException numberformatexception)
#             {
#                 l1 = s1.hashCode();
#             }
#         }
#         logger.info((new StringBuilder()).append("Preparing level \"").append(s).append("\"").toString());
#         initWorld(new SaveConverterMcRegion(new File(".")), s, l1);
#         logger.info((new StringBuilder()).append("Done (").append(System.nanoTime() - l).append("ns)! For help, type \"help\" or \"?\"").toString());
#         if(propertyManagerObj.getBooleanProperty("enable-query", false))
#         {
#             logger.info("Starting GS4 status listener");
#             rconQueryThread = new RConThreadQuery(this);
#             rconQueryThread.startThread();
#         }
#         if(propertyManagerObj.getBooleanProperty("enable-rcon", false))
#         {
#             logger.info("Starting remote control listener");
#             rconMainThread = new RConThreadMain(this);
#             rconMainThread.startThread();
#         }
        return True