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