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