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}