001    package net.minecraft.server.dedicated;
002    
003    import cpw.mods.fml.common.FMLCommonHandler;
004    import cpw.mods.fml.relauncher.Side;
005    import cpw.mods.fml.relauncher.SideOnly;
006    import java.io.File;
007    import java.io.IOException;
008    import java.net.InetAddress;
009    import java.util.ArrayList;
010    import java.util.Collections;
011    import java.util.List;
012    import java.util.Random;
013    import java.util.logging.Level;
014    import net.minecraft.command.ICommandSender;
015    import net.minecraft.command.ServerCommand;
016    import net.minecraft.crash.CrashReport;
017    import net.minecraft.network.NetworkListenThread;
018    import net.minecraft.network.rcon.IServer;
019    import net.minecraft.network.rcon.RConThreadMain;
020    import net.minecraft.network.rcon.RConThreadQuery;
021    import net.minecraft.profiler.PlayerUsageSnooper;
022    import net.minecraft.server.MinecraftServer;
023    import net.minecraft.server.gui.ServerGUI;
024    import net.minecraft.server.management.ServerConfigurationManager;
025    import net.minecraft.util.CryptManager;
026    import net.minecraft.util.MathHelper;
027    import net.minecraft.world.EnumGameType;
028    import net.minecraft.world.WorldSettings;
029    import net.minecraft.world.WorldType;
030    
031    public class DedicatedServer extends MinecraftServer implements IServer
032    {
033        private final List pendingCommandList = Collections.synchronizedList(new ArrayList());
034        private RConThreadQuery theRConThreadQuery;
035        private RConThreadMain theRConThreadMain;
036        private PropertyManager settings;
037        private boolean canSpawnStructures;
038        private EnumGameType gameType;
039        private NetworkListenThread networkThread;
040        private boolean guiIsEnabled = false;
041    
042        public DedicatedServer(File par1File)
043        {
044            super(par1File);
045            new DedicatedServerSleepThread(this);
046        }
047    
048        /**
049         * Initialises the server and starts it.
050         */
051        protected boolean startServer() throws IOException
052        {
053            DedicatedServerCommandThread var1 = new DedicatedServerCommandThread(this);
054            var1.setDaemon(true);
055            var1.start();
056            ConsoleLogManager.init();
057            logger.info("Starting minecraft server version 1.4.7");
058    
059            if (Runtime.getRuntime().maxMemory() / 1024L / 1024L < 512L)
060            {
061                logger.warning("To start the server with more ram, launch it as \"java -Xmx1024M -Xms1024M -jar minecraft_server.jar\"");
062            }
063    
064            FMLCommonHandler.instance().onServerStart(this);
065    
066            logger.info("Loading properties");
067            this.settings = new PropertyManager(new File("server.properties"));
068    
069            if (this.isSinglePlayer())
070            {
071                this.setHostname("127.0.0.1");
072            }
073            else
074            {
075                this.setOnlineMode(this.settings.getBooleanProperty("online-mode", true));
076                this.setHostname(this.settings.getProperty("server-ip", ""));
077            }
078    
079            this.setCanSpawnAnimals(this.settings.getBooleanProperty("spawn-animals", true));
080            this.setCanSpawnNPCs(this.settings.getBooleanProperty("spawn-npcs", true));
081            this.setAllowPvp(this.settings.getBooleanProperty("pvp", true));
082            this.setAllowFlight(this.settings.getBooleanProperty("allow-flight", false));
083            this.setTexturePack(this.settings.getProperty("texture-pack", ""));
084            this.setMOTD(this.settings.getProperty("motd", "A Minecraft Server"));
085    
086            if (this.settings.getIntProperty("difficulty", 1) < 0)
087            {
088                this.settings.setProperty("difficulty", Integer.valueOf(0));
089            }
090            else if (this.settings.getIntProperty("difficulty", 1) > 3)
091            {
092                this.settings.setProperty("difficulty", Integer.valueOf(3));
093            }
094    
095            this.canSpawnStructures = this.settings.getBooleanProperty("generate-structures", true);
096            int var2 = this.settings.getIntProperty("gamemode", EnumGameType.SURVIVAL.getID());
097            this.gameType = WorldSettings.getGameTypeById(var2);
098            logger.info("Default game type: " + this.gameType);
099            InetAddress var3 = null;
100    
101            if (this.getServerHostname().length() > 0)
102            {
103                var3 = InetAddress.getByName(this.getServerHostname());
104            }
105    
106            if (this.getServerPort() < 0)
107            {
108                this.setServerPort(this.settings.getIntProperty("server-port", 25565));
109            }
110    
111            logger.info("Generating keypair");
112            this.setKeyPair(CryptManager.createNewKeyPair());
113            logger.info("Starting Minecraft server on " + (this.getServerHostname().length() == 0 ? "*" : this.getServerHostname()) + ":" + this.getServerPort());
114    
115            try
116            {
117                this.networkThread = new DedicatedServerListenThread(this, var3, this.getServerPort());
118            }
119            catch (IOException var16)
120            {
121                logger.warning("**** FAILED TO BIND TO PORT!");
122                logger.log(Level.WARNING, "The exception was: " + var16.toString());
123                logger.warning("Perhaps a server is already running on that port?");
124                return false;
125            }
126    
127            if (!this.isServerInOnlineMode())
128            {
129                logger.warning("**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!");
130                logger.warning("The server will make no attempt to authenticate usernames. Beware.");
131                logger.warning("While this makes the game possible to play without internet access, it also opens up the ability for hackers to connect with any username they choose.");
132                logger.warning("To change this, set \"online-mode\" to \"true\" in the server.properties file.");
133            }
134    
135            FMLCommonHandler.instance().onServerStarted();
136    
137            this.setConfigurationManager(new DedicatedPlayerList(this));
138            long var4 = System.nanoTime();
139    
140            if (this.getFolderName() == null)
141            {
142                this.setFolderName(this.settings.getProperty("level-name", "world"));
143            }
144    
145            String var6 = this.settings.getProperty("level-seed", "");
146            String var7 = this.settings.getProperty("level-type", "DEFAULT");
147            String var8 = this.settings.getProperty("generator-settings", "");
148            long var9 = (new Random()).nextLong();
149    
150            if (var6.length() > 0)
151            {
152                try
153                {
154                    long var11 = Long.parseLong(var6);
155    
156                    if (var11 != 0L)
157                    {
158                        var9 = var11;
159                    }
160                }
161                catch (NumberFormatException var15)
162                {
163                    var9 = (long)var6.hashCode();
164                }
165            }
166    
167            WorldType var17 = WorldType.parseWorldType(var7);
168    
169            if (var17 == null)
170            {
171                var17 = WorldType.DEFAULT;
172            }
173    
174            this.setBuildLimit(this.settings.getIntProperty("max-build-height", 256));
175            this.setBuildLimit((this.getBuildLimit() + 8) / 16 * 16);
176            this.setBuildLimit(MathHelper.clamp_int(this.getBuildLimit(), 64, 256));
177            this.settings.setProperty("max-build-height", Integer.valueOf(this.getBuildLimit()));
178            logger.info("Preparing level \"" + this.getFolderName() + "\"");
179            this.loadAllWorlds(this.getFolderName(), this.getFolderName(), var9, var17, var8);
180            long var12 = System.nanoTime() - var4;
181            String var14 = String.format("%.3fs", new Object[] {Double.valueOf((double)var12 / 1.0E9D)});
182            logger.info("Done (" + var14 + ")! For help, type \"help\" or \"?\"");
183    
184            if (this.settings.getBooleanProperty("enable-query", false))
185            {
186                logger.info("Starting GS4 status listener");
187                this.theRConThreadQuery = new RConThreadQuery(this);
188                this.theRConThreadQuery.startThread();
189            }
190    
191            if (this.settings.getBooleanProperty("enable-rcon", false))
192            {
193                logger.info("Starting remote control listener");
194                this.theRConThreadMain = new RConThreadMain(this);
195                this.theRConThreadMain.startThread();
196            }
197    
198            FMLCommonHandler.instance().handleServerStarting(this);
199    
200            return true;
201        }
202    
203        public boolean canStructuresSpawn()
204        {
205            return this.canSpawnStructures;
206        }
207    
208        public EnumGameType getGameType()
209        {
210            return this.gameType;
211        }
212    
213        /**
214         * Defaults to "1" (Easy) for the dedicated server, defaults to "2" (Normal) on the client.
215         */
216        public int getDifficulty()
217        {
218            return this.settings.getIntProperty("difficulty", 1);
219        }
220    
221        /**
222         * Defaults to false.
223         */
224        public boolean isHardcore()
225        {
226            return this.settings.getBooleanProperty("hardcore", false);
227        }
228    
229        /**
230         * Called on exit from the main run() loop.
231         */
232        protected void finalTick(CrashReport par1CrashReport)
233        {
234            while (this.isServerRunning())
235            {
236                this.executePendingCommands();
237    
238                try
239                {
240                    Thread.sleep(10L);
241                }
242                catch (InterruptedException var3)
243                {
244                    var3.printStackTrace();
245                }
246            }
247        }
248    
249        /**
250         * Adds the server info, including from theWorldServer, to the crash report.
251         */
252        public CrashReport addServerInfoToCrashReport(CrashReport par1CrashReport)
253        {
254            par1CrashReport = super.addServerInfoToCrashReport(par1CrashReport);
255            par1CrashReport.func_85056_g().addCrashSectionCallable("Is Modded", new CallableType(this));
256            par1CrashReport.func_85056_g().addCrashSectionCallable("Type", new CallableServerType(this));
257            return par1CrashReport;
258        }
259    
260        /**
261         * Directly calls System.exit(0), instantly killing the program.
262         */
263        protected void systemExitNow()
264        {
265            System.exit(0);
266        }
267    
268        public void updateTimeLightAndEntities()
269        {
270            super.updateTimeLightAndEntities();
271            this.executePendingCommands();
272        }
273    
274        public boolean getAllowNether()
275        {
276            return this.settings.getBooleanProperty("allow-nether", true);
277        }
278    
279        public boolean allowSpawnMonsters()
280        {
281            return this.settings.getBooleanProperty("spawn-monsters", true);
282        }
283    
284        public void addServerStatsToSnooper(PlayerUsageSnooper par1PlayerUsageSnooper)
285        {
286            par1PlayerUsageSnooper.addData("whitelist_enabled", Boolean.valueOf(this.getDedicatedPlayerList().isWhiteListEnabled()));
287            par1PlayerUsageSnooper.addData("whitelist_count", Integer.valueOf(this.getDedicatedPlayerList().getWhiteListedPlayers().size()));
288            super.addServerStatsToSnooper(par1PlayerUsageSnooper);
289        }
290    
291        /**
292         * Returns whether snooping is enabled or not.
293         */
294        public boolean isSnooperEnabled()
295        {
296            return this.settings.getBooleanProperty("snooper-enabled", true);
297        }
298    
299        public void addPendingCommand(String par1Str, ICommandSender par2ICommandSender)
300        {
301            this.pendingCommandList.add(new ServerCommand(par1Str, par2ICommandSender));
302        }
303    
304        public void executePendingCommands()
305        {
306            while (!this.pendingCommandList.isEmpty())
307            {
308                ServerCommand var1 = (ServerCommand)this.pendingCommandList.remove(0);
309                this.getCommandManager().executeCommand(var1.sender, var1.command);
310            }
311        }
312    
313        public boolean isDedicatedServer()
314        {
315            return true;
316        }
317    
318        public DedicatedPlayerList getDedicatedPlayerList()
319        {
320            return (DedicatedPlayerList)super.getConfigurationManager();
321        }
322    
323        public NetworkListenThread getNetworkThread()
324        {
325            return this.networkThread;
326        }
327    
328        /**
329         * Gets an integer property. If it does not exist, set it to the specified value.
330         */
331        public int getIntProperty(String par1Str, int par2)
332        {
333            return this.settings.getIntProperty(par1Str, par2);
334        }
335    
336        /**
337         * Gets a string property. If it does not exist, set it to the specified value.
338         */
339        public String getStringProperty(String par1Str, String par2Str)
340        {
341            return this.settings.getProperty(par1Str, par2Str);
342        }
343    
344        /**
345         * Gets a boolean property. If it does not exist, set it to the specified value.
346         */
347        public boolean getBooleanProperty(String par1Str, boolean par2)
348        {
349            return this.settings.getBooleanProperty(par1Str, par2);
350        }
351    
352        /**
353         * Saves an Object with the given property name.
354         */
355        public void setProperty(String par1Str, Object par2Obj)
356        {
357            this.settings.setProperty(par1Str, par2Obj);
358        }
359    
360        /**
361         * Saves all of the server properties to the properties file.
362         */
363        public void saveProperties()
364        {
365            this.settings.saveProperties();
366        }
367    
368        /**
369         * Returns the filename where server properties are stored
370         */
371        public String getSettingsFilename()
372        {
373            File var1 = this.settings.getPropertiesFile();
374            return var1 != null ? var1.getAbsolutePath() : "No settings file";
375        }
376    
377        public boolean getGuiEnabled()
378        {
379            return this.guiIsEnabled;
380        }
381    
382        /**
383         * On dedicated does nothing. On integrated, sets commandsAllowedForAll, gameType and allows external connections.
384         */
385        public String shareToLAN(EnumGameType par1EnumGameType, boolean par2)
386        {
387            return "";
388        }
389    
390        /**
391         * Return whether command blocks are enabled.
392         */
393        public boolean isCommandBlockEnabled()
394        {
395            return this.settings.getBooleanProperty("enable-command-block", false);
396        }
397    
398        /**
399         * Return the spawn protection area's size.
400         */
401        public int getSpawnProtectionSize()
402        {
403            return this.settings.getIntProperty("spawn-protection", super.getSpawnProtectionSize());
404        }
405    
406        public ServerConfigurationManager getConfigurationManager()
407        {
408            return this.getDedicatedPlayerList();
409        }
410    
411        @SideOnly(Side.SERVER)
412        public void enableGui()
413        {
414            ServerGUI.initGUI(this);
415            this.guiIsEnabled = true;
416        }
417    }