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