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