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