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