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