001package net.minecraft.server.dedicated;
002
003import cpw.mods.fml.common.FMLCommonHandler;
004import cpw.mods.fml.relauncher.Side;
005import cpw.mods.fml.relauncher.SideOnly;
006import java.io.File;
007import java.io.IOException;
008import java.net.InetAddress;
009import java.util.ArrayList;
010import java.util.Collections;
011import java.util.List;
012import java.util.Random;
013import java.util.logging.Level;
014import net.minecraft.command.ICommandSender;
015import net.minecraft.command.ServerCommand;
016import net.minecraft.crash.CrashReport;
017import net.minecraft.network.NetworkListenThread;
018import net.minecraft.network.rcon.IServer;
019import net.minecraft.network.rcon.RConThreadMain;
020import net.minecraft.network.rcon.RConThreadQuery;
021import net.minecraft.profiler.PlayerUsageSnooper;
022import net.minecraft.server.MinecraftServer;
023import net.minecraft.server.gui.ServerGUI;
024import net.minecraft.server.management.ServerConfigurationManager;
025import net.minecraft.util.CryptManager;
026import net.minecraft.util.MathHelper;
027import net.minecraft.world.EnumGameType;
028import net.minecraft.world.WorldSettings;
029import net.minecraft.world.WorldType;
030
031public 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        if (!FMLCommonHandler.instance().handleServerAboutToStart(this)) { return false; }
179        logger.info("Preparing level \"" + this.getFolderName() + "\"");
180        this.loadAllWorlds(this.getFolderName(), this.getFolderName(), var9, var17, var8);
181        long var12 = System.nanoTime() - var4;
182        String var14 = String.format("%.3fs", new Object[] {Double.valueOf((double)var12 / 1.0E9D)});
183        logger.info("Done (" + var14 + ")! For help, type \"help\" or \"?\"");
184
185        if (this.settings.getBooleanProperty("enable-query", false))
186        {
187            logger.info("Starting GS4 status listener");
188            this.theRConThreadQuery = new RConThreadQuery(this);
189            this.theRConThreadQuery.startThread();
190        }
191
192        if (this.settings.getBooleanProperty("enable-rcon", false))
193        {
194            logger.info("Starting remote control listener");
195            this.theRConThreadMain = new RConThreadMain(this);
196            this.theRConThreadMain.startThread();
197        }
198
199        return FMLCommonHandler.instance().handleServerStarting(this);
200    }
201
202    public boolean canStructuresSpawn()
203    {
204        return this.canSpawnStructures;
205    }
206
207    public EnumGameType getGameType()
208    {
209        return this.gameType;
210    }
211
212    /**
213     * Defaults to "1" (Easy) for the dedicated server, defaults to "2" (Normal) on the client.
214     */
215    public int getDifficulty()
216    {
217        return this.settings.getIntProperty("difficulty", 1);
218    }
219
220    /**
221     * Defaults to false.
222     */
223    public boolean isHardcore()
224    {
225        return this.settings.getBooleanProperty("hardcore", false);
226    }
227
228    /**
229     * Called on exit from the main run() loop.
230     */
231    protected void finalTick(CrashReport par1CrashReport)
232    {
233        while (this.isServerRunning())
234        {
235            this.executePendingCommands();
236
237            try
238            {
239                Thread.sleep(10L);
240            }
241            catch (InterruptedException var3)
242            {
243                var3.printStackTrace();
244            }
245        }
246    }
247
248    /**
249     * Adds the server info, including from theWorldServer, to the crash report.
250     */
251    public CrashReport addServerInfoToCrashReport(CrashReport par1CrashReport)
252    {
253        par1CrashReport = super.addServerInfoToCrashReport(par1CrashReport);
254        par1CrashReport.func_85056_g().addCrashSectionCallable("Is Modded", new CallableType(this));
255        par1CrashReport.func_85056_g().addCrashSectionCallable("Type", new CallableServerType(this));
256        return par1CrashReport;
257    }
258
259    /**
260     * Directly calls System.exit(0), instantly killing the program.
261     */
262    protected void systemExitNow()
263    {
264        System.exit(0);
265    }
266
267    public void updateTimeLightAndEntities()
268    {
269        super.updateTimeLightAndEntities();
270        this.executePendingCommands();
271    }
272
273    public boolean getAllowNether()
274    {
275        return this.settings.getBooleanProperty("allow-nether", true);
276    }
277
278    public boolean allowSpawnMonsters()
279    {
280        return this.settings.getBooleanProperty("spawn-monsters", true);
281    }
282
283    public void addServerStatsToSnooper(PlayerUsageSnooper par1PlayerUsageSnooper)
284    {
285        par1PlayerUsageSnooper.addData("whitelist_enabled", Boolean.valueOf(this.getDedicatedPlayerList().isWhiteListEnabled()));
286        par1PlayerUsageSnooper.addData("whitelist_count", Integer.valueOf(this.getDedicatedPlayerList().getWhiteListedPlayers().size()));
287        super.addServerStatsToSnooper(par1PlayerUsageSnooper);
288    }
289
290    /**
291     * Returns whether snooping is enabled or not.
292     */
293    public boolean isSnooperEnabled()
294    {
295        return this.settings.getBooleanProperty("snooper-enabled", true);
296    }
297
298    public void addPendingCommand(String par1Str, ICommandSender par2ICommandSender)
299    {
300        this.pendingCommandList.add(new ServerCommand(par1Str, par2ICommandSender));
301    }
302
303    public void executePendingCommands()
304    {
305        while (!this.pendingCommandList.isEmpty())
306        {
307            ServerCommand var1 = (ServerCommand)this.pendingCommandList.remove(0);
308            this.getCommandManager().executeCommand(var1.sender, var1.command);
309        }
310    }
311
312    public boolean isDedicatedServer()
313    {
314        return true;
315    }
316
317    public DedicatedPlayerList getDedicatedPlayerList()
318    {
319        return (DedicatedPlayerList)super.getConfigurationManager();
320    }
321
322    public NetworkListenThread getNetworkThread()
323    {
324        return this.networkThread;
325    }
326
327    /**
328     * Gets an integer property. If it does not exist, set it to the specified value.
329     */
330    public int getIntProperty(String par1Str, int par2)
331    {
332        return this.settings.getIntProperty(par1Str, par2);
333    }
334
335    /**
336     * Gets a string property. If it does not exist, set it to the specified value.
337     */
338    public String getStringProperty(String par1Str, String par2Str)
339    {
340        return this.settings.getProperty(par1Str, par2Str);
341    }
342
343    /**
344     * Gets a boolean property. If it does not exist, set it to the specified value.
345     */
346    public boolean getBooleanProperty(String par1Str, boolean par2)
347    {
348        return this.settings.getBooleanProperty(par1Str, par2);
349    }
350
351    /**
352     * Saves an Object with the given property name.
353     */
354    public void setProperty(String par1Str, Object par2Obj)
355    {
356        this.settings.setProperty(par1Str, par2Obj);
357    }
358
359    /**
360     * Saves all of the server properties to the properties file.
361     */
362    public void saveProperties()
363    {
364        this.settings.saveProperties();
365    }
366
367    /**
368     * Returns the filename where server properties are stored
369     */
370    public String getSettingsFilename()
371    {
372        File var1 = this.settings.getPropertiesFile();
373        return var1 != null ? var1.getAbsolutePath() : "No settings file";
374    }
375
376    public boolean getGuiEnabled()
377    {
378        return this.guiIsEnabled;
379    }
380
381    /**
382     * On dedicated does nothing. On integrated, sets commandsAllowedForAll, gameType and allows external connections.
383     */
384    public String shareToLAN(EnumGameType par1EnumGameType, boolean par2)
385    {
386        return "";
387    }
388
389    /**
390     * Return whether command blocks are enabled.
391     */
392    public boolean isCommandBlockEnabled()
393    {
394        return this.settings.getBooleanProperty("enable-command-block", false);
395    }
396
397    /**
398     * Return the spawn protection area's size.
399     */
400    public int getSpawnProtectionSize()
401    {
402        return this.settings.getIntProperty("spawn-protection", super.getSpawnProtectionSize());
403    }
404
405    public ServerConfigurationManager getConfigurationManager()
406    {
407        return this.getDedicatedPlayerList();
408    }
409
410    @SideOnly(Side.SERVER)
411    public void enableGui()
412    {
413        ServerGUI.initGUI(this);
414        this.guiIsEnabled = true;
415    }
416}