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