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