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