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