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