Fork of Poseidon providing Bukkit #1060 to older Beta versions (b1.0-b1.7.3)
at develop 631 lines 24 kB view raw
1package net.minecraft.server; 2 3import com.legacyminecraft.poseidon.Poseidon; 4import com.legacyminecraft.poseidon.PoseidonConfig; 5import com.legacyminecraft.poseidon.util.CrackedAllowlist; 6import com.legacyminecraft.poseidon.util.ServerLogRotator; 7import com.legacyminecraft.poseidon.watchdog.WatchDogThread; 8import jline.ConsoleReader; 9import joptsimple.OptionSet; 10import org.bukkit.World.Environment; 11import org.bukkit.craftbukkit.CraftServer; 12import org.bukkit.craftbukkit.LoggerOutputStream; 13import org.bukkit.craftbukkit.command.ColouredConsoleSender; 14import org.bukkit.craftbukkit.scheduler.CraftScheduler; 15import org.bukkit.craftbukkit.util.ServerShutdownThread; 16import org.bukkit.event.server.ServerCommandEvent; 17import org.bukkit.event.world.WorldInitEvent; 18import org.bukkit.event.world.WorldLoadEvent; 19import org.bukkit.event.world.WorldSaveEvent; 20import org.bukkit.generator.ChunkGenerator; 21import org.bukkit.plugin.PluginLoadOrder; 22import uk.betacraft.uberbukkit.UberbukkitConfig; 23 24import java.io.File; 25import java.io.IOException; 26import java.io.PrintStream; 27import java.net.InetAddress; 28import java.net.UnknownHostException; 29import java.util.*; 30import java.util.logging.Level; 31import java.util.logging.Logger; 32 33// CraftBukkit start 34//import com.projectposeidon.johnymuffin.UUIDCacheFile; 35// CraftBukkit end 36 37public class MinecraftServer implements Runnable, ICommandListener { 38 39 public static Logger log = Logger.getLogger("Minecraft"); 40 public static HashMap trackerList = new HashMap(); 41 public NetworkListenThread networkListenThread; 42 public PropertyManager propertyManager; 43 // public WorldServer[] worldServer; // CraftBukkit - removed! 44 public ServerConfigurationManager serverConfigurationManager; 45 public ConsoleCommandHandler consoleCommandHandler; // CraftBukkit - made public 46 private boolean isRunning = true; 47 public boolean isStopped = false; 48 int ticks = 0; 49 public String i; 50 public int j; 51 private List r = new ArrayList(); 52 private List s = Collections.synchronizedList(new ArrayList()); 53 // public EntityTracker[] tracker = new EntityTracker[2]; // CraftBukkit - removed! 54 public boolean onlineMode; 55 public boolean spawnAnimals; 56 public boolean pvpMode; 57 public boolean allowFlight; 58 59 // CraftBukkit start 60 public List<WorldServer> worlds = new ArrayList<WorldServer>(); 61 public CraftServer server; 62 public OptionSet options; 63 public ColouredConsoleSender console; 64 public ConsoleReader reader; 65 public static int currentTick; 66 // CraftBukkit end 67 68 //Poseidon Start 69// private WatchDogThread watchDogThread; 70 private boolean modLoaderSupport = false; 71// private PoseidonVersionChecker poseidonVersionChecker; 72 //Poseidon End 73 74 public MinecraftServer(OptionSet options) { // CraftBukkit - adds argument OptionSet 75 new ThreadSleepForever(this); 76 77 // CraftBukkit start 78 this.options = options; 79 try { 80 this.reader = new ConsoleReader(); 81 } catch (IOException ex) { 82 Logger.getLogger(MinecraftServer.class.getName()).log(Level.SEVERE, null, ex); 83 } 84 Runtime.getRuntime().addShutdownHook(new ServerShutdownThread(this)); 85 // CraftBukkit end 86 } 87 88 private boolean init() throws UnknownHostException { // CraftBukkit - added throws UnknownHostException 89 this.consoleCommandHandler = new ConsoleCommandHandler(this); 90 ThreadCommandReader threadcommandreader = new ThreadCommandReader(this); 91 92 threadcommandreader.setDaemon(true); 93 threadcommandreader.start(); 94 ConsoleLogManager.init(this); // CraftBukkit 95 96 // CraftBukkit start 97 System.setOut(new PrintStream(new LoggerOutputStream(log, Level.INFO), true)); 98 System.setErr(new PrintStream(new LoggerOutputStream(log, Level.SEVERE), true)); 99 // CraftBukkit end 100 101 //If Poseidon Config DEBUG is enabled, enable debug mode 102 if (options.has("debug-config")) { 103 log.info("[Poseidon] Configuration debug mode has been enabled. This will cause the poseidon.yml to be reloaded every time the server starts."); 104 PoseidonConfig.getInstance().resetConfig(); 105 } 106 107 modLoaderSupport = PoseidonConfig.getInstance().getBoolean("settings.support.modloader.enable", false); 108 109 if (modLoaderSupport) { 110 log.info("[UberBukkit] ModLoaderMP support is enabled, but has been removed."); 111 } 112 113 log.info("Starting minecraft server... Accepting PVNs: " + String.join(", ", UberbukkitConfig.getInstance().getString("client.allowed_protocols.value", "14").split(","))); 114 if (Runtime.getRuntime().maxMemory() / 1024L / 1024L < 512L) { 115 log.warning("**** NOT ENOUGH RAM!"); 116 log.warning("To start the server with more ram, launch it as \"java -Xmx1024M -Xms1024M -jar minecraft_server.jar\""); 117 } 118 119 log.info("Loading properties"); 120 this.propertyManager = new PropertyManager(this.options); // CraftBukkit - CLI argument support 121 String s = this.propertyManager.getString("server-ip", ""); 122 123 this.onlineMode = this.propertyManager.getBoolean("online-mode", false); //Project Poseidon - False by default 124 this.spawnAnimals = this.propertyManager.getBoolean("spawn-animals", true); 125 this.pvpMode = this.propertyManager.getBoolean("pvp", true); 126 this.allowFlight = this.propertyManager.getBoolean("allow-flight", false); 127 InetAddress inetaddress = null; 128 129 if (s.length() > 0) { 130 inetaddress = InetAddress.getByName(s); 131 } 132 133 int i = this.propertyManager.getInt("server-port", 25565); 134 135 log.info("Starting Minecraft server on " + (s.length() == 0 ? "*" : s) + ":" + i); 136 137 try { 138 this.networkListenThread = new NetworkListenThread(this, inetaddress, i); 139 } catch (Throwable ioexception) { // CraftBukkit - IOException -> Throwable 140 log.warning("**** FAILED TO BIND TO PORT!"); 141 log.log(Level.WARNING, "The exception was: " + ioexception.toString()); 142 log.warning("Perhaps a server is already running on that port?"); 143 return false; 144 } 145 146 if (!this.onlineMode) { 147 log.warning("**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!"); 148 log.warning("The server will make no attempt to authenticate usernames. Beware."); 149 log.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."); 150 log.warning("To change this, set \"online-mode\" to \"true\" in the server.settings file."); 151 } 152 153 this.serverConfigurationManager = new ServerConfigurationManager(this); 154 // CraftBukkit - removed trackers 155 long j = System.nanoTime(); 156 String s1 = this.propertyManager.getString("level-name", "world"); 157 String s2 = this.propertyManager.getString("level-seed", ""); 158 long k = (new Random()).nextLong(); 159 160 if (s2.length() > 0) { 161 try { 162 k = Long.parseLong(s2); 163 } catch (NumberFormatException numberformatexception) { 164 k = (long) s2.hashCode(); 165 } 166 } 167 168 log.info("Preparing level \"" + s1 + "\""); 169 this.a(new WorldLoaderServer(new File(".")), s1, k); 170 171 //Project Poseidon Start 172 Poseidon.getServer().initializeServer(); 173 //Project Poseidon End 174 175 // CraftBukkit start 176 long elapsed = System.nanoTime() - j; 177 String time = String.format("%.3fs", elapsed / 10000000000.0D); 178 log.info("Done (" + time + ")! For help, type \"help\" or \"?\""); 179 180 // log rotator process start. 181 if ((boolean) PoseidonConfig.getInstance().getConfigOption("settings.per-day-log-file.enabled") && (boolean) PoseidonConfig.getInstance().getConfigOption("settings.per-day-log-file.latest-log.enabled")) { 182 String latestLogFileName = "latest"; 183 ServerLogRotator serverLogRotator = new ServerLogRotator(latestLogFileName); 184 serverLogRotator.start(); 185 } 186 187 if (this.propertyManager.properties.containsKey("spawn-protection")) { 188 log.info("'spawn-protection' in server.properties has been moved to 'settings.spawn-radius' in bukkit.yml. I will move your config for you."); 189 this.server.setSpawnRadius(this.propertyManager.getInt("spawn-protection", 16)); 190 this.propertyManager.properties.remove("spawn-protection"); 191 this.propertyManager.savePropertiesFile(); 192 } 193 return true; 194 } 195 196 public boolean isModloaderPresent() { 197 try { 198 Class.forName("net.minecraft.server.ModLoader"); 199 return true; 200 } catch (ClassNotFoundException e) { 201 return false; 202 } 203 } 204 205 private void a(Convertable convertable, String s, long i) { 206 if (convertable.isConvertable(s)) { 207 log.info("Converting map!"); 208 convertable.convert(s, new ConvertProgressUpdater(this)); 209 } 210 211 // CraftBukkit start 212 for (int j = 0; j < (this.propertyManager.getBoolean("allow-nether", true) ? 2 : 1); ++j) { 213 WorldServer world; 214 int dimension = j == 0 ? 0 : -1; 215 String worldType = Environment.getEnvironment(dimension).toString().toLowerCase(); 216 String name = (dimension == 0) ? s : s + "_" + worldType; 217 218 ChunkGenerator gen = this.server.getGenerator(name); 219 220 if (j == 0) { 221 world = new WorldServer(this, new ServerNBTManager(new File("."), s, true), s, dimension, i, org.bukkit.World.Environment.getEnvironment(dimension), gen); // CraftBukkit 222 } else { 223 String dim = "DIM-1"; 224 225 File newWorld = new File(new File(name), dim); 226 File oldWorld = new File(new File(s), dim); 227 228 if ((!newWorld.isDirectory()) && (oldWorld.isDirectory())) { 229 log.info("---- Migration of old " + worldType + " folder required ----"); 230 log.info("Unfortunately due to the way that Minecraft implemented multiworld support in 1.6, Bukkit requires that you move your " + worldType + " folder to a new location in order to operate correctly."); 231 log.info("We will move this folder for you, but it will mean that you need to move it back should you wish to stop using Bukkit in the future."); 232 log.info("Attempting to move " + oldWorld + " to " + newWorld + "..."); 233 234 if (newWorld.exists()) { 235 log.severe("A file or folder already exists at " + newWorld + "!"); 236 log.info("---- Migration of old " + worldType + " folder failed ----"); 237 } else if (newWorld.getParentFile().mkdirs()) { 238 if (oldWorld.renameTo(newWorld)) { 239 log.info("Success! To restore the nether in the future, simply move " + newWorld + " to " + oldWorld); 240 log.info("---- Migration of old " + worldType + " folder complete ----"); 241 } else { 242 log.severe("Could not move folder " + oldWorld + " to " + newWorld + "!"); 243 log.info("---- Migration of old " + worldType + " folder failed ----"); 244 } 245 } else { 246 log.severe("Could not create path for " + newWorld + "!"); 247 log.info("---- Migration of old " + worldType + " folder failed ----"); 248 } 249 } 250 251 world = new SecondaryWorldServer(this, new ServerNBTManager(new File("."), name, true), name, dimension, i, this.worlds.get(0), org.bukkit.World.Environment.getEnvironment(dimension), gen); // CraftBukkit 252 } 253 254 if (gen != null) { 255 world.getWorld().getPopulators().addAll(gen.getDefaultPopulators(world.getWorld())); 256 } 257 258 this.server.getPluginManager().callEvent(new WorldInitEvent(world.getWorld())); 259 260 world.tracker = new EntityTracker(this, dimension); 261 world.addIWorldAccess(new WorldManager(this, world)); 262 world.spawnMonsters = this.propertyManager.getBoolean("spawn-monsters", true) ? 1 : 0; 263 world.setSpawnFlags(this.propertyManager.getBoolean("spawn-monsters", true), this.spawnAnimals); 264 this.worlds.add(world); 265 this.serverConfigurationManager.setPlayerFileData(this.worlds.toArray(new WorldServer[0])); 266 } 267 // CraftBukkit end 268 269 short short1 = 196; 270 long k = System.currentTimeMillis(); 271 272 // CraftBukkit start 273 for (int l = 0; l < this.worlds.size(); ++l) { 274 // if (l == 0 || this.propertyManager.getBoolean("allow-nether", true)) { 275 WorldServer worldserver = this.worlds.get(l); 276 log.info("Preparing start region for level " + l + " (Seed: " + worldserver.getSeed() + ")"); 277 if (worldserver.getWorld().getKeepSpawnInMemory()) { 278 // CraftBukkit end 279 ChunkCoordinates chunkcoordinates = worldserver.getSpawn(); 280 281 for (int i1 = -short1; i1 <= short1 && this.isRunning; i1 += 16) { 282 for (int j1 = -short1; j1 <= short1 && this.isRunning; j1 += 16) { 283 long k1 = System.currentTimeMillis(); 284 285 if (k1 < k) { 286 k = k1; 287 } 288 289 if (k1 > k + 1000L) { 290 int l1 = (short1 * 2 + 1) * (short1 * 2 + 1); 291 int i2 = (i1 + short1) * (short1 * 2 + 1) + j1 + 1; 292 293 this.a("Preparing spawn area", i2 * 100 / l1); 294 k = k1; 295 } 296 297 worldserver.chunkProviderServer.getChunkAt(chunkcoordinates.x + i1 >> 4, chunkcoordinates.z + j1 >> 4); 298 299 while (worldserver.doLighting() && this.isRunning) { 300 ; 301 } 302 } 303 } 304 } // CraftBukkit 305 } 306 307 // CraftBukkit start 308 for (World world : this.worlds) { 309 this.server.getPluginManager().callEvent(new WorldLoadEvent(world.getWorld())); 310 } 311 // CraftBukkit end 312 313 this.e(); 314 } 315 316 private void a(String s, int i) { 317 this.i = s; 318 this.j = i; 319 log.info(s + ": " + i + "%"); 320 } 321 322 private void e() { 323 this.i = null; 324 this.j = 0; 325 326 this.server.enablePlugins(PluginLoadOrder.POSTWORLD); // CraftBukkit 327 } 328 329 void saveChunks() { // CraftBukkit - private -> default 330 log.info("Saving chunks"); 331 332 // CraftBukkit start 333 for (int i = 0; i < this.worlds.size(); ++i) { 334 WorldServer worldserver = this.worlds.get(i); 335 336 worldserver.save(true, (IProgressUpdate) null); 337 worldserver.saveLevel(); 338 339 WorldSaveEvent event = new WorldSaveEvent(worldserver.getWorld()); 340 this.server.getPluginManager().callEvent(event); 341 } 342 343 WorldServer world = this.worlds.get(0); 344 if (!world.canSave) { 345 this.serverConfigurationManager.savePlayers(); 346 } 347 // CraftBukkit end 348 } 349 350 public void stop() { // CraftBukkit - private -> public 351 log.info("Stopping server"); 352 353 //Project Poseidon Start 354 355 // This is done before disablePlugins() to ensure the watchdog doesn't detect plugins disabling as a server hang 356 Poseidon.getServer().shutdownServer(); 357 358 CrackedAllowlist.get().saveAllowlist(); 359 //Project Poseidon End 360 361 // CraftBukkit start 362 if (this.server != null) { 363 this.server.disablePlugins(); 364 } 365 // CraftBukkit end 366 367 if (this.serverConfigurationManager != null) { 368 this.serverConfigurationManager.savePlayers(); 369 } 370 371 // CraftBukkit start - multiworld is handled in saveChunks() already. 372 WorldServer worldserver = this.worlds.get(0); 373 374 if (worldserver != null) { 375 this.saveChunks(); 376 } 377 // CraftBukkit end 378 } 379 380 public void a() { 381 this.isRunning = false; 382 } 383 384 public void run() { 385 try { 386 if (this.init()) { 387 long i = System.currentTimeMillis(); 388 389 for (long j = 0L; this.isRunning; Thread.sleep(1L)) { 390 long k = System.currentTimeMillis(); 391 long l = k - i; 392 393 if (l > 2000L) { 394 log.warning("Can\'t keep up! Did the system time change, or is the server overloaded?"); 395 l = 2000L; 396 } 397 398 if (l < 0L) { 399 log.warning("Time ran backwards! Did the system time change?"); 400 l = 0L; 401 } 402 403 j += l; 404 i = k; 405 if (this.worlds.get(0).everyoneDeeplySleeping()) { // CraftBukkit 406 this.h(); 407 j = 0L; 408 } else { 409 while (j > 50L) { 410 MinecraftServer.currentTick = (int) (System.currentTimeMillis() / 50); // CraftBukkit 411 getWatchdog().tickUpdate(); // Project Poseidon 412 j -= 50L; 413 this.h(); 414 } 415 } 416 } 417 } else { 418 while (this.isRunning) { 419 this.b(); 420 421 try { 422 Thread.sleep(10L); 423 } catch (InterruptedException interruptedexception) { 424 interruptedexception.printStackTrace(); 425 } 426 } 427 } 428 } catch (Throwable throwable) { 429 throwable.printStackTrace(); 430 log.log(Level.SEVERE, "Unexpected exception", throwable); 431 432 while (this.isRunning) { 433 this.b(); 434 435 try { 436 Thread.sleep(10L); 437 } catch (InterruptedException interruptedexception1) { 438 interruptedexception1.printStackTrace(); 439 } 440 } 441 } finally { 442 try { 443 this.stop(); 444 this.isStopped = true; 445 } catch (Throwable throwable1) { 446 throwable1.printStackTrace(); 447 } finally { 448 System.exit(0); 449 } 450 } 451 } 452 453 //Project Poseidon Start - Tick Update 454 private final LinkedList<Double> tpsRecords = new LinkedList<>(); 455 private long lastTick = System.currentTimeMillis(); 456 private int tickCount = 0; 457 458 public LinkedList<Double> getTpsRecords() { 459 return tpsRecords; 460 } 461 //Project Poseidon End - Tick Update 462 463 private void h() { 464 ArrayList arraylist = new ArrayList(); 465 Iterator iterator = trackerList.keySet().iterator(); 466 467 while (iterator.hasNext()) { 468 String s = (String) iterator.next(); 469 int i = ((Integer) trackerList.get(s)).intValue(); 470 471 if (i > 0) { 472 trackerList.put(s, Integer.valueOf(i - 1)); 473 } else { 474 arraylist.add(s); 475 } 476 } 477 478 int j; 479 480 for (j = 0; j < arraylist.size(); ++j) { 481 trackerList.remove(arraylist.get(j)); 482 } 483 484 AxisAlignedBB.a(); 485 Vec3D.a(); 486 ++this.ticks; 487 488 ((CraftScheduler) this.server.getScheduler()).mainThreadHeartbeat(this.ticks); // CraftBukkit 489 490 //Project Poseidon Start - Tick Update 491 long currentTime = System.currentTimeMillis(); 492 tickCount++; 493 494 //Check if a second has passed 495 if (currentTime - lastTick >= 1000) { 496 double tps = tickCount / ((currentTime - lastTick) / 1000.0); 497 tpsRecords.addFirst(tps); 498 if (tpsRecords.size() > 900) { //Don't keep more than 15 minutes of data 499 tpsRecords.removeLast(); 500 } 501 502 tickCount = 0; 503 lastTick = currentTime; 504 } 505 506 //Project Poseidon End - Tick Update 507 508 509 for (j = 0; j < this.worlds.size(); ++j) { // CraftBukkit 510 // if (j == 0 || this.propertyManager.getBoolean("allow-nether", true)) { // CraftBukkit 511 WorldServer worldserver = this.worlds.get(j); // CraftBukkit 512 513 if (this.ticks % 20 == 0) { 514 // CraftBukkit start - only send timeupdates to the people in that world 515 for (int i = 0; i < worldserver.players.size(); ++i) { // Project Poseidon: serverConfigurationManager -> worldserver.players 516 EntityPlayer entityPlayer = (EntityPlayer) worldserver.players.get(i); 517 if (entityPlayer != null) { 518 entityPlayer.netServerHandler.sendPacket(new Packet4UpdateTime(entityPlayer.getPlayerTime())); // Add support for per player time 519 520 } 521 } 522 // CraftBukkit end 523 } 524 525 worldserver.doTick(); 526 527 while (worldserver.doLighting()) { 528 ; 529 } 530 531 worldserver.cleanUp(); 532 } 533 // } // CraftBukkit 534 535 this.networkListenThread.a(); 536 this.serverConfigurationManager.b(); 537 538 // CraftBukkit start 539 for (j = 0; j < this.worlds.size(); ++j) { 540 this.worlds.get(j).tracker.updatePlayers(); 541 } 542 // CraftBukkit end 543 544 for (j = 0; j < this.r.size(); ++j) { 545 ((IUpdatePlayerListBox) this.r.get(j)).a(); 546 } 547 548 try { 549 this.b(); 550 } catch (Exception exception) { 551 log.log(Level.WARNING, "Unexpected exception while parsing console command", exception); 552 } 553 } 554 555 public void issueCommand(String s, ICommandListener icommandlistener) { 556 this.s.add(new ServerCommand(s, icommandlistener)); 557 } 558 559 public void b() { 560 while (this.s.size() > 0) { 561 ServerCommand servercommand = (ServerCommand) this.s.remove(0); 562 563 // CraftBukkit start - ServerCommand for preprocessing 564 ServerCommandEvent event = new ServerCommandEvent(this.console, servercommand.command); 565 this.server.getPluginManager().callEvent(event); 566 servercommand = new ServerCommand(event.getCommand(), servercommand.b); 567 // CraftBukkit end 568 569 // this.consoleCommandHandler.handle(servercommand); // CraftBukkit - Removed its now called in server.dispatchCommand 570 this.server.dispatchCommand(this.console, servercommand); // CraftBukkit 571 } 572 } 573 574 public void a(IUpdatePlayerListBox iupdateplayerlistbox) { 575 this.r.add(iupdateplayerlistbox); 576 } 577 578 public static void main(final OptionSet options) { // CraftBukkit - replaces main(String args[]) 579 StatisticList.a(); 580 581 try { 582 MinecraftServer minecraftserver = new MinecraftServer(options); // CraftBukkit - pass in the options 583 584 // CraftBukkit - remove gui 585 586 (new ThreadServerApplication("Server thread", minecraftserver)).start(); 587 } catch (Exception exception) { 588 log.log(Level.SEVERE, "Failed to start the minecraft server", exception); 589 } 590 } 591 592 public File a(String s) { 593 return new File(s); 594 } 595 596 public void sendMessage(String s) { 597 log.info(s); 598 } 599 600 public void c(String s) { 601 log.warning(s); 602 } 603 604 public String getName() { 605 return "CONSOLE"; 606 } 607 608 public WorldServer getWorldServer(int i) { 609 // CraftBukkit start 610 for (WorldServer world : this.worlds) { 611 if (world.dimension == i) { 612 return world; 613 } 614 } 615 616 return this.worlds.get(0); 617 // CraftBukkit end 618 } 619 620 public EntityTracker getTracker(int i) { 621 return this.getWorldServer(i).tracker; // CraftBukkit 622 } 623 624 public static boolean isRunning(MinecraftServer minecraftserver) { 625 return minecraftserver.isRunning; 626 } 627 628 public WatchDogThread getWatchdog() { 629 return Poseidon.getServer().getWatchDogThread(); 630 } 631}