package net.minecraft.server; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import net.lax1dude.eaglercraft.EaglerAdapter; import net.lax1dude.eaglercraft.sp.ipc.IPCPacket0DProgressUpdate; import net.minecraft.src.AxisAlignedBB; import net.minecraft.src.ChunkCoordinates; import net.minecraft.src.CommandBase; import net.minecraft.src.DispenserBehaviors; import net.minecraft.src.EntityPlayer; import net.minecraft.src.EntityPlayerMP; import net.minecraft.src.EnumGameType; import net.minecraft.src.ICommandManager; import net.minecraft.src.ICommandSender; import net.minecraft.src.ILogAgent; import net.minecraft.src.IProgressUpdate; import net.minecraft.src.ISaveHandler; import net.minecraft.src.IUpdatePlayerListBox; import net.minecraft.src.MinecraftException; import net.minecraft.src.Packet; import net.minecraft.src.Packet4UpdateTime; import net.minecraft.src.ServerCommandManager; import net.minecraft.src.ServerConfigurationManager; import net.minecraft.src.StringTranslate; import net.minecraft.src.StringUtils; import net.minecraft.src.World; import net.minecraft.src.WorldInfo; import net.minecraft.src.WorldManager; import net.minecraft.src.WorldServer; import net.minecraft.src.WorldServerMulti; import net.minecraft.src.WorldSettings; public abstract class MinecraftServer implements ICommandSender, Runnable { /** Instance of Minecraft Server. */ protected static MinecraftServer mcServer = null; /** List of names of players who are online. */ protected final List playersOnline = new ArrayList(); protected final ICommandManager commandManager; /** The server world instances. */ public WorldServer[] worldServers; /** The ServerConfigurationManager instance. */ protected ServerConfigurationManager serverConfigManager; /** * Indicates whether the server is running or not. Set to false to initiate a * shutdown. */ protected boolean serverRunning = true; /** Indicates to other classes that the server is safely stopped. */ protected boolean serverStopped = false; /** Incremented every tick. */ protected int tickCounter = 0; /** * The task the server is currently working on(and will output on * outputPercentRemaining). */ protected String currentTask; /** The percentage of the current task finished so far. */ protected int percentDone; /** True if the server has animals turned on. */ protected boolean canSpawnAnimals; protected boolean canSpawnNPCs; /** Indicates whether PvP is active on the server or not. */ protected boolean pvpEnabled; /** Determines if flight is allowed or not. */ protected boolean allowFlight; /** The server MOTD string. */ protected String motd; /** Maximum build height. */ protected int buildLimit; protected long lastSentPacketID; protected long lastSentPacketSize; protected long lastReceivedID; protected long lastReceivedSize; public final long[] sentPacketCountArray = new long[100]; public final long[] sentPacketSizeArray = new long[100]; public final long[] receivedPacketCountArray = new long[100]; public final long[] receivedPacketSizeArray = new long[100]; public final long[] tickTimeArray = new long[100]; /** Stats are [dimension][tick%100] system.nanoTime is stored. */ public long[][] timeOfLastDimensionTick; /** Username of the server owner (for integrated servers) */ protected String serverOwner; protected String folderName; /** * If true, there is no need to save chunks or stop the server, because that is * already being done. */ protected boolean worldIsBeingDeleted; protected String texturePack = ""; protected boolean serverIsRunning = false; /** * Set when warned for "Can't keep up", which triggers again after 15 seconds. */ protected long timeOfLastWarning; protected String userMessage; protected boolean field_104057_T = false; public MinecraftServer(String folder) { mcServer = this; this.folderName = folder; this.commandManager = new ServerCommandManager(); this.registerDispenseBehaviors(); } /** * Register all dispense behaviors. */ private void registerDispenseBehaviors() { DispenserBehaviors.registerDispenserBehaviours(); } /** * Initialises the server and starts it. */ protected abstract boolean startServer() throws IOException; /** * Typically "menu.convertingLevel", "menu.loadingLevel" or others. */ protected void setUserMessage(String par1Str) { this.logInfo(par1Str); this.userMessage = par1Str; } protected void setUserMessage(String par1Str, float prog) { this.logInfo(par1Str + ": " + (prog > 1.0f ? "" + (int)prog : "" + (int)(prog * 100.0f) + "%")); this.userMessage = par1Str; } public abstract boolean canStructuresSpawn(); public abstract EnumGameType getGameType(); /** * Defaults to "1" (Easy) for the dedicated server, defaults to "2" (Normal) on * the client. */ public abstract int getDifficulty(); /** * Defaults to false. */ public abstract boolean isHardcore(); /** * Used to display a percent remaining given text and the percentage. */ protected void outputPercentRemaining(String par1Str, int par2) { this.currentTask = par1Str; this.percentDone = par2; setUserMessage(par1Str, (par2 / 100.0f)); } /** * Set current task to null and set its percentage to 0. */ protected void clearCurrentTask() { this.currentTask = null; this.percentDone = 0; } /** * par1 indicates if a log message should be output. */ public void saveAllWorlds(boolean par1) { if (!this.worldIsBeingDeleted) { WorldServer[] var2 = this.worldServers; int var3 = var2.length; for (int var4 = 0; var4 < var3; ++var4) { WorldServer var5 = var2[var4]; if (var5 != null) { setUserMessage("Saving chunks for level \'" + var5.getWorldInfo().getWorldName() + "\'/" + var5.provider.getDimensionName()); try { var5.saveAllChunks(true, (IProgressUpdate) null); } catch (MinecraftException var7) { this.getLogAgent().logWarning(var7.getMessage()); } } } } } /** * Saves all necessary data as preparation for stopping the server. */ public void stopServer() { if (!this.worldIsBeingDeleted) { setUserMessage("Stopping server"); if (this.serverConfigManager != null) { this.getLogAgent().logInfo("Saving players"); this.serverConfigManager.saveAllPlayerData(); this.serverConfigManager.removeAllPlayers(); } setUserMessage("Saving worlds"); this.saveAllWorlds(false); for (int var1 = 0; var1 < this.worldServers.length; ++var1) { WorldServer var2 = this.worldServers[var1]; var2.flush(); } } } /** * "getHostname" is already taken, but both return the hostname. */ public String getServerHostname() { return "127.1.1.1"; } public void setHostname(String par1Str) { throw new IllegalArgumentException("variable removed"); } public boolean isServerRunning() { return this.serverRunning; } /** * Sets the serverRunning variable to false, in order to get the server to shut * down. */ public void initiateShutdown() { this.serverRunning = false; } public void run() { try { if (this.startServer()) { long var1 = EaglerAdapter.steadyTimeMillis(); for (long var50 = 0L; this.serverRunning; this.serverIsRunning = true) { long var5 = EaglerAdapter.steadyTimeMillis(); long var7 = var5 - var1; if (var7 > 2000L && var1 - this.timeOfLastWarning >= 15000L) { this.getLogAgent().logWarning( "Can\'t keep up! Did the system time change, or is the server overloaded?"); var7 = 2000L; this.timeOfLastWarning = var1; } if (var7 < 0L) { this.getLogAgent().logWarning("Time ran backwards! Did the system time change?"); var7 = 0L; } var50 += var7; var1 = var5; if (this.worldServers[0].areAllPlayersAsleep()) { this.tick(); var50 = 0L; } else { while (var50 > 50L) { var50 -= 50L; this.tick(); } } EaglerAdapter.sleep(1); } } else { throw new RuntimeException("Server did not init correctly"); } } catch (Throwable var48) { this.getLogAgent().logSevereException( "Encountered an unexpected exception " + var48.getClass().getSimpleName(), var48); var48.printStackTrace(); //IntegratedServer.throwExceptionToClient("Encountered an unexpected exception", var48); } finally { try { this.stopServer(); this.serverStopped = true; } catch (Throwable var46) { var46.printStackTrace(); } finally { this.systemExitNow(); } } } /** * Directly calls System.exit(0), instantly killing the program. */ protected void systemExitNow() { } /** * Main function called by run() every loop. */ protected void tick() { long var1 = System.nanoTime(); AxisAlignedBB.getAABBPool().cleanPool(); ++this.tickCounter; this.updateTimeLightAndEntities(); if (this.tickCounter % 900 == 0) { this.serverConfigManager.saveAllPlayerData(); this.saveAllWorlds(true); } this.tickTimeArray[this.tickCounter % 100] = System.nanoTime() - var1; this.sentPacketCountArray[this.tickCounter % 100] = Packet.sentID - this.lastSentPacketID; this.lastSentPacketID = Packet.sentID; this.sentPacketSizeArray[this.tickCounter % 100] = Packet.sentSize - this.lastSentPacketSize; this.lastSentPacketSize = Packet.sentSize; this.receivedPacketCountArray[this.tickCounter % 100] = Packet.receivedID - this.lastReceivedID; this.lastReceivedID = Packet.receivedID; this.receivedPacketSizeArray[this.tickCounter % 100] = Packet.receivedSize - this.lastReceivedSize; this.lastReceivedSize = Packet.receivedSize; } public List getTPSAndChunkBuffer(int tpsCounter) { ArrayList strs = new ArrayList(); strs.add("Ticks/Second: " + tpsCounter + "/20"); int c = 0; int oc = 0; int e = 0; int te = 0; int r = 0; int w = 0; int g = 0; int tu = 0; int lu = 0; for(int i = 0; i < worldServers.length; ++i) { c += worldServers[i].getChunkProvider().getLoadedChunkCount(); e += worldServers[i].loadedEntityList.size(); te += worldServers[i].loadedTileEntityList.size(); r += worldServers[i].getR(); w += worldServers[i].getW(); g += worldServers[i].getG(); lu += worldServers[i].getLU(); tu += worldServers[i].getTU(); } for(EntityPlayerMP p : (List)this.playersOnline) { oc += p.loadedChunks.size(); } strs.add("Chunks: " + c + "/" + (c + oc)); strs.add("Entities: " + e + "+" + te); strs.add("R: " + r + ", G: " + g + ", W: " + w); strs.add("TU: " + tu + " LU: " + lu); int pp = this.playersOnline.size(); if(pp > 1) { strs.add("Players: " + pp); } return strs; } public void updateTimeLightAndEntities() { int var1; for (var1 = 0; var1 < this.worldServers.length; ++var1) { long var2 = System.nanoTime(); if (var1 == 0 || this.getAllowNether()) { WorldServer var4 = this.worldServers[var1]; var4.getWorldVec3Pool().clear(); if (this.tickCounter % 20 == 0) { this.serverConfigManager.sendPacketToAllPlayersInDimension( new Packet4UpdateTime(var4.getTotalWorldTime(), var4.getWorldTime(), this.worldServers[var1].getGameRules().getGameRuleBooleanValue("doDaylightCycle")), var4.provider.dimensionId); } var4.tick(); var4.updateEntities(); var4.getEntityTracker().updateTrackedEntities(); } this.timeOfLastDimensionTick[var1][this.tickCounter % 100] = System.nanoTime() - var2; } this.serverConfigManager.sendPlayerInfoToAllPlayers(); for (var1 = 0; var1 < this.playersOnline.size(); ++var1) { ((IUpdatePlayerListBox) this.playersOnline.get(var1)).update(); } } public boolean getAllowNether() { return true; } public void func_82010_a(IUpdatePlayerListBox par1IUpdatePlayerListBox) { this.playersOnline.add(par1IUpdatePlayerListBox); } /** * Logs the message with a level of INFO. */ public void logInfo(String par1Str) { this.getLogAgent().logInfo(par1Str); } /** * Logs the message with a level of WARN. */ public void logWarning(String par1Str) { this.getLogAgent().logWarning(par1Str); } /** * Gets the worldServer by the given dimension. */ public WorldServer worldServerForDimension(int par1) { return par1 == -1 ? this.worldServers[1] : (par1 == 1 ? this.worldServers[2] : this.worldServers[0]); } /** * Returns the number of players currently on the server. */ public int getCurrentPlayerCount() { return this.serverConfigManager.getCurrentPlayerCount(); } /** * Returns the maximum number of players allowed on the server. */ public int getMaxPlayers() { return this.serverConfigManager.getMaxPlayers(); } /** * Returns an array of the usernames of all the connected players. */ public String[] getAllUsernames() { return this.serverConfigManager.getAllUsernames(); } /** * If par2Str begins with /, then it searches for commands, otherwise it returns * players. */ public List getPossibleCompletions(ICommandSender par1ICommandSender, String par2Str) { ArrayList var3 = new ArrayList(); if (par2Str.startsWith("/")) { par2Str = par2Str.substring(1); boolean var10 = !par2Str.contains(" "); List var11 = this.commandManager.getPossibleCommands(par1ICommandSender, par2Str); if (var11 != null) { Iterator var12 = var11.iterator(); while (var12.hasNext()) { String var13 = (String) var12.next(); if (var10) { var3.add("/" + var13); } else { var3.add(var13); } } } return var3; } else { String[] var4 = par2Str.split(" ", -1); String var5 = var4[var4.length - 1]; String[] var6 = this.serverConfigManager.getAllUsernames(); int var7 = var6.length; for (int var8 = 0; var8 < var7; ++var8) { String var9 = var6[var8]; if (CommandBase.doesStringStartWith(var5, var9)) { var3.add(var9); } } return var3; } } /** * Gets mcServer. */ public static MinecraftServer getServer() { return mcServer; } /** * Gets the name of this command sender (usually username, but possibly "Rcon") */ public String getCommandSenderName() { return "Host"; } public void sendChatToPlayer(String par1Str) { this.getLogAgent().logInfo(StringUtils.stripControlCodes(par1Str)); } /** * Returns true if the command sender is allowed to use the given command. */ public boolean canCommandSenderUseCommand(int par1, String par2Str) { return par2Str.equals(this.getServerOwner()); } public ICommandManager getCommandManager() { return this.commandManager; } /** * Gets serverPort. */ public int getServerPort() { return 1; } /** * Returns the username of the server owner (for integrated servers) */ public String getServerOwner() { return this.serverOwner; } public boolean isSinglePlayer() { return this.serverOwner != null; } public void setDifficultyForAllWorlds(int par1) { for (int var2 = 0; var2 < this.worldServers.length; ++var2) { WorldServer var3 = this.worldServers[var2]; if (var3 != null) { if (var3.getWorldInfo().isHardcoreModeEnabled()) { var3.difficultySetting = 3; var3.setAllowedSpawnTypes(true, true); } else if (this.isSinglePlayer()) { var3.difficultySetting = par1; var3.setAllowedSpawnTypes(var3.difficultySetting > 0, true); } else { var3.difficultySetting = par1; var3.setAllowedSpawnTypes(this.allowSpawnMonsters(), this.canSpawnAnimals); } } } } protected boolean allowSpawnMonsters() { return true; } /** * WARNING : directly calls * getActiveAnvilConverter().deleteWorldDirectory(theWorldServer[0].getSaveHandler().getWorldDirectoryName()); */ public void deleteWorldAndStopServer() { this.worldIsBeingDeleted = true; for (int var1 = 0; var1 < this.worldServers.length; ++var1) { WorldServer var2 = this.worldServers[var1]; if (var2 != null) { var2.flush(); } } String dir = this.worldServers[0].getSaveHandler().getWorldDirectoryName(); this.initiateShutdown(); } public String getTexturePack() { return null; } /** * This is checked to be 16 upon receiving the packet, otherwise the packet is * ignored. */ public int textureSize() { return 16; } public abstract boolean isDedicatedServer(); public boolean getCanSpawnAnimals() { return this.canSpawnAnimals; } public boolean getCanSpawnNPCs() { return this.canSpawnNPCs; } public boolean isPVPEnabled() { return this.pvpEnabled; } public boolean isFlightAllowed() { return this.allowFlight; } /** * Return whether command blocks are enabled. */ public abstract boolean isCommandBlockEnabled(); public int getBuildLimit() { return 256; } public ServerConfigurationManager getConfigurationManager() { return this.serverConfigManager; } public void setConfigurationManager(ServerConfigurationManager par1ServerConfigurationManager) { this.serverConfigManager = par1ServerConfigurationManager; } /** * Sets the game type for all worlds. */ public void setGameType(EnumGameType par1EnumGameType) { for (int var2 = 0; var2 < this.worldServers.length; ++var2) { getServer().worldServers[var2].getWorldInfo().setGameType(par1EnumGameType); } } public boolean getGuiEnabled() { return false; } /** * On dedicated does nothing. On integrated, sets commandsAllowedForAll, * gameType and allows external connections. */ public abstract String shareToLAN(EnumGameType var1, boolean var2); public int getTickCounter() { return this.tickCounter; } /** * Return the position for this command sender. */ public ChunkCoordinates getPlayerCoordinates() { return new ChunkCoordinates(0, 0, 0); } /** * Return the spawn protection area's size. */ public int getSpawnProtectionSize() { return 0; } public boolean isBlockProtected(World par1World, int par2, int par3, int par4, EntityPlayer par5EntityPlayer) { return false; } public abstract ILogAgent getLogAgent(); }