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 }