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