001 package net.minecraft.server; 002 003 import cpw.mods.fml.common.Side; 004 import cpw.mods.fml.common.asm.SideOnly; 005 import java.awt.GraphicsEnvironment; 006 import java.io.File; 007 import java.io.IOException; 008 import java.security.KeyPair; 009 import java.text.SimpleDateFormat; 010 import java.util.ArrayList; 011 import java.util.Date; 012 import java.util.Hashtable; 013 import java.util.Iterator; 014 import java.util.List; 015 import java.util.logging.Level; 016 import java.util.logging.Logger; 017 import cpw.mods.fml.common.FMLCommonHandler; 018 import cpw.mods.fml.relauncher.ArgsWrapper; 019 import cpw.mods.fml.relauncher.FMLRelauncher; 020 import net.minecraft.src.AnvilSaveConverter; 021 import net.minecraft.src.AxisAlignedBB; 022 import net.minecraft.src.CallableIsServerModded; 023 import net.minecraft.src.CallablePlayers; 024 import net.minecraft.src.CallableServerProfiler; 025 import net.minecraft.src.ChunkCoordinates; 026 import net.minecraft.src.CommandBase; 027 import net.minecraft.src.ConvertProgressUpdater; 028 import net.minecraft.src.CrashReport; 029 import net.minecraft.src.DedicatedServer; 030 import net.minecraft.src.DemoWorldServer; 031 import net.minecraft.src.EnumGameType; 032 import net.minecraft.src.ICommandManager; 033 import net.minecraft.src.ICommandSender; 034 import net.minecraft.src.IPlayerUsage; 035 import net.minecraft.src.IProgressUpdate; 036 import net.minecraft.src.ISaveFormat; 037 import net.minecraft.src.ISaveHandler; 038 import net.minecraft.src.IUpdatePlayerListBox; 039 import net.minecraft.src.MathHelper; 040 import net.minecraft.src.MinecraftException; 041 import net.minecraft.src.NetworkListenThread; 042 import net.minecraft.src.Packet; 043 import net.minecraft.src.Packet4UpdateTime; 044 import net.minecraft.src.PlayerUsageSnooper; 045 import net.minecraft.src.Profiler; 046 import net.minecraft.src.RConConsoleSource; 047 import net.minecraft.src.ReportedException; 048 import net.minecraft.src.ServerCommandManager; 049 import net.minecraft.src.ServerConfigurationManager; 050 import net.minecraft.src.StatList; 051 import net.minecraft.src.StringTranslate; 052 import net.minecraft.src.StringUtils; 053 import net.minecraft.src.ThreadDedicatedServer; 054 import net.minecraft.src.ThreadServerApplication; 055 import net.minecraft.src.Vec3; 056 import net.minecraft.src.WorldInfo; 057 import net.minecraft.src.WorldManager; 058 import net.minecraft.src.WorldServer; 059 import net.minecraft.src.WorldServerMulti; 060 import net.minecraft.src.WorldSettings; 061 import net.minecraft.src.WorldType; 062 import net.minecraftforge.common.DimensionManager; 063 import net.minecraftforge.common.MinecraftForge; 064 import net.minecraftforge.event.world.WorldEvent; 065 066 public abstract class MinecraftServer implements Runnable, IPlayerUsage, ICommandSender 067 { 068 /** The logging system. */ 069 public static Logger logger = Logger.getLogger("Minecraft"); 070 071 /** Instance of Minecraft Server. */ 072 private static MinecraftServer mcServer = null; 073 private final ISaveFormat anvilConverterForAnvilFile; 074 075 /** The PlayerUsageSnooper instance. */ 076 private final PlayerUsageSnooper usageSnooper = new PlayerUsageSnooper("server", this); 077 private final File anvilFile; 078 079 /** List of names of players who are online. */ 080 private final List playersOnline = new ArrayList(); 081 private final ICommandManager commandManager; 082 public final Profiler theProfiler = new Profiler(); 083 084 /** The server's hostname. */ 085 private String hostname; 086 087 /** The server's port. */ 088 private int serverPort = -1; 089 090 /** The server world instances. */ 091 public WorldServer[] worldServers; 092 093 /** The ServerConfigurationManager instance. */ 094 private ServerConfigurationManager serverConfigManager; 095 096 /** 097 * Indicates whether the server is running or not. Set to false to initiate a shutdown. 098 */ 099 private boolean serverRunning = true; 100 101 /** Indicates to other classes that the server is safely stopped. */ 102 private boolean serverStopped = false; 103 104 /** Incremented every tick. */ 105 private int tickCounter = 0; 106 107 /** 108 * The task the server is currently working on(and will output on outputPercentRemaining). 109 */ 110 public String currentTask; 111 112 /** The percentage of the current task finished so far. */ 113 public int percentDone; 114 115 /** True if the server is in online mode. */ 116 private boolean onlineMode; 117 118 /** True if the server has animals turned on. */ 119 private boolean canSpawnAnimals; 120 private boolean canSpawnNPCs; 121 122 /** Indicates whether PvP is active on the server or not. */ 123 private boolean pvpEnabled; 124 125 /** Determines if flight is allowed or not. */ 126 private boolean allowFlight; 127 128 /** The server MOTD string. */ 129 private String motd; 130 131 /** Maximum build height. */ 132 private int buildLimit; 133 private long lastSentPacketID; 134 private long lastSentPacketSize; 135 private long lastReceivedID; 136 private long lastReceivedSize; 137 public final long[] sentPacketCountArray = new long[100]; 138 public final long[] sentPacketSizeArray = new long[100]; 139 public final long[] receivedPacketCountArray = new long[100]; 140 public final long[] receivedPacketSizeArray = new long[100]; 141 public final long[] tickTimeArray = new long[100]; 142 143 /** Stats are [dimension][tick%100] system.nanoTime is stored. */ 144 //public long[][] timeOfLastDimensionTick; 145 public Hashtable<Integer, long[]> worldTickTimes = new Hashtable<Integer, long[]>(); 146 public int spawnProtectionSize = 16; 147 private KeyPair serverKeyPair; 148 149 /** Username of the server owner (for integrated servers) */ 150 private String serverOwner; 151 private String folderName; 152 @SideOnly(Side.CLIENT) 153 private String worldName; 154 private boolean isDemo; 155 private boolean enableBonusChest; 156 157 /** 158 * If true, there is no need to save chunks or stop the server, because that is already being done. 159 */ 160 private boolean worldIsBeingDeleted; 161 private String texturePack = ""; 162 private boolean serverIsRunning = false; 163 164 /** 165 * Set when warned for "Can't keep up", which triggers again after 15 seconds. 166 */ 167 private long timeOfLastWarning; 168 private String userMessage; 169 private boolean startProfiling; 170 171 public MinecraftServer(File par1File) 172 { 173 mcServer = this; 174 this.anvilFile = par1File; 175 this.commandManager = new ServerCommandManager(); 176 this.anvilConverterForAnvilFile = new AnvilSaveConverter(par1File); 177 } 178 179 /** 180 * Initialises the server and starts it. 181 */ 182 protected abstract boolean startServer() throws IOException; 183 184 protected void convertMapIfNeeded(String par1Str) 185 { 186 if (this.getActiveAnvilConverter().isOldMapFormat(par1Str)) 187 { 188 logger.info("Converting map!"); 189 this.setUserMessage("menu.convertingLevel"); 190 this.getActiveAnvilConverter().convertMapFormat(par1Str, new ConvertProgressUpdater(this)); 191 } 192 } 193 194 /** 195 * Typically "menu.convertingLevel", "menu.loadingLevel" or others. 196 */ 197 protected synchronized void setUserMessage(String par1Str) 198 { 199 this.userMessage = par1Str; 200 } 201 202 @SideOnly(Side.CLIENT) 203 204 public synchronized String getUserMessage() 205 { 206 return this.userMessage; 207 } 208 209 protected void loadAllWorlds(String par1Str, String par2Str, long par3, WorldType par5WorldType) 210 { 211 this.convertMapIfNeeded(par1Str); 212 this.setUserMessage("menu.loadingLevel"); 213 ISaveHandler var6 = this.anvilConverterForAnvilFile.getSaveLoader(par1Str, true); 214 WorldInfo var8 = var6.loadWorldInfo(); 215 WorldSettings var7; 216 217 if (var8 == null) 218 { 219 var7 = new WorldSettings(par3, this.getGameType(), this.canStructuresSpawn(), this.isHardcore(), par5WorldType); 220 } 221 else 222 { 223 var7 = new WorldSettings(var8); 224 } 225 226 if (this.enableBonusChest) 227 { 228 var7.enableBonusChest(); 229 } 230 231 WorldServer overWorld = (isDemo() ? new DemoWorldServer(this, var6, par2Str, 0, theProfiler) : new WorldServer(this, var6, par2Str, 0, var7, theProfiler)); 232 for (int dim : DimensionManager.getIDs()) 233 { 234 WorldServer world = (dim == 0 ? overWorld : new WorldServerMulti(this, var6, par2Str, dim, var7, overWorld, theProfiler)); 235 world.addWorldAccess(new WorldManager(this, world)); 236 237 if (!this.isSinglePlayer()) 238 { 239 world.getWorldInfo().setGameType(this.getGameType()); 240 } 241 242 this.serverConfigManager.setPlayerManager(this.worldServers); 243 MinecraftForge.EVENT_BUS.post(new WorldEvent.Load(world)); 244 } 245 246 this.serverConfigManager.setPlayerManager(new WorldServer[]{ overWorld }); 247 this.setDifficultyForAllWorlds(this.getDifficulty()); 248 this.initialWorldChunkLoad(); 249 } 250 251 protected void initialWorldChunkLoad() 252 { 253 short var1 = 196; 254 long var2 = System.currentTimeMillis(); 255 this.setUserMessage("menu.generatingTerrain"); 256 257 for (int var4 = 0; var4 < 1; ++var4) 258 { 259 logger.info("Preparing start region for level " + var4); 260 WorldServer var5 = this.worldServers[var4]; 261 ChunkCoordinates var6 = var5.getSpawnPoint(); 262 263 for (int var7 = -var1; var7 <= var1 && this.isServerRunning(); var7 += 16) 264 { 265 for (int var8 = -var1; var8 <= var1 && this.isServerRunning(); var8 += 16) 266 { 267 long var9 = System.currentTimeMillis(); 268 269 if (var9 < var2) 270 { 271 var2 = var9; 272 } 273 274 if (var9 > var2 + 1000L) 275 { 276 int var11 = (var1 * 2 + 1) * (var1 * 2 + 1); 277 int var12 = (var7 + var1) * (var1 * 2 + 1) + var8 + 1; 278 this.outputPercentRemaining("Preparing spawn area", var12 * 100 / var11); 279 var2 = var9; 280 } 281 282 var5.theChunkProviderServer.loadChunk(var6.posX + var7 >> 4, var6.posZ + var8 >> 4); 283 284 while (var5.updatingLighting() && this.isServerRunning()) 285 { 286 ; 287 } 288 } 289 } 290 } 291 292 this.clearCurrentTask(); 293 } 294 295 public abstract boolean canStructuresSpawn(); 296 297 public abstract EnumGameType getGameType(); 298 299 /** 300 * Defaults to "1" (Easy) for the dedicated server, defaults to "2" (Normal) on the client. 301 */ 302 public abstract int getDifficulty(); 303 304 /** 305 * Defaults to false. 306 */ 307 public abstract boolean isHardcore(); 308 309 /** 310 * Used to display a percent remaining given text and the percentage. 311 */ 312 protected void outputPercentRemaining(String par1Str, int par2) 313 { 314 this.currentTask = par1Str; 315 this.percentDone = par2; 316 logger.info(par1Str + ": " + par2 + "%"); 317 } 318 319 /** 320 * Set current task to null and set its percentage to 0. 321 */ 322 protected void clearCurrentTask() 323 { 324 this.currentTask = null; 325 this.percentDone = 0; 326 } 327 328 /** 329 * par1 indicates if a log message should be output. 330 */ 331 protected void saveAllWorlds(boolean par1) 332 { 333 if (!this.worldIsBeingDeleted) 334 { 335 WorldServer[] var2 = this.worldServers; 336 int var3 = var2.length; 337 338 for (int var4 = 0; var4 < var3; ++var4) 339 { 340 WorldServer var5 = var2[var4]; 341 342 if (var5 != null) 343 { 344 if (!par1) 345 { 346 logger.info("Saving chunks for level \'" + var5.getWorldInfo().getWorldName() + "\'/" + var5.provider.getDimensionName()); 347 } 348 349 try 350 { 351 var5.saveAllChunks(true, (IProgressUpdate)null); 352 } 353 catch (MinecraftException var7) 354 { 355 logger.warning(var7.getMessage()); 356 } 357 } 358 } 359 } 360 } 361 362 /** 363 * Saves all necessary data as preparation for stopping the server. 364 */ 365 public void stopServer() 366 { 367 if (!this.worldIsBeingDeleted) 368 { 369 logger.info("Stopping server"); 370 371 if (this.getNetworkThread() != null) 372 { 373 this.getNetworkThread().stopListening(); 374 } 375 376 if (this.serverConfigManager != null) 377 { 378 logger.info("Saving players"); 379 this.serverConfigManager.saveAllPlayerData(); 380 this.serverConfigManager.removeAllPlayers(); 381 } 382 383 logger.info("Saving worlds"); 384 this.saveAllWorlds(false); 385 WorldServer[] var1 = this.worldServers; 386 int var2 = var1.length; 387 388 for (int var3 = 0; var3 < var2; ++var3) 389 { 390 WorldServer var4 = var1[var3]; 391 var4.flush(); 392 } 393 394 if (this.usageSnooper != null && this.usageSnooper.isSnooperRunning()) 395 { 396 this.usageSnooper.stopSnooper(); 397 } 398 } 399 } 400 401 /** 402 * "getHostname" is already taken, but both return the hostname. 403 */ 404 public String getServerHostname() 405 { 406 return this.hostname; 407 } 408 409 public void setHostname(String par1Str) 410 { 411 this.hostname = par1Str; 412 } 413 414 public boolean isServerRunning() 415 { 416 return this.serverRunning; 417 } 418 419 /** 420 * Sets the serverRunning variable to false, in order to get the server to shut down. 421 */ 422 public void initiateShutdown() 423 { 424 this.serverRunning = false; 425 } 426 427 public void run() 428 { 429 try 430 { 431 if (this.startServer()) 432 { 433 FMLCommonHandler.instance().handleServerStarted(); 434 long var1 = System.currentTimeMillis(); 435 436 FMLCommonHandler.instance().onWorldLoadTick(worldServers); 437 438 for (long var50 = 0L; this.serverRunning; this.serverIsRunning = true) 439 { 440 long var5 = System.currentTimeMillis(); 441 long var7 = var5 - var1; 442 443 if (var7 > 2000L && var1 - this.timeOfLastWarning >= 15000L) 444 { 445 logger.warning("Can\'t keep up! Did the system time change, or is the server overloaded?"); 446 var7 = 2000L; 447 this.timeOfLastWarning = var1; 448 } 449 450 if (var7 < 0L) 451 { 452 logger.warning("Time ran backwards! Did the system time change?"); 453 var7 = 0L; 454 } 455 456 var50 += var7; 457 var1 = var5; 458 459 if (this.worldServers[0].areAllPlayersAsleep()) 460 { 461 this.tick(); 462 var50 = 0L; 463 } 464 else 465 { 466 while (var50 > 50L) 467 { 468 var50 -= 50L; 469 this.tick(); 470 } 471 } 472 473 Thread.sleep(1L); 474 } 475 FMLCommonHandler.instance().handleServerStopping(); 476 } 477 else 478 { 479 this.finalTick((CrashReport)null); 480 } 481 } 482 catch (Throwable var48) 483 { 484 var48.printStackTrace(); 485 logger.log(Level.SEVERE, "Encountered an unexpected exception " + var48.getClass().getSimpleName(), var48); 486 CrashReport var2 = null; 487 488 if (var48 instanceof ReportedException) 489 { 490 var2 = this.addServerInfoToCrashReport(((ReportedException)var48).getTheReportedExceptionCrashReport()); 491 } 492 else 493 { 494 var2 = this.addServerInfoToCrashReport(new CrashReport("Exception in server tick loop", var48)); 495 } 496 497 File var3 = new File(new File(this.getDataDirectory(), "crash-reports"), "crash-" + (new SimpleDateFormat("yyyy-MM-dd_HH.mm.ss")).format(new Date()) + "-server.txt"); 498 499 if (var2.saveToFile(var3)) 500 { 501 logger.severe("This crash report has been saved to: " + var3.getAbsolutePath()); 502 } 503 else 504 { 505 logger.severe("We were unable to save this crash report to disk."); 506 } 507 508 this.finalTick(var2); 509 } 510 finally 511 { 512 try 513 { 514 this.stopServer(); 515 this.serverStopped = true; 516 } 517 catch (Throwable var46) 518 { 519 var46.printStackTrace(); 520 } 521 finally 522 { 523 this.systemExitNow(); 524 } 525 } 526 } 527 528 protected File getDataDirectory() 529 { 530 return new File("."); 531 } 532 533 /** 534 * Called on exit from the main run() loop. 535 */ 536 protected void finalTick(CrashReport par1CrashReport) {} 537 538 /** 539 * Directly calls System.exit(0), instantly killing the program. 540 */ 541 protected void systemExitNow() {} 542 543 /** 544 * Main function called by run() every loop. 545 */ 546 public void tick() 547 { 548 FMLCommonHandler.instance().rescheduleTicks(Side.SERVER); 549 long var1 = System.nanoTime(); 550 AxisAlignedBB.getAABBPool().cleanPool(); 551 Vec3.getVec3Pool().clear(); 552 FMLCommonHandler.instance().onPreServerTick(); 553 ++this.tickCounter; 554 555 if (this.startProfiling) 556 { 557 this.startProfiling = false; 558 this.theProfiler.profilingEnabled = true; 559 this.theProfiler.clearProfiling(); 560 } 561 562 this.theProfiler.startSection("root"); 563 this.updateTimeLightAndEntities(); 564 565 if (this.tickCounter % 900 == 0) 566 { 567 this.theProfiler.startSection("save"); 568 this.serverConfigManager.saveAllPlayerData(); 569 this.saveAllWorlds(true); 570 this.theProfiler.endSection(); 571 } 572 573 this.theProfiler.startSection("tallying"); 574 this.tickTimeArray[this.tickCounter % 100] = System.nanoTime() - var1; 575 this.sentPacketCountArray[this.tickCounter % 100] = Packet.sentID - this.lastSentPacketID; 576 this.lastSentPacketID = Packet.sentID; 577 this.sentPacketSizeArray[this.tickCounter % 100] = Packet.sentSize - this.lastSentPacketSize; 578 this.lastSentPacketSize = Packet.sentSize; 579 this.receivedPacketCountArray[this.tickCounter % 100] = Packet.receivedID - this.lastReceivedID; 580 this.lastReceivedID = Packet.receivedID; 581 this.receivedPacketSizeArray[this.tickCounter % 100] = Packet.receivedSize - this.lastReceivedSize; 582 this.lastReceivedSize = Packet.receivedSize; 583 this.theProfiler.endSection(); 584 this.theProfiler.startSection("snooper"); 585 586 if (!this.usageSnooper.isSnooperRunning() && this.tickCounter > 100) 587 { 588 this.usageSnooper.startSnooper(); 589 } 590 591 if (this.tickCounter % 6000 == 0) 592 { 593 this.usageSnooper.addMemoryStatsToSnooper(); 594 } 595 596 this.theProfiler.endSection(); 597 this.theProfiler.endSection(); 598 FMLCommonHandler.instance().onPostServerTick(); 599 } 600 601 public void updateTimeLightAndEntities() 602 { 603 this.theProfiler.startSection("levels"); 604 605 for (Integer id : DimensionManager.getIDs()) 606 { 607 long var2 = System.nanoTime(); 608 609 if (id == 0 || this.getAllowNether()) 610 { 611 WorldServer var4 = DimensionManager.getWorld(id); 612 this.theProfiler.startSection(var4.getWorldInfo().getWorldName()); 613 614 if (this.tickCounter % 20 == 0) 615 { 616 this.theProfiler.startSection("timeSync"); 617 this.serverConfigManager.sendPacketToAllPlayersInDimension(new Packet4UpdateTime(var4.getWorldTime()), var4.provider.dimensionId); 618 this.theProfiler.endSection(); 619 } 620 621 this.theProfiler.startSection("tick"); 622 FMLCommonHandler.instance().onPreWorldTick(var4); 623 var4.tick(); 624 FMLCommonHandler.instance().onPostWorldTick(var4); 625 this.theProfiler.endStartSection("lights"); 626 627 while (true) 628 { 629 if (!var4.updatingLighting()) 630 { 631 this.theProfiler.endSection(); 632 var4.updateEntities(); 633 this.theProfiler.startSection("tracker"); 634 var4.getEntityTracker().updateTrackedEntities(); 635 this.theProfiler.endSection(); 636 this.theProfiler.endSection(); 637 break; 638 } 639 } 640 } 641 642 worldTickTimes.get(id)[this.tickCounter % 100] = System.nanoTime() - var2; 643 } 644 645 this.theProfiler.endStartSection("connection"); 646 this.getNetworkThread().networkTick(); 647 this.theProfiler.endStartSection("players"); 648 this.serverConfigManager.sendPlayerInfoToAllPlayers(); 649 this.theProfiler.endStartSection("tickables"); 650 Iterator var5 = this.playersOnline.iterator(); 651 652 while (var5.hasNext()) 653 { 654 IUpdatePlayerListBox var6 = (IUpdatePlayerListBox)var5.next(); 655 var6.update(); 656 } 657 658 this.theProfiler.endSection(); 659 } 660 661 public boolean getAllowNether() 662 { 663 return true; 664 } 665 666 public void startServerThread() 667 { 668 (new ThreadServerApplication(this, "Server thread")).start(); 669 } 670 671 /** 672 * Returns a File object from the specified string. 673 */ 674 public File getFile(String par1Str) 675 { 676 return new File(this.getDataDirectory(), par1Str); 677 } 678 679 /** 680 * Logs the message with a level of INFO. 681 */ 682 public void logInfo(String par1Str) 683 { 684 logger.info(par1Str); 685 } 686 687 /** 688 * Logs the message with a level of WARN. 689 */ 690 public void logWarning(String par1Str) 691 { 692 logger.warning(par1Str); 693 } 694 695 /** 696 * Gets the worldServer by the given dimension. 697 */ 698 public WorldServer worldServerForDimension(int par1) 699 { 700 WorldServer ret = DimensionManager.getWorld(par1); 701 return (ret != null ? ret : DimensionManager.getWorld(0)); 702 } 703 704 @SideOnly(Side.SERVER) 705 706 /** 707 * Adds a player's name to the list of online players. 708 */ 709 public void addToOnlinePlayerList(IUpdatePlayerListBox par1IUpdatePlayerListBox) 710 { 711 this.playersOnline.add(par1IUpdatePlayerListBox); 712 } 713 714 /** 715 * Returns the server's hostname. 716 */ 717 public String getHostname() 718 { 719 return this.hostname; 720 } 721 722 /** 723 * Never used, but "getServerPort" is already taken. 724 */ 725 public int getPort() 726 { 727 return this.serverPort; 728 } 729 730 /** 731 * minecraftServer.getMOTD is used in 2 places instead (it is a non-virtual function which returns the same thing) 732 */ 733 public String getServerMOTD() 734 { 735 return this.motd; 736 } 737 738 /** 739 * Returns the server's Minecraft version as string. 740 */ 741 public String getMinecraftVersion() 742 { 743 return "1.3.2"; 744 } 745 746 /** 747 * Returns the number of players currently on the server. 748 */ 749 public int getCurrentPlayerCount() 750 { 751 return this.serverConfigManager.getCurrentPlayerCount(); 752 } 753 754 /** 755 * Returns the maximum number of players allowed on the server. 756 */ 757 public int getMaxPlayers() 758 { 759 return this.serverConfigManager.getMaxPlayers(); 760 } 761 762 /** 763 * Returns an array of the usernames of all the connected players. 764 */ 765 public String[] getAllUsernames() 766 { 767 return this.serverConfigManager.getAllUsernames(); 768 } 769 770 /** 771 * Used by RCon's Query in the form of "MajorServerMod 1.2.3: MyPlugin 1.3; AnotherPlugin 2.1; AndSoForth 1.0". 772 */ 773 public String getPlugins() 774 { 775 return ""; 776 } 777 778 public String executeCommand(String par1Str) 779 { 780 RConConsoleSource.consoleBuffer.resetLog(); 781 this.commandManager.executeCommand(RConConsoleSource.consoleBuffer, par1Str); 782 return RConConsoleSource.consoleBuffer.getChatBuffer(); 783 } 784 785 /** 786 * Returns true if debugging is enabled, false otherwise. 787 */ 788 public boolean isDebuggingEnabled() 789 { 790 return false; 791 } 792 793 /** 794 * Logs the error message with a level of SEVERE. 795 */ 796 public void logSevere(String par1Str) 797 { 798 logger.log(Level.SEVERE, par1Str); 799 } 800 801 /** 802 * If isDebuggingEnabled(), logs the message with a level of INFO. 803 */ 804 public void logDebug(String par1Str) 805 { 806 if (this.isDebuggingEnabled()) 807 { 808 logger.log(Level.INFO, par1Str); 809 } 810 } 811 812 public String getServerModName() 813 { 814 return "forge,fml"; 815 } 816 817 /** 818 * Adds the server info, including from theWorldServer, to the crash report. 819 */ 820 public CrashReport addServerInfoToCrashReport(CrashReport par1CrashReport) 821 { 822 par1CrashReport.addCrashSectionCallable("Is Modded", new CallableIsServerModded(this)); 823 par1CrashReport.addCrashSectionCallable("Profiler Position", new CallableServerProfiler(this)); 824 825 if (this.serverConfigManager != null) 826 { 827 par1CrashReport.addCrashSectionCallable("Player Count", new CallablePlayers(this)); 828 } 829 830 if (this.worldServers != null) 831 { 832 WorldServer[] var2 = this.worldServers; 833 int var3 = var2.length; 834 835 for (int var4 = 0; var4 < var3; ++var4) 836 { 837 WorldServer var5 = var2[var4]; 838 839 if (var5 != null) 840 { 841 var5.addWorldInfoToCrashReport(par1CrashReport); 842 } 843 } 844 } 845 846 return par1CrashReport; 847 } 848 849 /** 850 * If par2Str begins with /, then it searches for commands, otherwise it returns players. 851 */ 852 public List getPossibleCompletions(ICommandSender par1ICommandSender, String par2Str) 853 { 854 ArrayList var3 = new ArrayList(); 855 856 if (par2Str.startsWith("/")) 857 { 858 par2Str = par2Str.substring(1); 859 boolean var10 = !par2Str.contains(" "); 860 List var11 = this.commandManager.getPossibleCommands(par1ICommandSender, par2Str); 861 862 if (var11 != null) 863 { 864 Iterator var12 = var11.iterator(); 865 866 while (var12.hasNext()) 867 { 868 String var13 = (String)var12.next(); 869 870 if (var10) 871 { 872 var3.add("/" + var13); 873 } 874 else 875 { 876 var3.add(var13); 877 } 878 } 879 } 880 881 return var3; 882 } 883 else 884 { 885 String[] var4 = par2Str.split(" ", -1); 886 String var5 = var4[var4.length - 1]; 887 String[] var6 = this.serverConfigManager.getAllUsernames(); 888 int var7 = var6.length; 889 890 for (int var8 = 0; var8 < var7; ++var8) 891 { 892 String var9 = var6[var8]; 893 894 if (CommandBase.doesStringStartWith(var5, var9)) 895 { 896 var3.add(var9); 897 } 898 } 899 900 return var3; 901 } 902 } 903 904 /** 905 * Gets mcServer. 906 */ 907 public static MinecraftServer getServer() 908 { 909 return mcServer; 910 } 911 912 /** 913 * Gets the name of this command sender (usually username, but possibly "Rcon") 914 */ 915 public String getCommandSenderName() 916 { 917 return "Server"; 918 } 919 920 public void sendChatToPlayer(String par1Str) 921 { 922 logger.info(StringUtils.stripControlCodes(par1Str)); 923 } 924 925 /** 926 * Returns true if the command sender is allowed to use the given command. 927 */ 928 public boolean canCommandSenderUseCommand(String par1Str) 929 { 930 return true; 931 } 932 933 /** 934 * Translates and formats the given string key with the given arguments. 935 */ 936 public String translateString(String par1Str, Object ... par2ArrayOfObj) 937 { 938 return StringTranslate.getInstance().translateKeyFormat(par1Str, par2ArrayOfObj); 939 } 940 941 public ICommandManager getCommandManager() 942 { 943 return this.commandManager; 944 } 945 946 /** 947 * Gets KeyPair instanced in MinecraftServer. 948 */ 949 public KeyPair getKeyPair() 950 { 951 return this.serverKeyPair; 952 } 953 954 /** 955 * Gets serverPort. 956 */ 957 public int getServerPort() 958 { 959 return this.serverPort; 960 } 961 962 public void setServerPort(int par1) 963 { 964 this.serverPort = par1; 965 } 966 967 /** 968 * Returns the username of the server owner (for integrated servers) 969 */ 970 public String getServerOwner() 971 { 972 return this.serverOwner; 973 } 974 975 /** 976 * Sets the username of the owner of this server (in the case of an integrated server) 977 */ 978 public void setServerOwner(String par1Str) 979 { 980 this.serverOwner = par1Str; 981 } 982 983 public boolean isSinglePlayer() 984 { 985 return this.serverOwner != null; 986 } 987 988 public String getFolderName() 989 { 990 return this.folderName; 991 } 992 993 public void setFolderName(String par1Str) 994 { 995 this.folderName = par1Str; 996 } 997 998 @SideOnly(Side.CLIENT) 999 public void setWorldName(String par1Str) 1000 { 1001 this.worldName = par1Str; 1002 } 1003 1004 @SideOnly(Side.CLIENT) 1005 public String getWorldName() 1006 { 1007 return this.worldName; 1008 } 1009 1010 public void setKeyPair(KeyPair par1KeyPair) 1011 { 1012 this.serverKeyPair = par1KeyPair; 1013 } 1014 1015 public void setDifficultyForAllWorlds(int par1) 1016 { 1017 for (int var2 = 0; var2 < this.worldServers.length; ++var2) 1018 { 1019 WorldServer var3 = this.worldServers[var2]; 1020 1021 if (var3 != null) 1022 { 1023 if (var3.getWorldInfo().isHardcoreModeEnabled()) 1024 { 1025 var3.difficultySetting = 3; 1026 var3.setAllowedSpawnTypes(true, true); 1027 } 1028 else if (this.isSinglePlayer()) 1029 { 1030 var3.difficultySetting = par1; 1031 var3.setAllowedSpawnTypes(var3.difficultySetting > 0, true); 1032 } 1033 else 1034 { 1035 var3.difficultySetting = par1; 1036 var3.setAllowedSpawnTypes(this.allowSpawnMonsters(), this.canSpawnAnimals); 1037 } 1038 } 1039 } 1040 } 1041 1042 protected boolean allowSpawnMonsters() 1043 { 1044 return true; 1045 } 1046 1047 /** 1048 * Gets whether this is a demo or not. 1049 */ 1050 public boolean isDemo() 1051 { 1052 return this.isDemo; 1053 } 1054 1055 /** 1056 * Sets whether this is a demo or not. 1057 */ 1058 public void setDemo(boolean par1) 1059 { 1060 this.isDemo = par1; 1061 } 1062 1063 public void canCreateBonusChest(boolean par1) 1064 { 1065 this.enableBonusChest = par1; 1066 } 1067 1068 public ISaveFormat getActiveAnvilConverter() 1069 { 1070 return this.anvilConverterForAnvilFile; 1071 } 1072 1073 /** 1074 * WARNING : directly calls 1075 * getActiveAnvilConverter().deleteWorldDirectory(theWorldServer[0].getSaveHandler().getSaveDirectoryName()); 1076 */ 1077 public void deleteWorldAndStopServer() 1078 { 1079 this.worldIsBeingDeleted = true; 1080 this.getActiveAnvilConverter().flushCache(); 1081 1082 for (int var1 = 0; var1 < this.worldServers.length; ++var1) 1083 { 1084 WorldServer var2 = this.worldServers[var1]; 1085 1086 if (var2 != null) 1087 { 1088 var2.flush(); 1089 } 1090 } 1091 1092 this.getActiveAnvilConverter().deleteWorldDirectory(this.worldServers[0].getSaveHandler().getSaveDirectoryName()); 1093 this.initiateShutdown(); 1094 } 1095 1096 public String getTexturePack() 1097 { 1098 return this.texturePack; 1099 } 1100 1101 public void setTexturePack(String par1Str) 1102 { 1103 this.texturePack = par1Str; 1104 } 1105 1106 public void addServerStatsToSnooper(PlayerUsageSnooper par1PlayerUsageSnooper) 1107 { 1108 par1PlayerUsageSnooper.addData("whitelist_enabled", Boolean.valueOf(false)); 1109 par1PlayerUsageSnooper.addData("whitelist_count", Integer.valueOf(0)); 1110 par1PlayerUsageSnooper.addData("players_current", Integer.valueOf(this.getCurrentPlayerCount())); 1111 par1PlayerUsageSnooper.addData("players_max", Integer.valueOf(this.getMaxPlayers())); 1112 par1PlayerUsageSnooper.addData("players_seen", Integer.valueOf(this.serverConfigManager.getAvailablePlayerDat().length)); 1113 par1PlayerUsageSnooper.addData("uses_auth", Boolean.valueOf(this.onlineMode)); 1114 par1PlayerUsageSnooper.addData("gui_state", this.getGuiEnabled() ? "enabled" : "disabled"); 1115 par1PlayerUsageSnooper.addData("avg_tick_ms", Integer.valueOf((int)(MathHelper.average(this.tickTimeArray) * 1.0E-6D))); 1116 par1PlayerUsageSnooper.addData("avg_sent_packet_count", Integer.valueOf((int)MathHelper.average(this.sentPacketCountArray))); 1117 par1PlayerUsageSnooper.addData("avg_sent_packet_size", Integer.valueOf((int)MathHelper.average(this.sentPacketSizeArray))); 1118 par1PlayerUsageSnooper.addData("avg_rec_packet_count", Integer.valueOf((int)MathHelper.average(this.receivedPacketCountArray))); 1119 par1PlayerUsageSnooper.addData("avg_rec_packet_size", Integer.valueOf((int)MathHelper.average(this.receivedPacketSizeArray))); 1120 int var2 = 0; 1121 1122 for (int var3 = 0; var3 < this.worldServers.length; ++var3) 1123 { 1124 if (this.worldServers[var3] != null) 1125 { 1126 WorldServer var4 = this.worldServers[var3]; 1127 WorldInfo var5 = var4.getWorldInfo(); 1128 par1PlayerUsageSnooper.addData("world[" + var2 + "][dimension]", Integer.valueOf(var4.provider.dimensionId)); 1129 par1PlayerUsageSnooper.addData("world[" + var2 + "][mode]", var5.getGameType()); 1130 par1PlayerUsageSnooper.addData("world[" + var2 + "][difficulty]", Integer.valueOf(var4.difficultySetting)); 1131 par1PlayerUsageSnooper.addData("world[" + var2 + "][hardcore]", Boolean.valueOf(var5.isHardcoreModeEnabled())); 1132 par1PlayerUsageSnooper.addData("world[" + var2 + "][generator_name]", var5.getTerrainType().getWorldTypeName()); 1133 par1PlayerUsageSnooper.addData("world[" + var2 + "][generator_version]", Integer.valueOf(var5.getTerrainType().getGeneratorVersion())); 1134 par1PlayerUsageSnooper.addData("world[" + var2 + "][height]", Integer.valueOf(this.buildLimit)); 1135 par1PlayerUsageSnooper.addData("world[" + var2 + "][chunks_loaded]", Integer.valueOf(var4.getChunkProvider().getLoadedChunkCount())); 1136 ++var2; 1137 } 1138 } 1139 1140 par1PlayerUsageSnooper.addData("worlds", Integer.valueOf(var2)); 1141 } 1142 1143 public void addServerTypeToSnooper(PlayerUsageSnooper par1PlayerUsageSnooper) 1144 { 1145 par1PlayerUsageSnooper.addData("singleplayer", Boolean.valueOf(this.isSinglePlayer())); 1146 par1PlayerUsageSnooper.addData("server_brand", this.getServerModName()); 1147 par1PlayerUsageSnooper.addData("gui_supported", GraphicsEnvironment.isHeadless() ? "headless" : "supported"); 1148 par1PlayerUsageSnooper.addData("dedicated", Boolean.valueOf(this.isDedicatedServer())); 1149 } 1150 1151 /** 1152 * Returns whether snooping is enabled or not. 1153 */ 1154 public boolean isSnooperEnabled() 1155 { 1156 return true; 1157 } 1158 1159 /** 1160 * This is checked to be 16 upon receiving the packet, otherwise the packet is ignored. 1161 */ 1162 public int textureSize() 1163 { 1164 return 16; 1165 } 1166 1167 public abstract boolean isDedicatedServer(); 1168 1169 public boolean isServerInOnlineMode() 1170 { 1171 return this.onlineMode; 1172 } 1173 1174 public void setOnlineMode(boolean par1) 1175 { 1176 this.onlineMode = par1; 1177 } 1178 1179 public boolean getCanSpawnAnimals() 1180 { 1181 return this.canSpawnAnimals; 1182 } 1183 1184 public void setCanSpawnAnimals(boolean par1) 1185 { 1186 this.canSpawnAnimals = par1; 1187 } 1188 1189 public boolean getCanSpawnNPCs() 1190 { 1191 return this.canSpawnNPCs; 1192 } 1193 1194 public void setCanSpawnNPCs(boolean par1) 1195 { 1196 this.canSpawnNPCs = par1; 1197 } 1198 1199 public boolean isPVPEnabled() 1200 { 1201 return this.pvpEnabled; 1202 } 1203 1204 public void setAllowPvp(boolean par1) 1205 { 1206 this.pvpEnabled = par1; 1207 } 1208 1209 public boolean isFlightAllowed() 1210 { 1211 return this.allowFlight; 1212 } 1213 1214 public void setAllowFlight(boolean par1) 1215 { 1216 this.allowFlight = par1; 1217 } 1218 1219 public String getMOTD() 1220 { 1221 return this.motd; 1222 } 1223 1224 public void setMOTD(String par1Str) 1225 { 1226 this.motd = par1Str; 1227 } 1228 1229 public int getBuildLimit() 1230 { 1231 return this.buildLimit; 1232 } 1233 1234 public void setBuildLimit(int par1) 1235 { 1236 this.buildLimit = par1; 1237 } 1238 1239 public boolean isServerStopped() 1240 { 1241 return this.serverStopped; 1242 } 1243 1244 public ServerConfigurationManager getConfigurationManager() 1245 { 1246 return this.serverConfigManager; 1247 } 1248 1249 public void setConfigurationManager(ServerConfigurationManager par1ServerConfigurationManager) 1250 { 1251 this.serverConfigManager = par1ServerConfigurationManager; 1252 } 1253 1254 /** 1255 * Sets the game type for all worlds. 1256 */ 1257 public void setGameType(EnumGameType par1EnumGameType) 1258 { 1259 for (int var2 = 0; var2 < this.worldServers.length; ++var2) 1260 { 1261 getServer().worldServers[var2].getWorldInfo().setGameType(par1EnumGameType); 1262 } 1263 } 1264 1265 public abstract NetworkListenThread getNetworkThread(); 1266 1267 @SideOnly(Side.CLIENT) 1268 public boolean serverIsInRunLoop() 1269 { 1270 return this.serverIsRunning; 1271 } 1272 1273 public boolean getGuiEnabled() 1274 { 1275 return false; 1276 } 1277 1278 /** 1279 * On dedicated does nothing. On integrated, sets commandsAllowedForAll, gameType and allows external connections. 1280 */ 1281 public abstract String shareToLAN(EnumGameType var1, boolean var2); 1282 1283 public int getTickCounter() 1284 { 1285 return this.tickCounter; 1286 } 1287 1288 public void enableProfiling() 1289 { 1290 this.startProfiling = true; 1291 } 1292 1293 @SideOnly(Side.CLIENT) 1294 public PlayerUsageSnooper getPlayerUsageSnooper() 1295 { 1296 return this.usageSnooper; 1297 } 1298 1299 /** 1300 * Gets the current player count, maximum player count, and player entity list. 1301 */ 1302 public static ServerConfigurationManager getServerConfigurationManager(MinecraftServer par0MinecraftServer) 1303 { 1304 return par0MinecraftServer.serverConfigManager; 1305 } 1306 1307 @SideOnly(Side.SERVER) 1308 public static void main(String[] par0ArrayOfStr) 1309 { 1310 FMLRelauncher.handleServerRelaunch(new ArgsWrapper(par0ArrayOfStr)); 1311 } 1312 @SideOnly(Side.SERVER) 1313 public static void fmlReentry(ArgsWrapper wrap) 1314 { 1315 String[] par0ArrayOfStr = wrap.args; 1316 StatList.func_75919_a(); 1317 1318 try 1319 { 1320 boolean var1 = !GraphicsEnvironment.isHeadless(); 1321 String var2 = null; 1322 String var3 = "."; 1323 String var4 = null; 1324 boolean var5 = false; 1325 boolean var6 = false; 1326 int var7 = -1; 1327 1328 for (int var8 = 0; var8 < par0ArrayOfStr.length; ++var8) 1329 { 1330 String var9 = par0ArrayOfStr[var8]; 1331 String var10 = var8 == par0ArrayOfStr.length - 1 ? null : par0ArrayOfStr[var8 + 1]; 1332 boolean var11 = false; 1333 1334 if (!var9.equals("nogui") && !var9.equals("--nogui")) 1335 { 1336 if (var9.equals("--port") && var10 != null) 1337 { 1338 var11 = true; 1339 1340 try 1341 { 1342 var7 = Integer.parseInt(var10); 1343 } 1344 catch (NumberFormatException var13) 1345 { 1346 ; 1347 } 1348 } 1349 else if (var9.equals("--singleplayer") && var10 != null) 1350 { 1351 var11 = true; 1352 var2 = var10; 1353 } 1354 else if (var9.equals("--universe") && var10 != null) 1355 { 1356 var11 = true; 1357 var3 = var10; 1358 } 1359 else if (var9.equals("--world") && var10 != null) 1360 { 1361 var11 = true; 1362 var4 = var10; 1363 } 1364 else if (var9.equals("--demo")) 1365 { 1366 var5 = true; 1367 } 1368 else if (var9.equals("--bonusChest")) 1369 { 1370 var6 = true; 1371 } 1372 } 1373 else 1374 { 1375 var1 = false; 1376 } 1377 1378 if (var11) 1379 { 1380 ++var8; 1381 } 1382 } 1383 1384 DedicatedServer var15 = new DedicatedServer(new File(var3)); 1385 1386 if (var2 != null) 1387 { 1388 var15.setServerOwner(var2); 1389 } 1390 1391 if (var4 != null) 1392 { 1393 var15.setFolderName(var4); 1394 } 1395 1396 if (var7 >= 0) 1397 { 1398 var15.setServerPort(var7); 1399 } 1400 1401 if (var5) 1402 { 1403 var15.setDemo(true); 1404 } 1405 1406 if (var6) 1407 { 1408 var15.canCreateBonusChest(true); 1409 } 1410 1411 if (var1) 1412 { 1413 var15.func_79001_aj(); 1414 } 1415 1416 var15.startServerThread(); 1417 Runtime.getRuntime().addShutdownHook(new ThreadDedicatedServer(var15)); 1418 } 1419 catch (Exception var14) 1420 { 1421 logger.log(Level.SEVERE, "Failed to start the minecraft server", var14); 1422 } 1423 } 1424 }