001/* 002 * PlotSquared, a land and world management plugin for Minecraft. 003 * Copyright (C) IntellectualSites <https://intellectualsites.com> 004 * Copyright (C) IntellectualSites team and contributors 005 * 006 * This program is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with this program. If not, see <https://www.gnu.org/licenses/>. 018 */ 019package com.plotsquared.bukkit; 020 021import com.google.inject.Guice; 022import com.google.inject.Inject; 023import com.google.inject.Injector; 024import com.google.inject.Key; 025import com.google.inject.Singleton; 026import com.google.inject.Stage; 027import com.plotsquared.bukkit.generator.BukkitPlotGenerator; 028import com.plotsquared.bukkit.inject.BackupModule; 029import com.plotsquared.bukkit.inject.BukkitModule; 030import com.plotsquared.bukkit.inject.PermissionModule; 031import com.plotsquared.bukkit.inject.WorldManagerModule; 032import com.plotsquared.bukkit.listener.BlockEventListener; 033import com.plotsquared.bukkit.listener.BlockEventListener117; 034import com.plotsquared.bukkit.listener.ChunkListener; 035import com.plotsquared.bukkit.listener.EntityEventListener; 036import com.plotsquared.bukkit.listener.EntitySpawnListener; 037import com.plotsquared.bukkit.listener.HighFreqBlockEventListener; 038import com.plotsquared.bukkit.listener.PaperListener; 039import com.plotsquared.bukkit.listener.PlayerEventListener; 040import com.plotsquared.bukkit.listener.PlayerEventListener1201; 041import com.plotsquared.bukkit.listener.ProjectileEventListener; 042import com.plotsquared.bukkit.listener.ServerListener; 043import com.plotsquared.bukkit.listener.SingleWorldListener; 044import com.plotsquared.bukkit.listener.SpigotListener; 045import com.plotsquared.bukkit.listener.WorldEvents; 046import com.plotsquared.bukkit.placeholder.PAPIPlaceholders; 047import com.plotsquared.bukkit.placeholder.PlaceholderFormatter; 048import com.plotsquared.bukkit.player.BukkitPlayerManager; 049import com.plotsquared.bukkit.util.BukkitUtil; 050import com.plotsquared.bukkit.util.BukkitWorld; 051import com.plotsquared.bukkit.util.SetGenCB; 052import com.plotsquared.bukkit.util.TranslationUpdateManager; 053import com.plotsquared.bukkit.util.UpdateUtility; 054import com.plotsquared.bukkit.util.task.BukkitTaskManager; 055import com.plotsquared.bukkit.util.task.PaperTimeConverter; 056import com.plotsquared.bukkit.util.task.SpigotTimeConverter; 057import com.plotsquared.bukkit.uuid.EssentialsUUIDService; 058import com.plotsquared.bukkit.uuid.LuckPermsUUIDService; 059import com.plotsquared.bukkit.uuid.OfflinePlayerUUIDService; 060import com.plotsquared.bukkit.uuid.PaperUUIDService; 061import com.plotsquared.bukkit.uuid.SQLiteUUIDService; 062import com.plotsquared.bukkit.uuid.SquirrelIdUUIDService; 063import com.plotsquared.core.PlotPlatform; 064import com.plotsquared.core.PlotSquared; 065import com.plotsquared.core.backup.BackupManager; 066import com.plotsquared.core.components.ComponentPresetManager; 067import com.plotsquared.core.configuration.ConfigurationNode; 068import com.plotsquared.core.configuration.ConfigurationSection; 069import com.plotsquared.core.configuration.ConfigurationUtil; 070import com.plotsquared.core.configuration.Settings; 071import com.plotsquared.core.configuration.Storage; 072import com.plotsquared.core.configuration.caption.ChatFormatter; 073import com.plotsquared.core.configuration.file.YamlConfiguration; 074import com.plotsquared.core.database.DBFunc; 075import com.plotsquared.core.events.RemoveRoadEntityEvent; 076import com.plotsquared.core.events.Result; 077import com.plotsquared.core.generator.GeneratorWrapper; 078import com.plotsquared.core.generator.IndependentPlotGenerator; 079import com.plotsquared.core.generator.SingleWorldGenerator; 080import com.plotsquared.core.inject.annotations.BackgroundPipeline; 081import com.plotsquared.core.inject.annotations.DefaultGenerator; 082import com.plotsquared.core.inject.annotations.ImpromptuPipeline; 083import com.plotsquared.core.inject.annotations.WorldConfig; 084import com.plotsquared.core.inject.annotations.WorldFile; 085import com.plotsquared.core.inject.modules.PlotSquaredModule; 086import com.plotsquared.core.listener.PlotListener; 087import com.plotsquared.core.listener.WESubscriber; 088import com.plotsquared.core.player.PlotPlayer; 089import com.plotsquared.core.plot.Plot; 090import com.plotsquared.core.plot.PlotArea; 091import com.plotsquared.core.plot.PlotAreaTerrainType; 092import com.plotsquared.core.plot.PlotAreaType; 093import com.plotsquared.core.plot.PlotId; 094import com.plotsquared.core.plot.comment.CommentManager; 095import com.plotsquared.core.plot.flag.implementations.ServerPlotFlag; 096import com.plotsquared.core.plot.world.PlotAreaManager; 097import com.plotsquared.core.plot.world.SinglePlotArea; 098import com.plotsquared.core.plot.world.SinglePlotAreaManager; 099import com.plotsquared.core.setup.PlotAreaBuilder; 100import com.plotsquared.core.setup.SettingsNodesWrapper; 101import com.plotsquared.core.util.EventDispatcher; 102import com.plotsquared.core.util.FileUtils; 103import com.plotsquared.core.util.PlatformWorldManager; 104import com.plotsquared.core.util.PlayerManager; 105import com.plotsquared.core.util.PremiumVerification; 106import com.plotsquared.core.util.ReflectionUtils; 107import com.plotsquared.core.util.SetupUtils; 108import com.plotsquared.core.util.WorldUtil; 109import com.plotsquared.core.util.task.TaskManager; 110import com.plotsquared.core.util.task.TaskTime; 111import com.plotsquared.core.uuid.CacheUUIDService; 112import com.plotsquared.core.uuid.UUIDPipeline; 113import com.plotsquared.core.uuid.offline.OfflineModeUUIDService; 114import com.sk89q.worldedit.WorldEdit; 115import com.sk89q.worldedit.bukkit.BukkitAdapter; 116import io.papermc.lib.PaperLib; 117import net.kyori.adventure.audience.Audience; 118import net.kyori.adventure.text.Component; 119import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; 120import org.apache.logging.log4j.LogManager; 121import org.apache.logging.log4j.Logger; 122import org.bstats.bukkit.Metrics; 123import org.bstats.charts.DrilldownPie; 124import org.bstats.charts.SimplePie; 125import org.bukkit.Bukkit; 126import org.bukkit.Chunk; 127import org.bukkit.Location; 128import org.bukkit.World; 129import org.bukkit.command.PluginCommand; 130import org.bukkit.entity.Entity; 131import org.bukkit.entity.LivingEntity; 132import org.bukkit.entity.Player; 133import org.bukkit.event.Listener; 134import org.bukkit.generator.ChunkGenerator; 135import org.bukkit.metadata.FixedMetadataValue; 136import org.bukkit.metadata.MetadataValue; 137import org.bukkit.plugin.Plugin; 138import org.bukkit.plugin.RegisteredServiceProvider; 139import org.bukkit.plugin.java.JavaPlugin; 140import org.checkerframework.checker.nullness.qual.NonNull; 141import org.checkerframework.checker.nullness.qual.Nullable; 142import org.incendo.serverlib.ServerLib; 143 144import java.io.File; 145import java.io.IOException; 146import java.lang.reflect.Method; 147import java.util.ArrayList; 148import java.util.Arrays; 149import java.util.Collections; 150import java.util.Comparator; 151import java.util.HashMap; 152import java.util.HashSet; 153import java.util.Iterator; 154import java.util.List; 155import java.util.Locale; 156import java.util.Map; 157import java.util.Queue; 158import java.util.Set; 159import java.util.UUID; 160import java.util.concurrent.ExecutionException; 161import java.util.concurrent.Executors; 162import java.util.concurrent.LinkedBlockingQueue; 163import java.util.concurrent.TimeUnit; 164 165import static com.plotsquared.core.util.PremiumVerification.getDownloadID; 166import static com.plotsquared.core.util.PremiumVerification.getResourceID; 167import static com.plotsquared.core.util.PremiumVerification.getUserID; 168import static com.plotsquared.core.util.ReflectionUtils.getRefClass; 169 170@SuppressWarnings("unused") 171@Singleton 172public final class BukkitPlatform extends JavaPlugin implements Listener, PlotPlatform<Player> { 173 174 private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + BukkitPlatform.class.getSimpleName()); 175 private static final int BSTATS_ID = 1404; 176 177 static { 178 try { 179 Settings.load(new File(PlotSquared.platform().getDirectory(), "settings.yml")); 180 } catch (Throwable ignored) { 181 } 182 } 183 184 private int[] version; 185 private String pluginName; 186 private SingleWorldListener singleWorldListener; 187 private Method methodUnloadChunk0; 188 private boolean methodUnloadSetup = false; 189 private boolean metricsStarted; 190 private boolean faweHook = false; 191 192 private Injector injector; 193 194 @Inject 195 private PlotAreaManager plotAreaManager; 196 @Inject 197 private EventDispatcher eventDispatcher; 198 @Inject 199 private PlotListener plotListener; 200 @Inject 201 @WorldConfig 202 private YamlConfiguration worldConfiguration; 203 @Inject 204 @WorldFile 205 private File worldfile; 206 @Inject 207 private BukkitPlayerManager playerManager; 208 @Inject 209 private BackupManager backupManager; 210 @Inject 211 @ImpromptuPipeline 212 private UUIDPipeline impromptuPipeline; 213 @Inject 214 @BackgroundPipeline 215 private UUIDPipeline backgroundPipeline; 216 @Inject 217 private PlatformWorldManager<World> worldManager; 218 private Locale serverLocale; 219 220 @SuppressWarnings("StringSplitter") 221 @Override 222 public int @NonNull [] serverVersion() { 223 if (this.version == null) { 224 try { 225 this.version = new int[3]; 226 String[] split = Bukkit.getBukkitVersion().split("-")[0].split("\\."); 227 this.version[0] = Integer.parseInt(split[0]); 228 this.version[1] = Integer.parseInt(split[1]); 229 if (split.length == 3) { 230 this.version[2] = Integer.parseInt(split[2]); 231 } 232 } catch (NumberFormatException e) { 233 e.printStackTrace(); 234 return new int[]{1, 13, 0}; 235 } 236 } 237 return this.version; 238 } 239 240 @Override 241 public int versionMinHeight() { 242 return serverVersion()[1] >= 18 ? -64 : 0; 243 } 244 245 @Override 246 public int versionMaxHeight() { 247 return serverVersion()[1] >= 18 ? 319 : 255; 248 } 249 250 @Override 251 public @NonNull String serverImplementation() { 252 return Bukkit.getVersion(); 253 } 254 255 @Override 256 @SuppressWarnings("deprecation") // Paper deprecation 257 public void onEnable() { 258 this.pluginName = getDescription().getName(); 259 260 final TaskTime.TimeConverter timeConverter; 261 if (PaperLib.isPaper()) { 262 timeConverter = new PaperTimeConverter(); 263 } else { 264 timeConverter = new SpigotTimeConverter(); 265 } 266 267 // Stuff that needs to be created before the PlotSquared instance 268 PlotPlayer.registerConverter(Player.class, BukkitUtil::adapt); 269 TaskManager.setPlatformImplementation(new BukkitTaskManager(this, timeConverter)); 270 271 final PlotSquared plotSquared = new PlotSquared(this, "Bukkit"); 272 273 // FastAsyncWorldEdit 274 if (Settings.FAWE_Components.FAWE_HOOK) { 275 Plugin fawe = getServer().getPluginManager().getPlugin("FastAsyncWorldEdit"); 276 if (fawe != null) { 277 try { 278 Class.forName("com.fastasyncworldedit.bukkit.regions.plotsquared.FaweQueueCoordinator"); 279 faweHook = true; 280 } catch (Exception ignored) { 281 LOGGER.error("Incompatible version of FastAsyncWorldEdit to enable hook, please upgrade: https://ci.athion" + 282 ".net/job/FastAsyncWorldEdit/"); 283 } 284 } 285 } 286 287 // We create the injector after PlotSquared has been initialized, so that we have access 288 // to generated instances and settings 289 this.injector = Guice 290 .createInjector( 291 Stage.PRODUCTION, 292 new PermissionModule(), 293 new WorldManagerModule(), 294 new PlotSquaredModule(), 295 new BukkitModule(this), 296 new BackupModule() 297 ); 298 this.injector.injectMembers(this); 299 300 try { 301 this.injector.getInstance(TranslationUpdateManager.class).upgradeTranslationFile(); 302 } catch (IOException e) { 303 throw new RuntimeException(e); 304 } 305 306 this.serverLocale = Locale.forLanguageTag(Settings.Enabled_Components.DEFAULT_LOCALE); 307 308 if (PremiumVerification.isPremium() && Settings.Enabled_Components.UPDATE_NOTIFICATIONS) { 309 injector.getInstance(UpdateUtility.class).updateChecker(); 310 } 311 312 if (PremiumVerification.isPremium()) { 313 LOGGER.info("PlotSquared version licensed to Spigot user {}", getUserID()); 314 LOGGER.info("https://www.spigotmc.org/resources/{}", getResourceID()); 315 LOGGER.info("Download ID: {}", getDownloadID()); 316 LOGGER.info("Thanks for supporting us :)"); 317 } else { 318 LOGGER.info("Couldn't verify purchase :("); 319 } 320 321 // Database 322 if (Settings.Enabled_Components.DATABASE) { 323 plotSquared.setupDatabase(); 324 } 325 326 // Check if we need to convert old flag values, etc 327 if (!plotSquared.getConfigurationVersion().equalsIgnoreCase("v5")) { 328 // Perform upgrade 329 if (DBFunc.dbManager.convertFlags()) { 330 LOGGER.info("Flags were converted successfully!"); 331 // Update the config version 332 try { 333 plotSquared.setConfigurationVersion("v5"); 334 } catch (final Exception e) { 335 e.printStackTrace(); 336 } 337 } 338 } 339 340 // Comments 341 CommentManager.registerDefaultInboxes(); 342 343 // Do stuff that was previously done in PlotSquared 344 // Kill entities 345 if (Settings.Enabled_Components.KILL_ROAD_MOBS || Settings.Enabled_Components.KILL_ROAD_VEHICLES) { 346 this.runEntityTask(); 347 } 348 349 // WorldEdit 350 if (Settings.Enabled_Components.WORLDEDIT_RESTRICTIONS) { 351 try { 352 WorldEdit.getInstance().getEventBus().register(this.injector().getInstance(WESubscriber.class)); 353 LOGGER.info("{} hooked into WorldEdit", this.pluginName()); 354 } catch (Throwable e) { 355 LOGGER.error( 356 "Incompatible version of WorldEdit, please upgrade: https://builds.enginehub.org/job/worldedit?branch=master"); 357 } 358 } 359 360 if (Settings.Enabled_Components.EVENTS) { 361 getServer().getPluginManager().registerEvents(injector().getInstance(PlayerEventListener.class), this); 362 if ((serverVersion()[1] == 20 && serverVersion()[2] >= 1) || serverVersion()[1] > 20) { 363 getServer().getPluginManager().registerEvents(injector().getInstance(PlayerEventListener1201.class), this); 364 } 365 getServer().getPluginManager().registerEvents(injector().getInstance(BlockEventListener.class), this); 366 if (Settings.HIGH_FREQUENCY_LISTENER) { 367 getServer().getPluginManager().registerEvents(injector().getInstance(HighFreqBlockEventListener.class), this); 368 } 369 if (serverVersion()[1] >= 17) { 370 getServer().getPluginManager().registerEvents(injector().getInstance(BlockEventListener117.class), this); 371 } 372 getServer().getPluginManager().registerEvents(injector().getInstance(EntityEventListener.class), this); 373 getServer().getPluginManager().registerEvents(injector().getInstance(ProjectileEventListener.class), this); 374 getServer().getPluginManager().registerEvents(injector().getInstance(ServerListener.class), this); 375 getServer().getPluginManager().registerEvents(injector().getInstance(EntitySpawnListener.class), this); 376 if (PaperLib.isPaper() && Settings.Paper_Components.PAPER_LISTENERS) { 377 getServer().getPluginManager().registerEvents(injector().getInstance(PaperListener.class), this); 378 } else { 379 getServer().getPluginManager().registerEvents(injector().getInstance(SpigotListener.class), this); 380 } 381 this.plotListener.startRunnable(); 382 } 383 384 // Required 385 getServer().getPluginManager().registerEvents(injector().getInstance(WorldEvents.class), this); 386 if (Settings.Enabled_Components.CHUNK_PROCESSOR) { 387 getServer().getPluginManager().registerEvents(injector().getInstance(ChunkListener.class), this); 388 } 389 390 // Commands 391 if (Settings.Enabled_Components.COMMANDS) { 392 this.registerCommands(); 393 } 394 395 // Permissions 396 this.permissionHandler().initialize(); 397 398 if (Settings.Enabled_Components.COMPONENT_PRESETS) { 399 try { 400 injector().getInstance(ComponentPresetManager.class); 401 } catch (final Exception e) { 402 LOGGER.error("Failed to initialize the preset system", e); 403 } 404 } 405 406 // World generators: 407 final ConfigurationSection section = this.worldConfiguration.getConfigurationSection("worlds"); 408 final WorldUtil worldUtil = injector().getInstance(WorldUtil.class); 409 410 if (section != null) { 411 for (String world : section.getKeys(false)) { 412 if (world.equals("CheckingPlotSquaredGenerator")) { 413 continue; 414 } 415 if (worldUtil.isWorld(world)) { 416 this.setGenerator(world); 417 } 418 } 419 TaskManager.runTaskLater(() -> { 420 for (String world : section.getKeys(false)) { 421 if (world.equals("CheckingPlotSquaredGenerator")) { 422 continue; 423 } 424 if (!worldUtil.isWorld(world) && !world.equals("*")) { 425 if (Settings.DEBUG) { 426 LOGGER.warn( 427 "`{}` was not properly loaded - {} will now try to load it properly", 428 world, 429 this.pluginName() 430 ); 431 LOGGER.warn( 432 "- Are you trying to delete this world? Remember to remove it from the worlds.yml, bukkit.yml and multiverse worlds.yml"); 433 LOGGER.warn("- Your world management plugin may be faulty (or non existent)"); 434 LOGGER.warn("- The named world is not a plot world"); 435 LOGGER.warn("This message may also be a false positive and could be ignored."); 436 } 437 this.setGenerator(world); 438 } 439 } 440 }, TaskTime.ticks(1L)); 441 } 442 443 plotSquared.startExpiryTasks(); 444 445 // Once the server has loaded force updating all generators known to PlotSquared 446 TaskManager.runTaskLater(() -> PlotSquared.platform().setupUtils().updateGenerators(true), TaskTime.ticks(1L)); 447 448 // Services are accessed in order 449 final CacheUUIDService cacheUUIDService = new CacheUUIDService(Settings.UUID.UUID_CACHE_SIZE); 450 this.impromptuPipeline.registerService(cacheUUIDService); 451 this.backgroundPipeline.registerService(cacheUUIDService); 452 this.impromptuPipeline.registerConsumer(cacheUUIDService); 453 this.backgroundPipeline.registerConsumer(cacheUUIDService); 454 455 // Now, if the server is in offline mode we can only use profiles and direct UUID 456 // access, and so we skip the player profile stuff as well as SquirrelID (Mojang lookups) 457 if (Settings.UUID.OFFLINE) { 458 final OfflineModeUUIDService offlineModeUUIDService = new OfflineModeUUIDService(); 459 this.impromptuPipeline.registerService(offlineModeUUIDService); 460 this.backgroundPipeline.registerService(offlineModeUUIDService); 461 LOGGER.info("(UUID) Using the offline mode UUID service"); 462 } 463 464 if (Settings.UUID.SERVICE_BUKKIT) { 465 final OfflinePlayerUUIDService offlinePlayerUUIDService = new OfflinePlayerUUIDService(); 466 this.impromptuPipeline.registerService(offlinePlayerUUIDService); 467 this.backgroundPipeline.registerService(offlinePlayerUUIDService); 468 } 469 470 final SQLiteUUIDService sqLiteUUIDService = new SQLiteUUIDService("user_cache.db"); 471 472 final SQLiteUUIDService legacyUUIDService; 473 if (Settings.UUID.LEGACY_DATABASE_SUPPORT && FileUtils 474 .getFile(PlotSquared.platform().getDirectory(), "usercache.db") 475 .exists()) { 476 legacyUUIDService = new SQLiteUUIDService("usercache.db"); 477 } else { 478 legacyUUIDService = null; 479 } 480 481 final LuckPermsUUIDService luckPermsUUIDService; 482 if (Settings.UUID.SERVICE_LUCKPERMS && Bukkit.getPluginManager().getPlugin("LuckPerms") != null) { 483 luckPermsUUIDService = new LuckPermsUUIDService(); 484 LOGGER.info("(UUID) Using LuckPerms as a complementary UUID service"); 485 } else { 486 luckPermsUUIDService = null; 487 } 488 489 final EssentialsUUIDService essentialsUUIDService; 490 if (Settings.UUID.SERVICE_ESSENTIALSX && Bukkit.getPluginManager().getPlugin("Essentials") != null) { 491 essentialsUUIDService = new EssentialsUUIDService(); 492 LOGGER.info("(UUID) Using EssentialsX as a complementary UUID service"); 493 } else { 494 essentialsUUIDService = null; 495 } 496 497 if (!Settings.UUID.OFFLINE) { 498 // If running Paper we'll also try to use their profiles 499 if (Bukkit.getOnlineMode() && PaperLib.isPaper() && Settings.UUID.SERVICE_PAPER) { 500 final PaperUUIDService paperUUIDService = new PaperUUIDService(); 501 this.impromptuPipeline.registerService(paperUUIDService); 502 this.backgroundPipeline.registerService(paperUUIDService); 503 LOGGER.info("(UUID) Using Paper as a complementary UUID service"); 504 } 505 506 this.impromptuPipeline.registerService(sqLiteUUIDService); 507 this.backgroundPipeline.registerService(sqLiteUUIDService); 508 this.impromptuPipeline.registerConsumer(sqLiteUUIDService); 509 this.backgroundPipeline.registerConsumer(sqLiteUUIDService); 510 511 if (legacyUUIDService != null) { 512 this.impromptuPipeline.registerService(legacyUUIDService); 513 this.backgroundPipeline.registerService(legacyUUIDService); 514 } 515 516 // Plugin providers 517 if (luckPermsUUIDService != null) { 518 this.impromptuPipeline.registerService(luckPermsUUIDService); 519 this.backgroundPipeline.registerService(luckPermsUUIDService); 520 } 521 if (essentialsUUIDService != null) { 522 this.impromptuPipeline.registerService(essentialsUUIDService); 523 this.backgroundPipeline.registerService(essentialsUUIDService); 524 } 525 526 if (Settings.UUID.IMPROMPTU_SERVICE_MOJANG_API) { 527 final SquirrelIdUUIDService impromptuMojangService = new SquirrelIdUUIDService(Settings.UUID.IMPROMPTU_LIMIT); 528 this.impromptuPipeline.registerService(impromptuMojangService); 529 } 530 final SquirrelIdUUIDService backgroundMojangService = new SquirrelIdUUIDService(Settings.UUID.BACKGROUND_LIMIT); 531 this.backgroundPipeline.registerService(backgroundMojangService); 532 } else { 533 this.impromptuPipeline.registerService(sqLiteUUIDService); 534 this.backgroundPipeline.registerService(sqLiteUUIDService); 535 this.impromptuPipeline.registerConsumer(sqLiteUUIDService); 536 this.backgroundPipeline.registerConsumer(sqLiteUUIDService); 537 538 if (legacyUUIDService != null) { 539 this.impromptuPipeline.registerService(legacyUUIDService); 540 this.backgroundPipeline.registerService(legacyUUIDService); 541 } 542 } 543 544 this.impromptuPipeline.storeImmediately("*", DBFunc.EVERYONE); 545 546 if (Settings.UUID.BACKGROUND_CACHING_ENABLED) { 547 this.startUuidCaching(sqLiteUUIDService, cacheUUIDService); 548 } 549 550 if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { 551 injector.getInstance(PAPIPlaceholders.class).register(); 552 if (Settings.Enabled_Components.EXTERNAL_PLACEHOLDERS) { 553 ChatFormatter.formatters.add(injector().getInstance(PlaceholderFormatter.class)); 554 } 555 LOGGER.info("PlotSquared hooked into PlaceholderAPI"); 556 } 557 558 this.startMetrics(); 559 560 if (Settings.Enabled_Components.WORLDS) { 561 TaskManager.getPlatformImplementation().taskRepeat(this::unload, TaskTime.seconds(10L)); 562 try { 563 singleWorldListener = injector().getInstance(SingleWorldListener.class); 564 Bukkit.getPluginManager().registerEvents(singleWorldListener, this); 565 } catch (Exception e) { 566 e.printStackTrace(); 567 } 568 } 569 570 // Clean up potential memory leak 571 Bukkit.getScheduler().runTaskTimer(this, () -> { 572 try { 573 for (final PlotPlayer<? extends Player> player : this.playerManager().getPlayers()) { 574 if (player.getPlatformPlayer() == null || !player.getPlatformPlayer().isOnline()) { 575 this.playerManager().removePlayer(player); 576 } 577 } 578 } catch (final Exception e) { 579 getLogger().warning("Failed to clean up players: " + e.getMessage()); 580 } 581 }, 100L, 100L); 582 583 // Check if we are in a safe environment 584 ServerLib.checkUnsafeForks(); 585 } 586 587 private void unload() { 588 if (!this.methodUnloadSetup) { 589 this.methodUnloadSetup = true; 590 try { 591 ReflectionUtils.RefClass classCraftWorld = getRefClass("{cb}.CraftWorld"); 592 this.methodUnloadChunk0 = classCraftWorld.getRealClass().getDeclaredMethod( 593 "unloadChunk0", 594 int.class, 595 int.class, 596 boolean.class 597 ); 598 this.methodUnloadChunk0.setAccessible(true); 599 } catch (Throwable event) { 600 event.printStackTrace(); 601 } 602 } 603 604 if (this.plotAreaManager instanceof SinglePlotAreaManager) { 605 long start = System.currentTimeMillis(); 606 final SinglePlotArea area = ((SinglePlotAreaManager) this.plotAreaManager).getArea(); 607 608 outer: 609 for (final World world : Bukkit.getWorlds()) { 610 final String name = world.getName(); 611 final char char0 = name.charAt(0); 612 if (!Character.isDigit(char0) && char0 != '-') { 613 continue; 614 } 615 616 if (!world.getPlayers().isEmpty()) { 617 continue; 618 } 619 620 PlotId id; 621 try { 622 id = PlotId.fromString(name); 623 } catch (IllegalArgumentException ignored) { 624 continue; 625 } 626 final Plot plot = area.getOwnedPlot(id); 627 if (plot != null) { 628 if (!plot.getFlag(ServerPlotFlag.class) || PlotSquared 629 .platform() 630 .playerManager() 631 .getPlayerIfExists(plot.getOwner()) == null) { 632 if (world.getKeepSpawnInMemory()) { 633 world.setKeepSpawnInMemory(false); 634 return; 635 } 636 final Chunk[] chunks = world.getLoadedChunks(); 637 if (chunks.length == 0) { 638 if (!Bukkit.unloadWorld(world, true)) { 639 LOGGER.warn("Failed to unload {}", world.getName()); 640 } 641 return; 642 } else { 643 int index = 0; 644 do { 645 final Chunk chunkI = chunks[index++]; 646 boolean result; 647 if (methodUnloadChunk0 != null) { 648 try { 649 result = (boolean) methodUnloadChunk0.invoke(world, chunkI.getX(), chunkI.getZ(), true); 650 } catch (Throwable e) { 651 methodUnloadChunk0 = null; 652 e.printStackTrace(); 653 continue outer; 654 } 655 } else { 656 result = world.unloadChunk(chunkI.getX(), chunkI.getZ(), true); 657 } 658 if (!result) { 659 continue outer; 660 } 661 if (System.currentTimeMillis() - start > 5) { 662 return; 663 } 664 } while (index < chunks.length); 665 } 666 } 667 } 668 } 669 } 670 } 671 672 private void startUuidCaching( 673 final @NonNull SQLiteUUIDService sqLiteUUIDService, 674 final @NonNull CacheUUIDService cacheUUIDService 675 ) { 676 // Record all unique UUID's and put them into a queue 677 final Set<UUID> uuidSet = new HashSet<>(); 678 PlotSquared.get().forEachPlotRaw(plot -> { 679 uuidSet.add(plot.getOwnerAbs()); 680 uuidSet.addAll(plot.getMembers()); 681 uuidSet.addAll(plot.getTrusted()); 682 uuidSet.addAll(plot.getDenied()); 683 }); 684 final Queue<UUID> uuidQueue = new LinkedBlockingQueue<>(uuidSet); 685 686 LOGGER.info("(UUID) {} UUIDs will be cached", uuidQueue.size()); 687 688 Executors.newSingleThreadScheduledExecutor().schedule(() -> { 689 // Begin by reading all the SQLite cache at once 690 cacheUUIDService.accept(sqLiteUUIDService.getAll()); 691 // Now fetch names for all known UUIDs 692 final int totalSize = uuidQueue.size(); 693 int read = 0; 694 LOGGER.info("(UUID) PlotSquared will fetch UUIDs in groups of {}", Settings.UUID.BACKGROUND_LIMIT); 695 final List<UUID> uuidList = new ArrayList<>(Settings.UUID.BACKGROUND_LIMIT); 696 697 // Used to indicate that the second retrieval has been attempted 698 boolean secondRun = false; 699 700 while (!uuidQueue.isEmpty() || !uuidList.isEmpty()) { 701 if (!uuidList.isEmpty() && secondRun) { 702 LOGGER.warn("(UUID) Giving up on last batch. Fetching new batch instead"); 703 uuidList.clear(); 704 } 705 if (uuidList.isEmpty()) { 706 // Retrieve the secondRun variable to indicate that we're retrieving a 707 // fresh batch 708 secondRun = false; 709 // Populate the request list 710 for (int i = 0; i < Settings.UUID.BACKGROUND_LIMIT && !uuidQueue.isEmpty(); i++) { 711 uuidList.add(uuidQueue.poll()); 712 read++; 713 } 714 } else { 715 // If the list isn't empty then this is a second run for 716 // an old batch, so we re-use the patch 717 secondRun = true; 718 } 719 try { 720 PlotSquared.get().getBackgroundUUIDPipeline().getNames(uuidList).get(); 721 // Clear the list if we successfully index all the names 722 uuidList.clear(); 723 // Print progress 724 final double percentage = ((double) read / (double) totalSize) * 100.0D; 725 if (Settings.DEBUG) { 726 LOGGER.info("(UUID) PlotSquared has cached {} of UUIDs", String.format("%.1f%%", percentage)); 727 } 728 } catch (final InterruptedException | ExecutionException e) { 729 LOGGER.error("(UUID) Failed to retrieve last batch. Will try again", e); 730 } 731 } 732 LOGGER.info("(UUID) PlotSquared has cached all UUIDs"); 733 }, 10, TimeUnit.SECONDS); 734 } 735 736 @Override 737 public void onDisable() { 738 PlotSquared.get().disable(); 739 Bukkit.getScheduler().cancelTasks(this); 740 } 741 742 @Override 743 public void shutdown() { 744 this.getServer().getPluginManager().disablePlugin(this); 745 } 746 747 @Override 748 public void shutdownServer() { 749 getServer().shutdown(); 750 } 751 752 private void registerCommands() { 753 final BukkitCommand bukkitCommand = new BukkitCommand(); 754 final PluginCommand plotCommand = getCommand("plots"); 755 if (plotCommand != null) { 756 plotCommand.setExecutor(bukkitCommand); 757 plotCommand.setAliases(Arrays.asList("p", "ps", "plotme", "plot")); 758 plotCommand.setTabCompleter(bukkitCommand); 759 } 760 } 761 762 @Override 763 public @NonNull File getDirectory() { 764 return getDataFolder(); 765 } 766 767 @Override 768 public @NonNull File worldContainer() { 769 return Bukkit.getWorldContainer(); 770 } 771 772 @SuppressWarnings("deprecation") 773 private void runEntityTask() { 774 TaskManager.runTaskRepeat(() -> this.plotAreaManager.forEachPlotArea(plotArea -> { 775 final World world = Bukkit.getWorld(plotArea.getWorldName()); 776 try { 777 if (world == null) { 778 return; 779 } 780 List<Entity> entities = world.getEntities(); 781 Iterator<Entity> iterator = entities.iterator(); 782 while (iterator.hasNext()) { 783 Entity entity = iterator.next(); 784 //noinspection ConstantValue - getEntitySpawnReason annotated as NotNull, but is not NotNull. lol. 785 if (PaperLib.isPaper() && entity.getEntitySpawnReason() != null && "CUSTOM".equals(entity.getEntitySpawnReason().name())) { 786 continue; 787 } 788 // Fallback for Spigot not having Entity#getEntitySpawnReason 789 if (entity.getMetadata("ps_custom_spawned").stream().anyMatch(MetadataValue::asBoolean)) { 790 continue; 791 } 792 // TODO: use (type) pattern matching when targeting java 21 793 switch (entity.getType().toString()) { 794 case "EGG": 795 case "FISHING_HOOK", "FISHING_BOBBER": 796 case "ENDER_SIGNAL", "EYE_OF_ENDER": 797 case "AREA_EFFECT_CLOUD": 798 case "EXPERIENCE_ORB": 799 case "LEASH_HITCH", "LEASH_KNOT": 800 case "FIREWORK", "FIREWORK_ROCKET": 801 case "LIGHTNING", "LIGHTNING_BOLT": 802 case "WITHER_SKULL": 803 case "UNKNOWN": 804 case "PLAYER": 805 // non moving / unmovable 806 continue; 807 case "THROWN_EXP_BOTTLE", "EXPERIENCE_BOTTLE": 808 case "SPLASH_POTION", "POTION": 809 case "SNOWBALL": 810 case "SHULKER_BULLET": 811 case "SPECTRAL_ARROW": 812 case "ENDER_PEARL": 813 case "ARROW": 814 case "LLAMA_SPIT": 815 case "TRIDENT": 816 // managed elsewhere | projectile 817 continue; 818 case "ITEM_FRAME": 819 case "PAINTING": 820 // Not vehicles 821 continue; 822 case "ARMOR_STAND": 823 // Temporarily classify as vehicle 824 case "MINECART": 825 case "MINECART_CHEST": 826 case "CHEST_MINECART": 827 case "MINECART_COMMAND": 828 case "COMMAND_BLOCK_MINECART": 829 case "MINECART_FURNACE": 830 case "FURNACE_MINECART": 831 case "MINECART_HOPPER": 832 case "HOPPER_MINECART": 833 case "MINECART_MOB_SPAWNER": 834 case "SPAWNER_MINECART": 835 case "ENDER_CRYSTAL": 836 case "MINECART_TNT": 837 case "TNT_MINECART": 838 case "CHEST_BOAT": 839 case "BOAT": 840 case "ACACIA_BOAT", "BIRCH_BOAT", "CHERRY_BOAT", "DARK_OAK_BOAT", "JUNGLE_BOAT", "MANGROVE_BOAT", 841 "OAK_BOAT", "PALE_OAK_BOAT", "SPRUCE_BOAT", "BAMBOO_RAFT": 842 case "ACACIA_CHEST_BOAT", "BIRCH_CHEST_BOAT", "CHERRY_CHEST_BOAT", "DARK_OAK_CHEST_BOAT", 843 "JUNGLE_CHEST_BOAT", "MANGROVE_CHEST_BOAT", "OAK_CHEST_BOAT", "PALE_OAK_CHEST_BOAT", 844 "SPRUCE_CHEST_BOAT", "BAMBOO_CHEST_RAFT": 845 if (Settings.Enabled_Components.KILL_ROAD_VEHICLES) { 846 com.plotsquared.core.location.Location location = BukkitUtil.adapt(entity.getLocation()); 847 Plot plot = location.getPlot(); 848 if (plot == null) { 849 if (location.isPlotArea()) { 850 if (entity.hasMetadata("ps-tmp-teleport")) { 851 continue; 852 } 853 this.removeRoadEntity(entity, iterator); 854 } 855 continue; 856 } 857 List<MetadataValue> meta = entity.getMetadata("plot"); 858 if (meta.isEmpty()) { 859 continue; 860 } 861 Plot origin = (Plot) meta.get(0).value(); 862 if (!plot.equals(origin.getBasePlot(false))) { 863 if (entity.hasMetadata("ps-tmp-teleport")) { 864 continue; 865 } 866 this.removeRoadEntity(entity, iterator); 867 } 868 } 869 continue; 870 case "SMALL_FIREBALL": 871 case "FIREBALL": 872 case "DRAGON_FIREBALL": 873 case "DROPPED_ITEM", "ITEM": 874 if (Settings.Enabled_Components.KILL_ROAD_ITEMS 875 && plotArea.getOwnedPlotAbs(BukkitUtil.adapt(entity.getLocation())) == null) { 876 this.removeRoadEntity(entity, iterator); 877 } 878 // dropped item 879 continue; 880 case "PRIMED_TNT", "TNT": 881 case "FALLING_BLOCK": 882 // managed elsewhere 883 continue; 884 case "SHULKER": 885 if (Settings.Enabled_Components.KILL_ROAD_MOBS && (Settings.Enabled_Components.KILL_NAMED_ROAD_MOBS || entity.getCustomName() == null)) { 886 LivingEntity livingEntity = (LivingEntity) entity; 887 List<MetadataValue> meta = entity.getMetadata("shulkerPlot"); 888 if (!meta.isEmpty()) { 889 if (livingEntity.isLeashed() && !Settings.Enabled_Components.KILL_OWNED_ROAD_MOBS) { 890 continue; 891 } 892 if (entity.hasMetadata("keep")) { 893 continue; 894 } 895 896 PlotId originalPlotId = (PlotId) meta.get(0).value(); 897 if (originalPlotId != null) { 898 com.plotsquared.core.location.Location pLoc = BukkitUtil.adapt(entity.getLocation()); 899 PlotArea area = pLoc.getPlotArea(); 900 if (area != null) { 901 Plot currentPlot = area.getPlotAbs(pLoc); 902 if (currentPlot == null || !originalPlotId.equals(currentPlot.getId())) { 903 if (entity.hasMetadata("ps-tmp-teleport")) { 904 continue; 905 } 906 this.removeRoadEntity(entity, iterator); 907 } 908 } 909 } 910 } else { 911 //This is to apply the metadata to already spawned shulkers (see EntitySpawnListener.java) 912 com.plotsquared.core.location.Location pLoc = BukkitUtil.adapt(entity.getLocation()); 913 PlotArea area = pLoc.getPlotArea(); 914 if (area != null) { 915 Plot currentPlot = area.getPlotAbs(pLoc); 916 if (currentPlot != null) { 917 entity.setMetadata( 918 "shulkerPlot", 919 new FixedMetadataValue((Plugin) PlotSquared.platform(), currentPlot.getId()) 920 ); 921 } 922 } 923 } 924 } 925 continue; 926 case "ZOMBIFIED_PIGLIN": 927 case "PIGLIN_BRUTE": 928 case "LLAMA": 929 case "DONKEY": 930 case "MULE": 931 case "ZOMBIE_HORSE": 932 case "SKELETON_HORSE": 933 case "HUSK": 934 case "ELDER_GUARDIAN": 935 case "WITHER_SKELETON": 936 case "STRAY": 937 case "ZOMBIE_VILLAGER": 938 case "EVOKER": 939 case "EVOKER_FANGS": 940 case "VEX": 941 case "VINDICATOR": 942 case "POLAR_BEAR": 943 case "BAT": 944 case "BLAZE": 945 case "CAVE_SPIDER": 946 case "CHICKEN": 947 case "COW": 948 case "CREEPER": 949 case "ENDERMAN": 950 case "ENDERMITE": 951 case "ENDER_DRAGON": 952 case "GHAST": 953 case "GIANT": 954 case "GUARDIAN": 955 case "HORSE": 956 case "IRON_GOLEM": 957 case "MAGMA_CUBE": 958 case "MUSHROOM_COW", "MOOSHROOM": 959 case "OCELOT": 960 case "PIG": 961 case "PIG_ZOMBIE": 962 case "RABBIT": 963 case "SHEEP": 964 case "SILVERFISH": 965 case "SKELETON": 966 case "SLIME": 967 case "SNOWMAN", "SNOW_GOLEM": 968 case "SPIDER": 969 case "SQUID": 970 case "VILLAGER": 971 case "WITCH": 972 case "WITHER": 973 case "WOLF": 974 case "ZOMBIE": 975 case "PARROT": 976 case "SALMON": 977 case "DOLPHIN": 978 case "TROPICAL_FISH": 979 case "DROWNED": 980 case "COD": 981 case "TURTLE": 982 case "PUFFERFISH": 983 case "PHANTOM": 984 case "ILLUSIONER": 985 case "CAT": 986 case "PANDA": 987 case "FOX": 988 case "PILLAGER": 989 case "TRADER_LLAMA": 990 case "WANDERING_TRADER": 991 case "RAVAGER": 992 case "BEE": 993 case "HOGLIN": 994 case "PIGLIN": 995 case "ZOGLIN": 996 default: { 997 if (Settings.Enabled_Components.KILL_ROAD_MOBS) { 998 Location location = entity.getLocation(); 999 if (BukkitUtil.adapt(location).isPlotRoad()) { 1000 if (entity instanceof LivingEntity livingEntity) { 1001 if ((Settings.Enabled_Components.KILL_OWNED_ROAD_MOBS || !livingEntity.isLeashed()) 1002 || !entity.hasMetadata("keep")) { 1003 Entity passenger = entity.getPassenger(); 1004 if ((Settings.Enabled_Components.KILL_OWNED_ROAD_MOBS 1005 || !((passenger instanceof Player) || livingEntity.isLeashed())) 1006 && (Settings.Enabled_Components.KILL_NAMED_ROAD_MOBS || entity.getCustomName() == null) 1007 && entity.getMetadata("keep").isEmpty()) { 1008 if (entity.hasMetadata("ps-tmp-teleport")) { 1009 continue; 1010 } 1011 this.removeRoadEntity(entity, iterator); 1012 } 1013 } 1014 } else { 1015 Entity passenger = entity.getPassenger(); 1016 if ((Settings.Enabled_Components.KILL_OWNED_ROAD_MOBS || !(passenger instanceof Player)) 1017 && (Settings.Enabled_Components.KILL_NAMED_ROAD_MOBS && entity.getCustomName() != null) 1018 && entity.getMetadata("keep").isEmpty()) { 1019 if (entity.hasMetadata("ps-tmp-teleport")) { 1020 continue; 1021 } 1022 this.removeRoadEntity(entity, iterator); 1023 } 1024 } 1025 } 1026 } 1027 } 1028 } 1029 } 1030 } catch (Throwable e) { 1031 e.printStackTrace(); 1032 } 1033 }), TaskTime.seconds(1L)); 1034 } 1035 1036 private void removeRoadEntity(Entity entity, Iterator<Entity> entityIterator) { 1037 RemoveRoadEntityEvent event = eventDispatcher.callRemoveRoadEntity(BukkitAdapter.adapt(entity)); 1038 1039 if (event.getEventResult() == Result.DENY) { 1040 return; 1041 } 1042 1043 entityIterator.remove(); 1044 entity.remove(); 1045 } 1046 1047 @Override 1048 public @Nullable 1049 final ChunkGenerator getDefaultWorldGenerator( 1050 final @NonNull String worldName, 1051 final @Nullable String id 1052 ) { 1053 final IndependentPlotGenerator result; 1054 if (id != null && id.equalsIgnoreCase("single")) { 1055 result = injector().getInstance(SingleWorldGenerator.class); 1056 } else { 1057 result = injector().getInstance(Key.get(IndependentPlotGenerator.class, DefaultGenerator.class)); 1058 if (!PlotSquared.get().setupPlotWorld(worldName, id, result)) { 1059 return null; 1060 } 1061 } 1062 return (ChunkGenerator) result.specify(worldName); 1063 } 1064 1065 @Override 1066 public @Nullable GeneratorWrapper<?> getGenerator( 1067 final @NonNull String world, 1068 final @Nullable String name 1069 ) { 1070 if (name == null) { 1071 return null; 1072 } 1073 final Plugin genPlugin = Bukkit.getPluginManager().getPlugin(name); 1074 if (genPlugin != null && genPlugin.isEnabled()) { 1075 ChunkGenerator gen = genPlugin.getDefaultWorldGenerator(world, ""); 1076 if (gen instanceof GeneratorWrapper<?>) { 1077 return (GeneratorWrapper<?>) gen; 1078 } 1079 return new BukkitPlotGenerator(world, gen, this.plotAreaManager); 1080 } else { 1081 return new BukkitPlotGenerator( 1082 world, 1083 injector().getInstance(Key.get(IndependentPlotGenerator.class, DefaultGenerator.class)), 1084 this.plotAreaManager 1085 ); 1086 } 1087 } 1088 1089 @Override 1090 public void startMetrics() { 1091 if (this.metricsStarted) { 1092 return; 1093 } 1094 this.metricsStarted = true; 1095 Metrics metrics = new Metrics(this, BSTATS_ID); // bstats 1096 metrics.addCustomChart(new DrilldownPie("area_types", () -> { 1097 final Map<String, Map<String, Integer>> map = new HashMap<>(); 1098 for (final PlotAreaType plotAreaType : PlotAreaType.values()) { 1099 final Map<String, Integer> terrainTypes = new HashMap<>(); 1100 for (final PlotAreaTerrainType plotAreaTerrainType : PlotAreaTerrainType.values()) { 1101 terrainTypes.put(plotAreaTerrainType.name().toLowerCase(), 0); 1102 } 1103 map.put(plotAreaType.name().toLowerCase(), terrainTypes); 1104 } 1105 for (final PlotArea plotArea : this.plotAreaManager.getAllPlotAreas()) { 1106 final Map<String, Integer> terrainTypeMap = map.get(plotArea.getType().name().toLowerCase()); 1107 terrainTypeMap.put( 1108 plotArea.getTerrain().name().toLowerCase(), 1109 terrainTypeMap.get(plotArea.getTerrain().name().toLowerCase()) + 1 1110 ); 1111 } 1112 return map; 1113 })); 1114 metrics.addCustomChart(new SimplePie( 1115 "premium", 1116 () -> PremiumVerification.isPremium() ? "Premium" : "Non-Premium" 1117 )); 1118 metrics.addCustomChart(new SimplePie("worlds", () -> Settings.Enabled_Components.WORLDS ? "true" : "false")); 1119 metrics.addCustomChart(new SimplePie("economy", () -> Settings.Enabled_Components.ECONOMY ? "true" : "false")); 1120 metrics.addCustomChart(new SimplePie( 1121 "plot_expiry", 1122 () -> Settings.Enabled_Components.PLOT_EXPIRY ? "true" : "false" 1123 )); 1124 metrics.addCustomChart(new SimplePie("database_type", () -> Storage.MySQL.USE ? "MySQL" : "SQLite")); 1125 metrics.addCustomChart(new SimplePie( 1126 "worldedit_implementation", 1127 () -> Bukkit.getPluginManager().getPlugin("FastAsyncWorldEdit") != null ? "FastAsyncWorldEdit" : "WorldEdit" 1128 )); 1129 metrics.addCustomChart(new SimplePie("offline_mode", () -> Settings.UUID.OFFLINE ? "true" : "false")); 1130 metrics.addCustomChart(new SimplePie("offline_mode_force", () -> Settings.UUID.FORCE_LOWERCASE ? "true" : "false")); 1131 } 1132 1133 @Override 1134 public void unregister(final @NonNull PlotPlayer<?> player) { 1135 PlotSquared.platform().playerManager().removePlayer(player.getUUID()); 1136 } 1137 1138 @Override 1139 public void setGenerator(final @NonNull String worldName) { 1140 World world = BukkitUtil.getWorld(worldName); 1141 if (world == null) { 1142 // create world 1143 ConfigurationSection worldConfig = this.worldConfiguration.getConfigurationSection("worlds." + worldName); 1144 String manager = worldConfig.getString("generator.plugin", pluginName()); 1145 PlotAreaBuilder builder = 1146 PlotAreaBuilder.newBuilder().plotManager(manager).generatorName(worldConfig.getString( 1147 "generator.init", 1148 manager 1149 )) 1150 .plotAreaType(ConfigurationUtil.getType(worldConfig)).terrainType(ConfigurationUtil.getTerrain( 1151 worldConfig)) 1152 .settingsNodesWrapper(new SettingsNodesWrapper(new ConfigurationNode[0], null)).worldName(worldName); 1153 injector().getInstance(SetupUtils.class).setupWorld(builder); 1154 world = Bukkit.getWorld(worldName); 1155 } else { 1156 try { 1157 if (!this.plotAreaManager.hasPlotArea(worldName)) { 1158 SetGenCB.setGenerator(BukkitUtil.getWorld(worldName)); 1159 } 1160 } catch (final Exception e) { 1161 LOGGER.error("Failed to reload world: {} | {}", world, e.getMessage()); 1162 Bukkit.getServer().unloadWorld(world, false); 1163 return; 1164 } 1165 } 1166 assert world != null; 1167 ChunkGenerator gen = world.getGenerator(); 1168 if (gen instanceof BukkitPlotGenerator) { 1169 PlotSquared.get().loadWorld(worldName, (BukkitPlotGenerator) gen); 1170 } else if (gen != null) { 1171 PlotSquared.get().loadWorld(worldName, new BukkitPlotGenerator(worldName, gen, this.plotAreaManager)); 1172 } else if (this.worldConfiguration.contains("worlds." + worldName)) { 1173 PlotSquared.get().loadWorld(worldName, null); 1174 } 1175 } 1176 1177 @Override 1178 public @NonNull String serverNativePackage() { 1179 final String name = Bukkit.getServer().getClass().getPackage().getName(); 1180 String ver = name.substring(name.lastIndexOf('.') + 1); 1181 // org.bukkit.craftbukkit is no longer suffixed by a version 1182 return ver.equals("craftbukkit") ? "" : ver; 1183 } 1184 1185 @Override 1186 public @NonNull GeneratorWrapper<?> wrapPlotGenerator( 1187 final @NonNull String world, 1188 final @NonNull IndependentPlotGenerator generator 1189 ) { 1190 return new BukkitPlotGenerator(world, generator, this.plotAreaManager); 1191 } 1192 1193 @SuppressWarnings("deprecation") // Paper deprecation 1194 @Override 1195 public @NonNull String pluginsFormatted() { 1196 StringBuilder msg = new StringBuilder(); 1197 List<Plugin> plugins = new ArrayList<>(); 1198 Collections.addAll(plugins, Bukkit.getServer().getPluginManager().getPlugins()); 1199 plugins.sort(Comparator.comparing(Plugin::getName)); 1200 msg.append("Plugins (").append(plugins.size()).append("): \n"); 1201 for (Plugin p : plugins) { 1202 msg.append(" - ").append(p.getName()).append(":").append("\n") 1203 .append(" • Version: ").append(p.getDescription().getVersion()).append("\n") 1204 .append(" • Enabled: ").append(p.isEnabled()).append("\n") 1205 .append(" • Main: ").append(p.getDescription().getMain()).append("\n") 1206 .append(" • Authors: ").append(p.getDescription().getAuthors()).append("\n") 1207 .append(" • Load Before: ").append(p.getDescription().getLoadBefore()).append("\n") 1208 .append(" • Dependencies: ").append(p.getDescription().getDepend()).append("\n") 1209 .append(" • Soft Dependencies: ").append(p.getDescription().getSoftDepend()).append("\n"); 1210 List<RegisteredServiceProvider<?>> providers = Bukkit.getServicesManager().getRegistrations(p); 1211 if (!providers.isEmpty()) { 1212 msg.append(" • Provided Services: \n"); 1213 for (RegisteredServiceProvider<?> provider : providers) { 1214 msg.append(" • ") 1215 .append(provider.getService().getName()).append(" = ") 1216 .append(provider.getProvider().getClass().getName()) 1217 .append(" (priority: ").append(provider.getPriority()).append(")") 1218 .append("\n"); 1219 } 1220 } 1221 } 1222 return msg.toString(); 1223 } 1224 1225 @Override 1226 @SuppressWarnings({"ConstantConditions", "deprecation"}) // Paper deprecation 1227 public @NonNull String worldEditImplementations() { 1228 StringBuilder msg = new StringBuilder(); 1229 if (Bukkit.getPluginManager().getPlugin("FastAsyncWorldEdit") != null) { 1230 msg.append("FastAsyncWorldEdit: ").append(Bukkit 1231 .getPluginManager() 1232 .getPlugin("FastAsyncWorldEdit") 1233 .getDescription() 1234 .getVersion()); 1235 } else if (Bukkit.getPluginManager().getPlugin("AsyncWorldEdit") != null) { 1236 msg.append("AsyncWorldEdit: ").append(Bukkit 1237 .getPluginManager() 1238 .getPlugin("AsyncWorldEdit") 1239 .getDescription() 1240 .getVersion()).append("\n"); 1241 msg.append("WorldEdit: ").append(Bukkit.getPluginManager().getPlugin("WorldEdit").getDescription().getVersion()); 1242 } else { 1243 msg.append("WorldEdit: ").append(Bukkit.getPluginManager().getPlugin("WorldEdit").getDescription().getVersion()); 1244 } 1245 return msg.toString(); 1246 } 1247 1248 @Override 1249 public com.plotsquared.core.location.@NonNull World<?> getPlatformWorld(final @NonNull String worldName) { 1250 return BukkitWorld.of(worldName); 1251 } 1252 1253 @Override 1254 public @NonNull Audience consoleAudience() { 1255 return BukkitUtil.BUKKIT_AUDIENCES.console(); 1256 } 1257 1258 @Override 1259 public @NonNull String pluginName() { 1260 return this.pluginName; 1261 } 1262 1263 public SingleWorldListener getSingleWorldListener() { 1264 return this.singleWorldListener; 1265 } 1266 1267 @Override 1268 public @NonNull Injector injector() { 1269 return this.injector; 1270 } 1271 1272 @Override 1273 public @NonNull PlotAreaManager plotAreaManager() { 1274 return this.plotAreaManager; 1275 } 1276 1277 @NonNull 1278 @Override 1279 public Locale getLocale() { 1280 return this.serverLocale; 1281 } 1282 1283 @Override 1284 public void setLocale(final @NonNull Locale locale) { 1285 throw new UnsupportedOperationException("Cannot replace server locale"); 1286 } 1287 1288 @Override 1289 public @NonNull PlatformWorldManager<?> worldManager() { 1290 return this.worldManager; 1291 } 1292 1293 @Override 1294 @NonNull 1295 public PlayerManager<? extends PlotPlayer<Player>, ? extends Player> playerManager() { 1296 return this.playerManager; 1297 } 1298 1299 @Override 1300 public void copyCaptionMaps() { 1301 /* Make this prettier at some point */ 1302 final String[] languages = new String[]{"en"}; 1303 for (final String language : languages) { 1304 if (!new File(new File(this.getDataFolder(), "lang"), String.format("messages_%s.json", language)).exists()) { 1305 this.saveResource(String.format("lang/messages_%s.json", language), false); 1306 LOGGER.info("Copied language file 'messages_{}.json'", language); 1307 } 1308 } 1309 } 1310 1311 @NonNull 1312 @Override 1313 public String toLegacyPlatformString(final @NonNull Component component) { 1314 return LegacyComponentSerializer.legacyAmpersand().serialize(component); 1315 } 1316 1317 @Override 1318 public boolean isFaweHooking() { 1319 return faweHook; 1320 } 1321 1322}