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