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