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