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