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