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 this.systemExitNow(); 560 } 561 } 562 } 563 564 protected File getDataDirectory() 565 { 566 return new File("."); 567 } 568 569 /** 570 * Called on exit from the main run() loop. 571 */ 572 protected void finalTick(CrashReport par1CrashReport) {} 573 574 /** 575 * Directly calls System.exit(0), instantly killing the program. 576 */ 577 protected void systemExitNow() {} 578 579 /** 580 * Main function called by run() every loop. 581 */ 582 public void tick() 583 { 584 FMLCommonHandler.instance().rescheduleTicks(Side.SERVER); 585 long var1 = System.nanoTime(); 586 AxisAlignedBB.getAABBPool().cleanPool(); 587 FMLCommonHandler.instance().onPreServerTick(); 588 ++this.tickCounter; 589 590 if (this.startProfiling) 591 { 592 this.startProfiling = false; 593 this.theProfiler.profilingEnabled = true; 594 this.theProfiler.clearProfiling(); 595 } 596 597 this.theProfiler.startSection("root"); 598 this.updateTimeLightAndEntities(); 599 600 if (this.tickCounter % 900 == 0) 601 { 602 this.theProfiler.startSection("save"); 603 this.serverConfigManager.saveAllPlayerData(); 604 this.saveAllWorlds(true); 605 this.theProfiler.endSection(); 606 } 607 608 this.theProfiler.startSection("tallying"); 609 this.tickTimeArray[this.tickCounter % 100] = System.nanoTime() - var1; 610 this.sentPacketCountArray[this.tickCounter % 100] = Packet.sentID - this.lastSentPacketID; 611 this.lastSentPacketID = Packet.sentID; 612 this.sentPacketSizeArray[this.tickCounter % 100] = Packet.sentSize - this.lastSentPacketSize; 613 this.lastSentPacketSize = Packet.sentSize; 614 this.receivedPacketCountArray[this.tickCounter % 100] = Packet.receivedID - this.lastReceivedID; 615 this.lastReceivedID = Packet.receivedID; 616 this.receivedPacketSizeArray[this.tickCounter % 100] = Packet.receivedSize - this.lastReceivedSize; 617 this.lastReceivedSize = Packet.receivedSize; 618 this.theProfiler.endSection(); 619 this.theProfiler.startSection("snooper"); 620 621 if (!this.usageSnooper.isSnooperRunning() && this.tickCounter > 100) 622 { 623 this.usageSnooper.startSnooper(); 624 } 625 626 if (this.tickCounter % 6000 == 0) 627 { 628 this.usageSnooper.addMemoryStatsToSnooper(); 629 } 630 631 this.theProfiler.endSection(); 632 this.theProfiler.endSection(); 633 FMLCommonHandler.instance().onPostServerTick(); 634 } 635 636 public void updateTimeLightAndEntities() 637 { 638 this.theProfiler.startSection("levels"); 639 int var1; 640 641 Integer[] ids = DimensionManager.getIDs(); 642 for (int x = 0; x < ids.length; x++) 643 { 644 int id = ids[x]; 645 long var2 = System.nanoTime(); 646 647 if (id == 0 || this.getAllowNether()) 648 { 649 WorldServer var4 = DimensionManager.getWorld(id); 650 this.theProfiler.startSection(var4.getWorldInfo().getWorldName()); 651 this.theProfiler.startSection("pools"); 652 var4.getWorldVec3Pool().clear(); 653 this.theProfiler.endSection(); 654 655 if (this.tickCounter % 20 == 0) 656 { 657 this.theProfiler.startSection("timeSync"); 658 this.serverConfigManager.sendPacketToAllPlayersInDimension(new Packet4UpdateTime(var4.getTotalWorldTime(), var4.getWorldTime()), var4.provider.dimensionId); 659 this.theProfiler.endSection(); 660 } 661 662 this.theProfiler.startSection("tick"); 663 FMLCommonHandler.instance().onPreWorldTick(var4); 664 CrashReport var6; 665 666 try 667 { 668 var4.tick(); 669 } 670 catch (Throwable var8) 671 { 672 var6 = CrashReport.makeCrashReport(var8, "Exception ticking world"); 673 var4.addWorldInfoToCrashReport(var6); 674 throw new ReportedException(var6); 675 } 676 677 try 678 { 679 var4.updateEntities(); 680 } 681 catch (Throwable var7) 682 { 683 var6 = CrashReport.makeCrashReport(var7, "Exception ticking world entities"); 684 var4.addWorldInfoToCrashReport(var6); 685 throw new ReportedException(var6); 686 } 687 688 FMLCommonHandler.instance().onPostWorldTick(var4); 689 this.theProfiler.endSection(); 690 this.theProfiler.startSection("tracker"); 691 var4.getEntityTracker().updateTrackedEntities(); 692 this.theProfiler.endSection(); 693 this.theProfiler.endSection(); 694 } 695 696 worldTickTimes.get(id)[this.tickCounter % 100] = System.nanoTime() - var2; 697 } 698 699 this.theProfiler.endStartSection("dim_unloading"); 700 DimensionManager.unloadWorlds(worldTickTimes); 701 this.theProfiler.endStartSection("connection"); 702 this.getNetworkThread().networkTick(); 703 this.theProfiler.endStartSection("players"); 704 this.serverConfigManager.sendPlayerInfoToAllPlayers(); 705 this.theProfiler.endStartSection("tickables"); 706 707 for (var1 = 0; var1 < this.tickables.size(); ++var1) 708 { 709 ((IUpdatePlayerListBox)this.tickables.get(var1)).update(); 710 } 711 712 this.theProfiler.endSection(); 713 } 714 715 public boolean getAllowNether() 716 { 717 return true; 718 } 719 720 public void startServerThread() 721 { 722 (new ThreadMinecraftServer(this, "Server thread")).start(); 723 } 724 725 /** 726 * Returns a File object from the specified string. 727 */ 728 public File getFile(String par1Str) 729 { 730 return new File(this.getDataDirectory(), par1Str); 731 } 732 733 /** 734 * Logs the message with a level of INFO. 735 */ 736 public void logInfo(String par1Str) 737 { 738 logger.info(par1Str); 739 } 740 741 /** 742 * Logs the message with a level of WARN. 743 */ 744 public void logWarning(String par1Str) 745 { 746 logger.warning(par1Str); 747 } 748 749 /** 750 * Gets the worldServer by the given dimension. 751 */ 752 public WorldServer worldServerForDimension(int par1) 753 { 754 WorldServer ret = DimensionManager.getWorld(par1); 755 if (ret == null) 756 { 757 DimensionManager.initDimension(par1); 758 ret = DimensionManager.getWorld(par1); 759 } 760 return ret; 761 } 762 763 @SideOnly(Side.SERVER) 764 public void func_82010_a(IUpdatePlayerListBox par1IUpdatePlayerListBox) 765 { 766 this.tickables.add(par1IUpdatePlayerListBox); 767 } 768 769 /** 770 * Returns the server's hostname. 771 */ 772 public String getHostname() 773 { 774 return this.hostname; 775 } 776 777 /** 778 * Never used, but "getServerPort" is already taken. 779 */ 780 public int getPort() 781 { 782 return this.serverPort; 783 } 784 785 /** 786 * minecraftServer.getMOTD is used in 2 places instead (it is a non-virtual function which returns the same thing) 787 */ 788 public String getServerMOTD() 789 { 790 return this.motd; 791 } 792 793 /** 794 * Returns the server's Minecraft version as string. 795 */ 796 public String getMinecraftVersion() 797 { 798 return "1.4.7"; 799 } 800 801 /** 802 * Returns the number of players currently on the server. 803 */ 804 public int getCurrentPlayerCount() 805 { 806 return this.serverConfigManager.getCurrentPlayerCount(); 807 } 808 809 /** 810 * Returns the maximum number of players allowed on the server. 811 */ 812 public int getMaxPlayers() 813 { 814 return this.serverConfigManager.getMaxPlayers(); 815 } 816 817 /** 818 * Returns an array of the usernames of all the connected players. 819 */ 820 public String[] getAllUsernames() 821 { 822 return this.serverConfigManager.getAllUsernames(); 823 } 824 825 /** 826 * Used by RCon's Query in the form of "MajorServerMod 1.2.3: MyPlugin 1.3; AnotherPlugin 2.1; AndSoForth 1.0". 827 */ 828 public String getPlugins() 829 { 830 return ""; 831 } 832 833 public String executeCommand(String par1Str) 834 { 835 RConConsoleSource.consoleBuffer.resetLog(); 836 this.commandManager.executeCommand(RConConsoleSource.consoleBuffer, par1Str); 837 return RConConsoleSource.consoleBuffer.getChatBuffer(); 838 } 839 840 /** 841 * Returns true if debugging is enabled, false otherwise. 842 */ 843 public boolean isDebuggingEnabled() 844 { 845 return false; 846 } 847 848 /** 849 * Logs the error message with a level of SEVERE. 850 */ 851 public void logSevere(String par1Str) 852 { 853 logger.log(Level.SEVERE, par1Str); 854 } 855 856 /** 857 * If isDebuggingEnabled(), logs the message with a level of INFO. 858 */ 859 public void logDebug(String par1Str) 860 { 861 if (this.isDebuggingEnabled()) 862 { 863 logger.log(Level.INFO, par1Str); 864 } 865 } 866 867 public String getServerModName() 868 { 869 return "fml"; 870 } 871 872 /** 873 * Adds the server info, including from theWorldServer, to the crash report. 874 */ 875 public CrashReport addServerInfoToCrashReport(CrashReport par1CrashReport) 876 { 877 par1CrashReport.func_85056_g().addCrashSectionCallable("Profiler Position", new CallableIsServerModded(this)); 878 879 if (this.worldServers != null && this.worldServers.length > 0 && this.worldServers[0] != null) 880 { 881 par1CrashReport.func_85056_g().addCrashSectionCallable("Vec3 Pool Size", new CallableServerProfiler(this)); 882 } 883 884 if (this.serverConfigManager != null) 885 { 886 par1CrashReport.func_85056_g().addCrashSectionCallable("Player Count", new CallableServerMemoryStats(this)); 887 } 888 889 return par1CrashReport; 890 } 891 892 /** 893 * If par2Str begins with /, then it searches for commands, otherwise it returns players. 894 */ 895 public List getPossibleCompletions(ICommandSender par1ICommandSender, String par2Str) 896 { 897 ArrayList var3 = new ArrayList(); 898 899 if (par2Str.startsWith("/")) 900 { 901 par2Str = par2Str.substring(1); 902 boolean var10 = !par2Str.contains(" "); 903 List var11 = this.commandManager.getPossibleCommands(par1ICommandSender, par2Str); 904 905 if (var11 != null) 906 { 907 Iterator var12 = var11.iterator(); 908 909 while (var12.hasNext()) 910 { 911 String var13 = (String)var12.next(); 912 913 if (var10) 914 { 915 var3.add("/" + var13); 916 } 917 else 918 { 919 var3.add(var13); 920 } 921 } 922 } 923 924 return var3; 925 } 926 else 927 { 928 String[] var4 = par2Str.split(" ", -1); 929 String var5 = var4[var4.length - 1]; 930 String[] var6 = this.serverConfigManager.getAllUsernames(); 931 int var7 = var6.length; 932 933 for (int var8 = 0; var8 < var7; ++var8) 934 { 935 String var9 = var6[var8]; 936 937 if (CommandBase.doesStringStartWith(var5, var9)) 938 { 939 var3.add(var9); 940 } 941 } 942 943 return var3; 944 } 945 } 946 947 /** 948 * Gets mcServer. 949 */ 950 public static MinecraftServer getServer() 951 { 952 return mcServer; 953 } 954 955 /** 956 * Gets the name of this command sender (usually username, but possibly "Rcon") 957 */ 958 public String getCommandSenderName() 959 { 960 return "Server"; 961 } 962 963 public void sendChatToPlayer(String par1Str) 964 { 965 logger.info(StringUtils.stripControlCodes(par1Str)); 966 } 967 968 /** 969 * Returns true if the command sender is allowed to use the given command. 970 */ 971 public boolean canCommandSenderUseCommand(int par1, String par2Str) 972 { 973 return true; 974 } 975 976 /** 977 * Translates and formats the given string key with the given arguments. 978 */ 979 public String translateString(String par1Str, Object ... par2ArrayOfObj) 980 { 981 return StringTranslate.getInstance().translateKeyFormat(par1Str, par2ArrayOfObj); 982 } 983 984 public ICommandManager getCommandManager() 985 { 986 return this.commandManager; 987 } 988 989 /** 990 * Gets KeyPair instanced in MinecraftServer. 991 */ 992 public KeyPair getKeyPair() 993 { 994 return this.serverKeyPair; 995 } 996 997 /** 998 * Gets serverPort. 999 */ 1000 public int getServerPort() 1001 { 1002 return this.serverPort; 1003 } 1004 1005 public void setServerPort(int par1) 1006 { 1007 this.serverPort = par1; 1008 } 1009 1010 /** 1011 * Returns the username of the server owner (for integrated servers) 1012 */ 1013 public String getServerOwner() 1014 { 1015 return this.serverOwner; 1016 } 1017 1018 /** 1019 * Sets the username of the owner of this server (in the case of an integrated server) 1020 */ 1021 public void setServerOwner(String par1Str) 1022 { 1023 this.serverOwner = par1Str; 1024 } 1025 1026 public boolean isSinglePlayer() 1027 { 1028 return this.serverOwner != null; 1029 } 1030 1031 public String getFolderName() 1032 { 1033 return this.folderName; 1034 } 1035 1036 public void setFolderName(String par1Str) 1037 { 1038 this.folderName = par1Str; 1039 } 1040 1041 @SideOnly(Side.CLIENT) 1042 public void setWorldName(String par1Str) 1043 { 1044 this.worldName = par1Str; 1045 } 1046 1047 @SideOnly(Side.CLIENT) 1048 public String getWorldName() 1049 { 1050 return this.worldName; 1051 } 1052 1053 public void setKeyPair(KeyPair par1KeyPair) 1054 { 1055 this.serverKeyPair = par1KeyPair; 1056 } 1057 1058 public void setDifficultyForAllWorlds(int par1) 1059 { 1060 for (int var2 = 0; var2 < this.worldServers.length; ++var2) 1061 { 1062 WorldServer var3 = this.worldServers[var2]; 1063 1064 if (var3 != null) 1065 { 1066 if (var3.getWorldInfo().isHardcoreModeEnabled()) 1067 { 1068 var3.difficultySetting = 3; 1069 var3.setAllowedSpawnTypes(true, true); 1070 } 1071 else if (this.isSinglePlayer()) 1072 { 1073 var3.difficultySetting = par1; 1074 var3.setAllowedSpawnTypes(var3.difficultySetting > 0, true); 1075 } 1076 else 1077 { 1078 var3.difficultySetting = par1; 1079 var3.setAllowedSpawnTypes(this.allowSpawnMonsters(), this.canSpawnAnimals); 1080 } 1081 } 1082 } 1083 } 1084 1085 protected boolean allowSpawnMonsters() 1086 { 1087 return true; 1088 } 1089 1090 /** 1091 * Gets whether this is a demo or not. 1092 */ 1093 public boolean isDemo() 1094 { 1095 return this.isDemo; 1096 } 1097 1098 /** 1099 * Sets whether this is a demo or not. 1100 */ 1101 public void setDemo(boolean par1) 1102 { 1103 this.isDemo = par1; 1104 } 1105 1106 public void canCreateBonusChest(boolean par1) 1107 { 1108 this.enableBonusChest = par1; 1109 } 1110 1111 public ISaveFormat getActiveAnvilConverter() 1112 { 1113 return this.anvilConverterForAnvilFile; 1114 } 1115 1116 /** 1117 * WARNING : directly calls 1118 * getActiveAnvilConverter().deleteWorldDirectory(theWorldServer[0].getSaveHandler().getSaveDirectoryName()); 1119 */ 1120 public void deleteWorldAndStopServer() 1121 { 1122 this.worldIsBeingDeleted = true; 1123 this.getActiveAnvilConverter().flushCache(); 1124 1125 for (int var1 = 0; var1 < this.worldServers.length; ++var1) 1126 { 1127 WorldServer var2 = this.worldServers[var1]; 1128 1129 if (var2 != null) 1130 { 1131 MinecraftForge.EVENT_BUS.post(new WorldEvent.Unload(var2)); 1132 var2.flush(); 1133 } 1134 } 1135 1136 this.getActiveAnvilConverter().deleteWorldDirectory(this.worldServers[0].getSaveHandler().getSaveDirectoryName()); 1137 this.initiateShutdown(); 1138 } 1139 1140 public String getTexturePack() 1141 { 1142 return this.texturePack; 1143 } 1144 1145 public void setTexturePack(String par1Str) 1146 { 1147 this.texturePack = par1Str; 1148 } 1149 1150 public void addServerStatsToSnooper(PlayerUsageSnooper par1PlayerUsageSnooper) 1151 { 1152 par1PlayerUsageSnooper.addData("whitelist_enabled", Boolean.valueOf(false)); 1153 par1PlayerUsageSnooper.addData("whitelist_count", Integer.valueOf(0)); 1154 par1PlayerUsageSnooper.addData("players_current", Integer.valueOf(this.getCurrentPlayerCount())); 1155 par1PlayerUsageSnooper.addData("players_max", Integer.valueOf(this.getMaxPlayers())); 1156 par1PlayerUsageSnooper.addData("players_seen", Integer.valueOf(this.serverConfigManager.getAvailablePlayerDat().length)); 1157 par1PlayerUsageSnooper.addData("uses_auth", Boolean.valueOf(this.onlineMode)); 1158 par1PlayerUsageSnooper.addData("gui_state", this.getGuiEnabled() ? "enabled" : "disabled"); 1159 par1PlayerUsageSnooper.addData("avg_tick_ms", Integer.valueOf((int)(MathHelper.average(this.tickTimeArray) * 1.0E-6D))); 1160 par1PlayerUsageSnooper.addData("avg_sent_packet_count", Integer.valueOf((int)MathHelper.average(this.sentPacketCountArray))); 1161 par1PlayerUsageSnooper.addData("avg_sent_packet_size", Integer.valueOf((int)MathHelper.average(this.sentPacketSizeArray))); 1162 par1PlayerUsageSnooper.addData("avg_rec_packet_count", Integer.valueOf((int)MathHelper.average(this.receivedPacketCountArray))); 1163 par1PlayerUsageSnooper.addData("avg_rec_packet_size", Integer.valueOf((int)MathHelper.average(this.receivedPacketSizeArray))); 1164 int var2 = 0; 1165 1166 for (int var3 = 0; var3 < this.worldServers.length; ++var3) 1167 { 1168 if (this.worldServers[var3] != null) 1169 { 1170 WorldServer var4 = this.worldServers[var3]; 1171 WorldInfo var5 = var4.getWorldInfo(); 1172 par1PlayerUsageSnooper.addData("world[" + var2 + "][dimension]", Integer.valueOf(var4.provider.dimensionId)); 1173 par1PlayerUsageSnooper.addData("world[" + var2 + "][mode]", var5.getGameType()); 1174 par1PlayerUsageSnooper.addData("world[" + var2 + "][difficulty]", Integer.valueOf(var4.difficultySetting)); 1175 par1PlayerUsageSnooper.addData("world[" + var2 + "][hardcore]", Boolean.valueOf(var5.isHardcoreModeEnabled())); 1176 par1PlayerUsageSnooper.addData("world[" + var2 + "][generator_name]", var5.getTerrainType().getWorldTypeName()); 1177 par1PlayerUsageSnooper.addData("world[" + var2 + "][generator_version]", Integer.valueOf(var5.getTerrainType().getGeneratorVersion())); 1178 par1PlayerUsageSnooper.addData("world[" + var2 + "][height]", Integer.valueOf(this.buildLimit)); 1179 par1PlayerUsageSnooper.addData("world[" + var2 + "][chunks_loaded]", Integer.valueOf(var4.getChunkProvider().getLoadedChunkCount())); 1180 ++var2; 1181 } 1182 } 1183 1184 par1PlayerUsageSnooper.addData("worlds", Integer.valueOf(var2)); 1185 } 1186 1187 public void addServerTypeToSnooper(PlayerUsageSnooper par1PlayerUsageSnooper) 1188 { 1189 par1PlayerUsageSnooper.addData("singleplayer", Boolean.valueOf(this.isSinglePlayer())); 1190 par1PlayerUsageSnooper.addData("server_brand", this.getServerModName()); 1191 par1PlayerUsageSnooper.addData("gui_supported", GraphicsEnvironment.isHeadless() ? "headless" : "supported"); 1192 par1PlayerUsageSnooper.addData("dedicated", Boolean.valueOf(this.isDedicatedServer())); 1193 } 1194 1195 /** 1196 * Returns whether snooping is enabled or not. 1197 */ 1198 public boolean isSnooperEnabled() 1199 { 1200 return true; 1201 } 1202 1203 /** 1204 * This is checked to be 16 upon receiving the packet, otherwise the packet is ignored. 1205 */ 1206 public int textureSize() 1207 { 1208 return 16; 1209 } 1210 1211 public abstract boolean isDedicatedServer(); 1212 1213 public boolean isServerInOnlineMode() 1214 { 1215 return this.onlineMode; 1216 } 1217 1218 public void setOnlineMode(boolean par1) 1219 { 1220 this.onlineMode = par1; 1221 } 1222 1223 public boolean getCanSpawnAnimals() 1224 { 1225 return this.canSpawnAnimals; 1226 } 1227 1228 public void setCanSpawnAnimals(boolean par1) 1229 { 1230 this.canSpawnAnimals = par1; 1231 } 1232 1233 public boolean getCanSpawnNPCs() 1234 { 1235 return this.canSpawnNPCs; 1236 } 1237 1238 public void setCanSpawnNPCs(boolean par1) 1239 { 1240 this.canSpawnNPCs = par1; 1241 } 1242 1243 public boolean isPVPEnabled() 1244 { 1245 return this.pvpEnabled; 1246 } 1247 1248 public void setAllowPvp(boolean par1) 1249 { 1250 this.pvpEnabled = par1; 1251 } 1252 1253 public boolean isFlightAllowed() 1254 { 1255 return this.allowFlight; 1256 } 1257 1258 public void setAllowFlight(boolean par1) 1259 { 1260 this.allowFlight = par1; 1261 } 1262 1263 /** 1264 * Return whether command blocks are enabled. 1265 */ 1266 public abstract boolean isCommandBlockEnabled(); 1267 1268 public String getMOTD() 1269 { 1270 return this.motd; 1271 } 1272 1273 public void setMOTD(String par1Str) 1274 { 1275 this.motd = par1Str; 1276 } 1277 1278 public int getBuildLimit() 1279 { 1280 return this.buildLimit; 1281 } 1282 1283 public void setBuildLimit(int par1) 1284 { 1285 this.buildLimit = par1; 1286 } 1287 1288 public boolean isServerStopped() 1289 { 1290 return this.serverStopped; 1291 } 1292 1293 public ServerConfigurationManager getConfigurationManager() 1294 { 1295 return this.serverConfigManager; 1296 } 1297 1298 public void setConfigurationManager(ServerConfigurationManager par1ServerConfigurationManager) 1299 { 1300 this.serverConfigManager = par1ServerConfigurationManager; 1301 } 1302 1303 /** 1304 * Sets the game type for all worlds. 1305 */ 1306 public void setGameType(EnumGameType par1EnumGameType) 1307 { 1308 for (int var2 = 0; var2 < this.worldServers.length; ++var2) 1309 { 1310 getServer().worldServers[var2].getWorldInfo().setGameType(par1EnumGameType); 1311 } 1312 } 1313 1314 public abstract NetworkListenThread getNetworkThread(); 1315 1316 @SideOnly(Side.CLIENT) 1317 public boolean serverIsInRunLoop() 1318 { 1319 return this.serverIsRunning; 1320 } 1321 1322 public boolean getGuiEnabled() 1323 { 1324 return false; 1325 } 1326 1327 /** 1328 * On dedicated does nothing. On integrated, sets commandsAllowedForAll, gameType and allows external connections. 1329 */ 1330 public abstract String shareToLAN(EnumGameType var1, boolean var2); 1331 1332 public int getTickCounter() 1333 { 1334 return this.tickCounter; 1335 } 1336 1337 public void enableProfiling() 1338 { 1339 this.startProfiling = true; 1340 } 1341 1342 @SideOnly(Side.CLIENT) 1343 public PlayerUsageSnooper getPlayerUsageSnooper() 1344 { 1345 return this.usageSnooper; 1346 } 1347 1348 /** 1349 * Return the coordinates for this player as ChunkCoordinates. 1350 */ 1351 public ChunkCoordinates getPlayerCoordinates() 1352 { 1353 return new ChunkCoordinates(0, 0, 0); 1354 } 1355 1356 /** 1357 * Return the spawn protection area's size. 1358 */ 1359 public int getSpawnProtectionSize() 1360 { 1361 return 16; 1362 } 1363 1364 /** 1365 * Gets the current player count, maximum player count, and player entity list. 1366 */ 1367 public static ServerConfigurationManager getServerConfigurationManager(MinecraftServer par0MinecraftServer) 1368 { 1369 return par0MinecraftServer.serverConfigManager; 1370 } 1371 1372 @SideOnly(Side.SERVER) 1373 public static void main(String[] par0ArrayOfStr) 1374 { 1375 FMLRelauncher.handleServerRelaunch(new ArgsWrapper(par0ArrayOfStr)); 1376 } 1377 1378 @SideOnly(Side.SERVER) 1379 public static void fmlReentry(ArgsWrapper wrap) 1380 { 1381 String[] par0ArrayOfStr = wrap.args; 1382 StatList.nopInit(); 1383 1384 try 1385 { 1386 boolean var1 = !GraphicsEnvironment.isHeadless(); 1387 String var2 = null; 1388 String var3 = "."; 1389 String var4 = null; 1390 boolean var5 = false; 1391 boolean var6 = false; 1392 int var7 = -1; 1393 1394 for (int var8 = 0; var8 < par0ArrayOfStr.length; ++var8) 1395 { 1396 String var9 = par0ArrayOfStr[var8]; 1397 String var10 = var8 == par0ArrayOfStr.length - 1 ? null : par0ArrayOfStr[var8 + 1]; 1398 boolean var11 = false; 1399 1400 if (!var9.equals("nogui") && !var9.equals("--nogui")) 1401 { 1402 if (var9.equals("--port") && var10 != null) 1403 { 1404 var11 = true; 1405 1406 try 1407 { 1408 var7 = Integer.parseInt(var10); 1409 } 1410 catch (NumberFormatException var13) 1411 { 1412 ; 1413 } 1414 } 1415 else if (var9.equals("--singleplayer") && var10 != null) 1416 { 1417 var11 = true; 1418 var2 = var10; 1419 } 1420 else if (var9.equals("--universe") && var10 != null) 1421 { 1422 var11 = true; 1423 var3 = var10; 1424 } 1425 else if (var9.equals("--world") && var10 != null) 1426 { 1427 var11 = true; 1428 var4 = var10; 1429 } 1430 else if (var9.equals("--demo")) 1431 { 1432 var5 = true; 1433 } 1434 else if (var9.equals("--bonusChest")) 1435 { 1436 var6 = true; 1437 } 1438 } 1439 else 1440 { 1441 var1 = false; 1442 } 1443 1444 if (var11) 1445 { 1446 ++var8; 1447 } 1448 } 1449 1450 DedicatedServer var15 = new DedicatedServer(new File(var3)); 1451 1452 if (var2 != null) 1453 { 1454 var15.setServerOwner(var2); 1455 } 1456 1457 if (var4 != null) 1458 { 1459 var15.setFolderName(var4); 1460 } 1461 1462 if (var7 >= 0) 1463 { 1464 var15.setServerPort(var7); 1465 } 1466 1467 if (var5) 1468 { 1469 var15.setDemo(true); 1470 } 1471 1472 if (var6) 1473 { 1474 var15.canCreateBonusChest(true); 1475 } 1476 1477 if (var1) 1478 { 1479 var15.enableGui(); 1480 } 1481 1482 var15.startServerThread(); 1483 Runtime.getRuntime().addShutdownHook(new ThreadDedicatedServer(var15)); 1484 } 1485 catch (Exception var14) 1486 { 1487 logger.log(Level.SEVERE, "Failed to start the minecraft server", var14); 1488 } 1489 } 1490 }