From 328212b0ca49826b9bc9a65a3e1e3d62827a93d6 Mon Sep 17 00:00:00 2001 From: OmerBenGera Date: Sat, 1 Jun 2024 16:57:18 +0300 Subject: [PATCH] Do not keep references of World objects & handle cases when worlds are loaded & unloaded --- .../api/hooks/TickableProvider.java | 18 ++- .../wildloaders/api/loaders/ChunkLoader.java | 8 ++ .../hooks/TickableProvider_EpicSpawners6.java | 13 +- .../hooks/TickableProvider_EpicSpawners7.java | 13 +- .../hooks/TickableProvider_EpicSpawners8.java | 13 +- .../wildloaders/handlers/DataHandler.java | 33 ++--- .../wildloaders/handlers/LoadersHandler.java | 123 +++++++++++++++--- .../wildloaders/handlers/NPCHandler.java | 47 ++++--- .../handlers/ProvidersHandler.java | 2 +- .../wildloaders/listeners/ChunksListener.java | 19 ++- .../loaders/UnloadedChunkLoader.java | 38 ++++++ .../wildloaders/loaders/WChunkLoader.java | 27 ++-- .../wildloaders/npc/NPCIdentifier.java | 41 ------ .../wildloaders/utils/BlockPosition.java | 92 +++++++++++++ .../utils/database/QueryParameters.java | 17 ++- .../utils/locations/LocationUtils.java | 41 ------ 16 files changed, 364 insertions(+), 181 deletions(-) create mode 100644 src/main/java/com/bgsoftware/wildloaders/loaders/UnloadedChunkLoader.java delete mode 100644 src/main/java/com/bgsoftware/wildloaders/npc/NPCIdentifier.java create mode 100644 src/main/java/com/bgsoftware/wildloaders/utils/BlockPosition.java delete mode 100644 src/main/java/com/bgsoftware/wildloaders/utils/locations/LocationUtils.java diff --git a/API/src/main/java/com/bgsoftware/wildloaders/api/hooks/TickableProvider.java b/API/src/main/java/com/bgsoftware/wildloaders/api/hooks/TickableProvider.java index e8576a4..7ba8be7 100644 --- a/API/src/main/java/com/bgsoftware/wildloaders/api/hooks/TickableProvider.java +++ b/API/src/main/java/com/bgsoftware/wildloaders/api/hooks/TickableProvider.java @@ -2,12 +2,28 @@ package com.bgsoftware.wildloaders.api.hooks; import org.bukkit.Chunk; +import java.util.Collection; + public interface TickableProvider { /** * Simulate a tick on a list of provided chunks. + * + * @param chunks The chunks to tick. + * @deprecated See {@link #tick(Collection)} + */ + @Deprecated + default void tick(Chunk[] chunks) { + throw new UnsupportedOperationException("TickableProvider#tick is not supported anymore"); + } + + /** + * Simulate a tick on a list of provided chunks. + * * @param chunks The chunks to tick. */ - void tick(Chunk[] chunks); + default void tick(Collection chunks) { + tick(chunks.toArray(new Chunk[0])); + } } diff --git a/API/src/main/java/com/bgsoftware/wildloaders/api/loaders/ChunkLoader.java b/API/src/main/java/com/bgsoftware/wildloaders/api/loaders/ChunkLoader.java index f0cd6fb..92fecaa 100644 --- a/API/src/main/java/com/bgsoftware/wildloaders/api/loaders/ChunkLoader.java +++ b/API/src/main/java/com/bgsoftware/wildloaders/api/loaders/ChunkLoader.java @@ -34,9 +34,17 @@ public interface ChunkLoader { /** * Get the chunks that this chunk-loader is loading. + * + * @deprecated See {@link #getLoadedChunksCollection()} */ + @Deprecated Chunk[] getLoadedChunks(); + /** + * Get the chunks that this chunk-loader is loading. + */ + Collection getLoadedChunksCollection(); + /** * Get the NPC of this chunk loader. */ diff --git a/Hooks/EpicSpawners6/src/main/java/com/bgsoftware/wildloaders/hooks/TickableProvider_EpicSpawners6.java b/Hooks/EpicSpawners6/src/main/java/com/bgsoftware/wildloaders/hooks/TickableProvider_EpicSpawners6.java index 907b970..b63e559 100644 --- a/Hooks/EpicSpawners6/src/main/java/com/bgsoftware/wildloaders/hooks/TickableProvider_EpicSpawners6.java +++ b/Hooks/EpicSpawners6/src/main/java/com/bgsoftware/wildloaders/hooks/TickableProvider_EpicSpawners6.java @@ -5,25 +5,26 @@ import com.songoda.epicspawners.EpicSpawners; import org.bukkit.Chunk; import org.bukkit.Location; +import java.util.Collection; import java.util.HashMap; -import java.util.List; +import java.util.HashSet; import java.util.Map; -import java.util.stream.Collectors; -import java.util.stream.Stream; +import java.util.Set; public final class TickableProvider_EpicSpawners6 implements TickableProvider { private final Map spawnerDelays = new HashMap<>(); @Override - public void tick(Chunk[] chunks) { + public void tick(Collection chunks) { if (EpicSpawners.getInstance().getSpawnerManager() == null) return; - List chunkList = Stream.of(chunks).map(chunk -> pair(chunk.getX(), chunk.getZ())).collect(Collectors.toList()); + Set chunkKeys = new HashSet<>(); + chunks.forEach(chunk -> chunkKeys.add(pair(chunk.getX(), chunk.getZ()))); EpicSpawners.getInstance().getSpawnerManager().getSpawners().stream() - .filter(spawner -> chunkList.contains(pair(spawner.getX() >> 4, spawner.getZ() >> 4))) + .filter(spawner -> chunkKeys.contains(pair(spawner.getX() >> 4, spawner.getZ() >> 4))) .forEach(spawner -> { Location location = spawner.getLocation(); TickDelay tickDelay = spawnerDelays.get(location); diff --git a/Hooks/EpicSpawners7/src/main/java/com/bgsoftware/wildloaders/hooks/TickableProvider_EpicSpawners7.java b/Hooks/EpicSpawners7/src/main/java/com/bgsoftware/wildloaders/hooks/TickableProvider_EpicSpawners7.java index 29903b2..aff3af3 100644 --- a/Hooks/EpicSpawners7/src/main/java/com/bgsoftware/wildloaders/hooks/TickableProvider_EpicSpawners7.java +++ b/Hooks/EpicSpawners7/src/main/java/com/bgsoftware/wildloaders/hooks/TickableProvider_EpicSpawners7.java @@ -5,25 +5,26 @@ import com.songoda.epicspawners.EpicSpawners; import org.bukkit.Chunk; import org.bukkit.Location; +import java.util.Collection; import java.util.HashMap; -import java.util.List; +import java.util.HashSet; import java.util.Map; -import java.util.stream.Collectors; -import java.util.stream.Stream; +import java.util.Set; public final class TickableProvider_EpicSpawners7 implements TickableProvider { private final Map spawnerDelays = new HashMap<>(); @Override - public void tick(Chunk[] chunks) { + public void tick(Collection chunks) { if (EpicSpawners.getInstance().getSpawnerManager() == null) return; - List chunkList = Stream.of(chunks).map(chunk -> pair(chunk.getX(), chunk.getZ())).collect(Collectors.toList()); + Set chunkKeys = new HashSet<>(); + chunks.forEach(chunk -> chunkKeys.add(pair(chunk.getX(), chunk.getZ()))); EpicSpawners.getInstance().getSpawnerManager().getSpawners().stream() - .filter(spawner -> chunkList.contains(pair(spawner.getX() >> 4, spawner.getZ() >> 4))) + .filter(spawner -> chunkKeys.contains(pair(spawner.getX() >> 4, spawner.getZ() >> 4))) .forEach(spawner -> { Location location = spawner.getLocation(); TickDelay tickDelay = spawnerDelays.get(location); diff --git a/Hooks/EpicSpawners8/src/main/java/com/bgsoftware/wildloaders/hooks/TickableProvider_EpicSpawners8.java b/Hooks/EpicSpawners8/src/main/java/com/bgsoftware/wildloaders/hooks/TickableProvider_EpicSpawners8.java index dd0b27c..74ead0f 100644 --- a/Hooks/EpicSpawners8/src/main/java/com/bgsoftware/wildloaders/hooks/TickableProvider_EpicSpawners8.java +++ b/Hooks/EpicSpawners8/src/main/java/com/bgsoftware/wildloaders/hooks/TickableProvider_EpicSpawners8.java @@ -5,25 +5,26 @@ import com.craftaro.epicspawners.api.EpicSpawnersApi; import org.bukkit.Chunk; import org.bukkit.Location; +import java.util.Collection; import java.util.HashMap; -import java.util.List; +import java.util.HashSet; import java.util.Map; -import java.util.stream.Collectors; -import java.util.stream.Stream; +import java.util.Set; public final class TickableProvider_EpicSpawners8 implements TickableProvider { private final Map spawnerDelays = new HashMap<>(); @Override - public void tick(Chunk[] chunks) { + public void tick(Collection chunks) { if (EpicSpawnersApi.getSpawnerManager() == null) return; - List chunkList = Stream.of(chunks).map(chunk -> pair(chunk.getX(), chunk.getZ())).collect(Collectors.toList()); + Set chunkKeys = new HashSet<>(); + chunks.forEach(chunk -> chunkKeys.add(pair(chunk.getX(), chunk.getZ()))); EpicSpawnersApi.getSpawnerManager().getSpawners().stream() - .filter(spawner -> chunkList.contains(pair(spawner.getX() >> 4, spawner.getZ() >> 4))) + .filter(spawner -> chunkKeys.contains(pair(spawner.getX() >> 4, spawner.getZ() >> 4))) .forEach(spawner -> { Location location = spawner.getLocation(); TickDelay tickDelay = spawnerDelays.get(location); diff --git a/src/main/java/com/bgsoftware/wildloaders/handlers/DataHandler.java b/src/main/java/com/bgsoftware/wildloaders/handlers/DataHandler.java index cf89937..8bd7f78 100644 --- a/src/main/java/com/bgsoftware/wildloaders/handlers/DataHandler.java +++ b/src/main/java/com/bgsoftware/wildloaders/handlers/DataHandler.java @@ -2,18 +2,16 @@ package com.bgsoftware.wildloaders.handlers; import com.bgsoftware.wildloaders.WildLoadersPlugin; import com.bgsoftware.wildloaders.api.loaders.LoaderData; -import com.bgsoftware.wildloaders.utils.ChunkLoaderChunks; +import com.bgsoftware.wildloaders.utils.BlockPosition; import com.bgsoftware.wildloaders.utils.ServerVersion; import com.bgsoftware.wildloaders.utils.database.Database; -import com.bgsoftware.wildloaders.utils.locations.LocationUtils; import com.bgsoftware.wildloaders.utils.threads.Executor; import org.bukkit.Bukkit; -import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.World; import java.io.File; -import java.util.List; import java.util.Optional; import java.util.UUID; @@ -38,16 +36,16 @@ public final class DataHandler { Database.executeUpdate("CREATE TABLE IF NOT EXISTS npc_identifiers (location TEXT NOT NULL PRIMARY KEY, uuid TEXT NOT NULL);"); Database.executeQuery("SELECT * FROM npc_identifiers;", resultSet -> { while (resultSet.next()) { - Location location = LocationUtils.getLocation(resultSet.getString("location")); + BlockPosition blockPosition = BlockPosition.deserialize(resultSet.getString("location")); UUID uuid = UUID.fromString(resultSet.getString("uuid")); - plugin.getNPCs().registerUUID(location, uuid); + plugin.getNPCs().registerUUID(blockPosition, uuid); } }); Database.executeUpdate("CREATE TABLE IF NOT EXISTS chunk_loaders (location TEXT NOT NULL PRIMARY KEY, placer TEXT NOT NULL, loader_data TEXT NOT NULL, timeLeft BIGINT NOT NULL);"); Database.executeQuery("SELECT * FROM chunk_loaders;", resultSet -> { while (resultSet.next()) { - Location location = LocationUtils.getLocation(resultSet.getString("location")); + BlockPosition blockPosition = BlockPosition.deserialize(resultSet.getString("location")); UUID placer = UUID.fromString(resultSet.getString("placer")); Optional loaderData = plugin.getLoaders().getLoaderData(resultSet.getString("loader_data")); long timeLeft = resultSet.getLong("timeLeft"); @@ -55,21 +53,14 @@ public final class DataHandler { if (!loaderData.isPresent()) continue; - Material blockType = location.getBlock().getType(); - - if (ServerVersion.isLegacy() && blockType == Material.CAULDRON) { - blockType = Material.CAULDRON_ITEM; + World world = blockPosition.getWorld(); + if (world != null) { + Location location = blockPosition.getLocation(); + plugin.getLoaders().addChunkLoaderWithoutDBSave(loaderData.get(), placer, + location, timeLeft, true); + } else { + plugin.getLoaders().addUnloadedChunkLoader(loaderData.get(), placer, blockPosition, timeLeft); } - - if (blockType != loaderData.get().getLoaderItem().getType()) { - WildLoadersPlugin.log("The chunk-loader at " + LocationUtils.getLocation(location) + " is invalid."); - continue; - } - - List chunksToLoad = ChunkLoaderChunks.calculateChunks(loaderData.get(), placer, location); - chunksToLoad.removeIf(chunk -> plugin.getLoaders().getChunkLoader(chunk).isPresent()); - - plugin.getLoaders().addChunkLoaderWithoutDBSave(loaderData.get(), placer, location, timeLeft, chunksToLoad); } }); } diff --git a/src/main/java/com/bgsoftware/wildloaders/handlers/LoadersHandler.java b/src/main/java/com/bgsoftware/wildloaders/handlers/LoadersHandler.java index 598692d..490db57 100644 --- a/src/main/java/com/bgsoftware/wildloaders/handlers/LoadersHandler.java +++ b/src/main/java/com/bgsoftware/wildloaders/handlers/LoadersHandler.java @@ -4,19 +4,25 @@ import com.bgsoftware.wildloaders.WildLoadersPlugin; import com.bgsoftware.wildloaders.api.loaders.ChunkLoader; import com.bgsoftware.wildloaders.api.loaders.LoaderData; import com.bgsoftware.wildloaders.api.managers.LoadersManager; +import com.bgsoftware.wildloaders.loaders.UnloadedChunkLoader; import com.bgsoftware.wildloaders.loaders.WChunkLoader; import com.bgsoftware.wildloaders.loaders.WLoaderData; +import com.bgsoftware.wildloaders.utils.BlockPosition; import com.bgsoftware.wildloaders.utils.ChunkLoaderChunks; +import com.bgsoftware.wildloaders.utils.ServerVersion; import com.bgsoftware.wildloaders.utils.chunks.ChunkPosition; import com.bgsoftware.wildloaders.utils.database.Query; import com.google.common.collect.Maps; import org.bukkit.Chunk; import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import java.util.ArrayList; import java.util.Collections; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; @@ -24,8 +30,10 @@ import java.util.UUID; public final class LoadersHandler implements LoadersManager { - private final Map chunkLoaders = Maps.newConcurrentMap(); + private final Map chunkLoaders = Maps.newConcurrentMap(); private final Map chunkLoadersByChunks = Maps.newConcurrentMap(); + private final Map> chunkLoadersByWorlds = Maps.newConcurrentMap(); + private final Map> unloadedChunkLoadersByWorlds = Maps.newConcurrentMap(); private final Map loadersData = Maps.newConcurrentMap(); private final WildLoadersPlugin plugin; @@ -40,12 +48,12 @@ public final class LoadersHandler implements LoadersManager { @Override public Optional getChunkLoader(Location location) { - return Optional.ofNullable(chunkLoaders.get(location)); + return Optional.ofNullable(chunkLoaders.get(BlockPosition.of(location))); } @Override public List getChunkLoaders() { - return Collections.unmodifiableList(new ArrayList<>(chunkLoaders.values())); + return Collections.unmodifiableList(new LinkedList<>(chunkLoaders.values())); } @Override @@ -60,41 +68,117 @@ public final class LoadersHandler implements LoadersManager { @Override public ChunkLoader addChunkLoader(LoaderData loaderData, Player whoPlaced, Location location, long timeLeft) { - WChunkLoader chunkLoader = addChunkLoaderWithoutDBSave(loaderData, whoPlaced.getUniqueId(), location, timeLeft, - ChunkLoaderChunks.calculateChunks(loaderData, whoPlaced.getUniqueId(), location)); + BlockPosition blockPosition = BlockPosition.of(location); + + WChunkLoader chunkLoader = addChunkLoaderWithoutDBSave(loaderData, whoPlaced.getUniqueId(), + location, timeLeft, false); Query.INSERT_CHUNK_LOADER.insertParameters() - .setLocation(location) + .setLocation(blockPosition) .setObject(whoPlaced.getUniqueId().toString()) .setObject(loaderData.getName()) .setObject(timeLeft) - .queue(location); + .queue(blockPosition); return chunkLoader; } - public WChunkLoader addChunkLoaderWithoutDBSave(LoaderData loaderData, UUID placer, Location location, long timeLeft, List loadedChunks) { - WChunkLoader chunkLoader = new WChunkLoader(loaderData, placer, location, loadedChunks.toArray(new Chunk[0]), timeLeft); - chunkLoaders.put(location, chunkLoader); - for (Chunk loadedChunk : chunkLoader.getLoadedChunks()) { + public WChunkLoader addChunkLoaderWithoutDBSave(LoaderData loaderData, UUID placer, Location location, + long timeLeft, boolean validateBlock) { + BlockPosition blockPosition = BlockPosition.of(location); + + if (validateBlock) { + Material blockType = location.getBlock().getType(); + + if (ServerVersion.isLegacy() && blockType == Material.CAULDRON) { + blockType = Material.CAULDRON_ITEM; + } + + if (blockType != loaderData.getLoaderItem().getType()) { + WildLoadersPlugin.log("The chunk-loader at " + blockPosition.serialize() + " is invalid."); + return null; + } + } + + List loadedChunks = ChunkLoaderChunks.calculateChunks(loaderData, placer, location); + loadedChunks.removeIf(chunk -> plugin.getLoaders().getChunkLoader(chunk).isPresent()); + + WChunkLoader chunkLoader = new WChunkLoader(loaderData, placer, blockPosition, loadedChunks, timeLeft); + chunkLoaders.put(blockPosition, chunkLoader); + chunkLoadersByWorlds.computeIfAbsent(blockPosition.getWorldName(), i -> new LinkedList<>()).add(chunkLoader); + for (Chunk loadedChunk : chunkLoader.getLoadedChunksCollection()) { chunkLoadersByChunks.put(ChunkPosition.of(loadedChunk), chunkLoader); } - plugin.getNPCs().createNPC(location); + plugin.getNPCs().createNPC(blockPosition); return chunkLoader; } + public void addUnloadedChunkLoader(LoaderData loaderData, UUID placer, BlockPosition blockPosition, long timeLeft) { + UnloadedChunkLoader unloadedChunkLoader = new UnloadedChunkLoader(loaderData, placer, blockPosition, timeLeft); + unloadedChunkLoadersByWorlds.computeIfAbsent(blockPosition.getWorldName(), i -> new LinkedList<>()) + .add(unloadedChunkLoader); + } + + public void loadUnloadedChunkLoaders(World world) { + List unloadedChunkLoaders = this.unloadedChunkLoadersByWorlds.remove(world.getName()); + if (unloadedChunkLoaders == null || unloadedChunkLoaders.isEmpty()) + return; + + unloadedChunkLoaders.forEach(unloadedChunkLoader -> { + Location location = unloadedChunkLoader.getBlockPosition().getLocation(); + if (location.getWorld() != world) + throw new IllegalStateException(); + + addChunkLoaderWithoutDBSave(unloadedChunkLoader.getLoaderData(), unloadedChunkLoader.getPlacer(), + location, unloadedChunkLoader.getTimeLeft(), true); + }); + + } + + public void unloadWorld(World world) { + List worldChunkLoaders = this.chunkLoadersByWorlds.remove(world.getName()); + if (worldChunkLoaders == null || worldChunkLoaders.isEmpty()) + return; + + List unloadedChunkLoaders = new LinkedList<>(); + + worldChunkLoaders.forEach(chunkLoader -> { + plugin.getNMSAdapter().removeLoader(chunkLoader, false); + BlockPosition blockPosition = removeChunkLoaderWithoutDBSave(chunkLoader); + UnloadedChunkLoader unloadedChunkLoader = new UnloadedChunkLoader(chunkLoader.getLoaderData(), + chunkLoader.getWhoPlaced().getUniqueId(), blockPosition, chunkLoader.getTimeLeft()); + unloadedChunkLoaders.add(unloadedChunkLoader); + + Query.UPDATE_CHUNK_LOADER_TIME_LEFT.insertParameters() + .setObject(unloadedChunkLoader.getTimeLeft()) + .setLocation(blockPosition) + .queue(blockPosition); + }); + + this.unloadedChunkLoadersByWorlds.put(world.getName(), unloadedChunkLoaders); + } + @Override public void removeChunkLoader(ChunkLoader chunkLoader) { - Location location = chunkLoader.getLocation(); - chunkLoaders.remove(location); - for (Chunk loadedChunk : chunkLoader.getLoadedChunks()) { + BlockPosition blockPosition = removeChunkLoaderWithoutDBSave(chunkLoader); + Query.DELETE_CHUNK_LOADER.insertParameters() + .setLocation(blockPosition) + .queue(blockPosition); + } + + private BlockPosition removeChunkLoaderWithoutDBSave(ChunkLoader chunkLoader) { + BlockPosition blockPosition = BlockPosition.of(chunkLoader.getLocation()); + chunkLoaders.remove(blockPosition); + for (Chunk loadedChunk : chunkLoader.getLoadedChunksCollection()) { chunkLoadersByChunks.remove(ChunkPosition.of(loadedChunk)); } - chunkLoader.getNPC().ifPresent(npc -> plugin.getNPCs().killNPC(npc)); - Query.DELETE_CHUNK_LOADER.insertParameters() - .setLocation(location) - .queue(location); + List worldChunkLoaders = chunkLoadersByWorlds.get(blockPosition.getWorldName()); + if (worldChunkLoaders != null) + worldChunkLoaders.remove(chunkLoader); + + chunkLoader.getNPC().ifPresent(npc -> plugin.getNPCs().killNPC(npc)); + return blockPosition; } @Override @@ -114,5 +198,6 @@ public final class LoadersHandler implements LoadersManager { chunkLoaders.values().forEach(chunkLoader -> plugin.getNMSAdapter().removeLoader(chunkLoader, false)); chunkLoaders.clear(); chunkLoadersByChunks.clear(); + chunkLoadersByWorlds.clear(); } } diff --git a/src/main/java/com/bgsoftware/wildloaders/handlers/NPCHandler.java b/src/main/java/com/bgsoftware/wildloaders/handlers/NPCHandler.java index 509ff51..0092768 100644 --- a/src/main/java/com/bgsoftware/wildloaders/handlers/NPCHandler.java +++ b/src/main/java/com/bgsoftware/wildloaders/handlers/NPCHandler.java @@ -3,7 +3,7 @@ package com.bgsoftware.wildloaders.handlers; import com.bgsoftware.wildloaders.WildLoadersPlugin; import com.bgsoftware.wildloaders.api.managers.NPCManager; import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; -import com.bgsoftware.wildloaders.npc.NPCIdentifier; +import com.bgsoftware.wildloaders.utils.BlockPosition; import com.bgsoftware.wildloaders.utils.database.Query; import com.google.common.collect.Maps; import org.bukkit.Location; @@ -21,8 +21,8 @@ public final class NPCHandler implements NPCManager { private static int NPCS_COUNTER = 0; private final WildLoadersPlugin plugin; - private final Map npcs = Maps.newConcurrentMap(); - private final Map npcUUIDs = Maps.newConcurrentMap(); + private final Map npcs = Maps.newConcurrentMap(); + private final Map npcUUIDs = Maps.newConcurrentMap(); public NPCHandler(WildLoadersPlugin plugin) { @@ -31,13 +31,21 @@ public final class NPCHandler implements NPCManager { @Override public Optional getNPC(Location location) { - return Optional.ofNullable(npcs.get(new NPCIdentifier(location))); + return getNPC(BlockPosition.of(location)); + } + + public Optional getNPC(BlockPosition blockPosition) { + return Optional.ofNullable(npcs.get(blockPosition)); } @Override public ChunkLoaderNPC createNPC(Location location) { - return npcs.computeIfAbsent(new NPCIdentifier(location), i -> { - ChunkLoaderNPC npc = plugin.getNMSAdapter().createNPC(i.getSpawnLocation(), getUUID(i)); + return createNPC(BlockPosition.of(location)); + } + + public ChunkLoaderNPC createNPC(BlockPosition blockPosition) { + return npcs.computeIfAbsent(blockPosition, i -> { + ChunkLoaderNPC npc = plugin.getNMSAdapter().createNPC(i.getLocation(), getUUID(i)); Entity npcEntity = npc.getPlayer(); npcEntity.setMetadata("NPC", new FixedMetadataValue(plugin, true)); return npc; @@ -52,13 +60,13 @@ public final class NPCHandler implements NPCManager { @Override public void killNPC(ChunkLoaderNPC npc) { - NPCIdentifier identifier = new NPCIdentifier(npc.getLocation()); - npcs.remove(identifier); + BlockPosition blockPosition = BlockPosition.of(npc.getLocation()); - npcUUIDs.remove(identifier); + npcs.remove(blockPosition); + npcUUIDs.remove(blockPosition); Query.DELETE_NPC_IDENTIFIER.insertParameters() - .setLocation(identifier.getSpawnLocation()) + .setLocation(blockPosition) .queue(npc.getUniqueId()); Entity npcEntity = npc.getPlayer(); @@ -76,28 +84,27 @@ public final class NPCHandler implements NPCManager { npcs.clear(); } - public Map getNPCs() { + public Map getNPCs() { return Collections.unmodifiableMap(npcs); } - public void registerUUID(Location location, UUID uuid) { - npcUUIDs.put(new NPCIdentifier(location), uuid); + public void registerUUID(BlockPosition blockPosition, UUID uuid) { + npcUUIDs.put(blockPosition, uuid); } - private UUID getUUID(NPCIdentifier identifier) { - if (npcUUIDs.containsKey(identifier)) - return npcUUIDs.get(identifier); - - UUID uuid; + private UUID getUUID(BlockPosition blockPosition) { + UUID uuid = npcUUIDs.get(blockPosition); + if (uuid != null) + return uuid; do { uuid = UUID.randomUUID(); } while (npcUUIDs.containsValue(uuid)); - npcUUIDs.put(identifier, uuid); + npcUUIDs.put(blockPosition, uuid); Query.INSERT_NPC_IDENTIFIER.insertParameters() - .setLocation(identifier.getSpawnLocation()) + .setLocation(blockPosition) .setObject(uuid.toString()) .queue(uuid); diff --git a/src/main/java/com/bgsoftware/wildloaders/handlers/ProvidersHandler.java b/src/main/java/com/bgsoftware/wildloaders/handlers/ProvidersHandler.java index 4a1f7ae..c980311 100644 --- a/src/main/java/com/bgsoftware/wildloaders/handlers/ProvidersHandler.java +++ b/src/main/java/com/bgsoftware/wildloaders/handlers/ProvidersHandler.java @@ -93,7 +93,7 @@ public final class ProvidersHandler implements ProvidersManager { return false; } - public void tick(Chunk[] chunks) { + public void tick(List chunks) { tickableProviders.forEach(tickableProvider -> tickableProvider.tick(chunks)); } diff --git a/src/main/java/com/bgsoftware/wildloaders/listeners/ChunksListener.java b/src/main/java/com/bgsoftware/wildloaders/listeners/ChunksListener.java index e899b66..ee8b5bc 100644 --- a/src/main/java/com/bgsoftware/wildloaders/listeners/ChunksListener.java +++ b/src/main/java/com/bgsoftware/wildloaders/listeners/ChunksListener.java @@ -5,22 +5,35 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.world.ChunkUnloadEvent; +import org.bukkit.event.world.WorldLoadEvent; +import org.bukkit.event.world.WorldUnloadEvent; @SuppressWarnings("unused") public final class ChunksListener implements Listener { private final WildLoadersPlugin plugin; - public ChunksListener(WildLoadersPlugin plugin){ + public ChunksListener(WildLoadersPlugin plugin) { this.plugin = plugin; } @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) - public void onChunkUnload(ChunkUnloadEvent e){ + public void onChunkUnload(ChunkUnloadEvent e) { try { if (plugin.getLoaders().getChunkLoader(e.getChunk()).isPresent()) e.setCancelled(true); - }catch (Throwable ignored){} + } catch (Throwable ignored) { + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onWorldLoad(WorldLoadEvent e) { + plugin.getLoaders().loadUnloadedChunkLoaders(e.getWorld()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onWorldUnload(WorldUnloadEvent e) { + plugin.getLoaders().unloadWorld(e.getWorld()); } } diff --git a/src/main/java/com/bgsoftware/wildloaders/loaders/UnloadedChunkLoader.java b/src/main/java/com/bgsoftware/wildloaders/loaders/UnloadedChunkLoader.java new file mode 100644 index 0000000..644e48c --- /dev/null +++ b/src/main/java/com/bgsoftware/wildloaders/loaders/UnloadedChunkLoader.java @@ -0,0 +1,38 @@ +package com.bgsoftware.wildloaders.loaders; + +import com.bgsoftware.wildloaders.api.loaders.LoaderData; +import com.bgsoftware.wildloaders.utils.BlockPosition; + +import java.util.UUID; + +public class UnloadedChunkLoader { + + private final LoaderData loaderData; + private final UUID placer; + private final BlockPosition blockPosition; + private final long timeLeft; + + public UnloadedChunkLoader(LoaderData loaderData, UUID placer, BlockPosition blockPosition, long timeLeft) { + this.loaderData = loaderData; + this.placer = placer; + this.blockPosition = blockPosition; + this.timeLeft = timeLeft; + } + + public LoaderData getLoaderData() { + return loaderData; + } + + public UUID getPlacer() { + return placer; + } + + public BlockPosition getBlockPosition() { + return blockPosition; + } + + public long getTimeLeft() { + return timeLeft; + } + +} diff --git a/src/main/java/com/bgsoftware/wildloaders/loaders/WChunkLoader.java b/src/main/java/com/bgsoftware/wildloaders/loaders/WChunkLoader.java index bc04431..bcbe37a 100644 --- a/src/main/java/com/bgsoftware/wildloaders/loaders/WChunkLoader.java +++ b/src/main/java/com/bgsoftware/wildloaders/loaders/WChunkLoader.java @@ -5,7 +5,7 @@ import com.bgsoftware.wildloaders.api.holograms.Hologram; import com.bgsoftware.wildloaders.api.loaders.ChunkLoader; import com.bgsoftware.wildloaders.api.loaders.LoaderData; import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC; -import com.bgsoftware.wildloaders.utils.ChunkLoaderChunks; +import com.bgsoftware.wildloaders.utils.BlockPosition; import com.bgsoftware.wildloaders.utils.database.Query; import com.bgsoftware.wildloaders.utils.threads.Executor; import org.bukkit.Bukkit; @@ -16,6 +16,7 @@ import org.bukkit.OfflinePlayer; import org.bukkit.inventory.ItemStack; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -25,18 +26,18 @@ public final class WChunkLoader implements ChunkLoader { private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin(); private final UUID whoPlaced; - private final Location location; - private final Chunk[] loadedChunks; + private final BlockPosition blockPosition; + private final List loadedChunks; private final String loaderName; private final ITileEntityChunkLoader tileEntityChunkLoader; private boolean active = true; private long timeLeft; - public WChunkLoader(LoaderData loaderData, UUID whoPlaced, Location location, Chunk[] loadedChunks, long timeLeft) { + public WChunkLoader(LoaderData loaderData, UUID whoPlaced, BlockPosition blockPosition, List loadedChunks, long timeLeft) { this.loaderName = loaderData.getName(); this.whoPlaced = whoPlaced; - this.location = location.clone(); + this.blockPosition = blockPosition; this.loadedChunks = loadedChunks; this.timeLeft = timeLeft; this.tileEntityChunkLoader = plugin.getNMSAdapter().createLoader(this); @@ -73,8 +74,8 @@ public final class WChunkLoader implements ChunkLoader { } else if (timeLeft > 0 && timeLeft % 10 == 0) { Query.UPDATE_CHUNK_LOADER_TIME_LEFT.insertParameters() .setObject(timeLeft) - .setLocation(location) - .queue(location); + .setLocation(this.blockPosition) + .queue(this.blockPosition); } } } @@ -85,17 +86,23 @@ public final class WChunkLoader implements ChunkLoader { @Override public Location getLocation() { - return location.clone(); + return this.blockPosition.getLocation(); } @Override + @Deprecated public Chunk[] getLoadedChunks() { - return loadedChunks; + return loadedChunks.toArray(new Chunk[0]); + } + + @Override + public Collection getLoadedChunksCollection() { + return Collections.unmodifiableCollection(loadedChunks); } @Override public Optional getNPC() { - return plugin.getNPCs().getNPC(location); + return plugin.getNPCs().getNPC(this.blockPosition); } @Override diff --git a/src/main/java/com/bgsoftware/wildloaders/npc/NPCIdentifier.java b/src/main/java/com/bgsoftware/wildloaders/npc/NPCIdentifier.java deleted file mode 100644 index 836a0a5..0000000 --- a/src/main/java/com/bgsoftware/wildloaders/npc/NPCIdentifier.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.bgsoftware.wildloaders.npc; - -import org.bukkit.Location; - -import java.util.Objects; - -public final class NPCIdentifier { - - private final Object identifier; - - public NPCIdentifier(Location location){ - this.identifier = getBlockLocation(location); - } - - public Location getSpawnLocation(){ - return (Location) identifier; - } - - @Override - public String toString() { - return identifier.toString(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - NPCIdentifier that = (NPCIdentifier) o; - return Objects.equals(identifier, that.identifier); - } - - @Override - public int hashCode() { - return Objects.hash(identifier); - } - - private static Location getBlockLocation(Location location){ - return new Location(location.getWorld(), location.getBlockX(), location.getBlockY(), location.getBlockZ()); - } - -} diff --git a/src/main/java/com/bgsoftware/wildloaders/utils/BlockPosition.java b/src/main/java/com/bgsoftware/wildloaders/utils/BlockPosition.java new file mode 100644 index 0000000..a405ad0 --- /dev/null +++ b/src/main/java/com/bgsoftware/wildloaders/utils/BlockPosition.java @@ -0,0 +1,92 @@ +package com.bgsoftware.wildloaders.utils; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; + +import java.lang.ref.WeakReference; +import java.util.Objects; + +public class BlockPosition { + + private final String worldName; + private final int x; + private final int y; + private final int z; + private WeakReference cachedWorld = new WeakReference<>(null); + + public static BlockPosition of(Location location) { + World world = location.getWorld(); + return new BlockPosition(world == null ? "null" : world.getName(), + location.getBlockX(), location.getBlockY(), location.getBlockZ()); + } + + public static BlockPosition deserialize(String serialized) { + String[] locationSections = serialized.split(","); + + if (locationSections.length != 4) + throw new IllegalArgumentException("Cannot parse location " + serialized); + + String worldName = locationSections[0]; + int x = (int) Double.parseDouble(locationSections[1]); + int y = (int) Double.parseDouble(locationSections[2]); + int z = (int) Double.parseDouble(locationSections[3]); + + return new BlockPosition(worldName, x, y, z); + } + + public BlockPosition(String worldName, int x, int y, int z) { + this.worldName = worldName; + this.x = x; + this.y = y; + this.z = z; + } + + public String getWorldName() { + return worldName; + } + + public World getWorld() { + World cachedWorld = this.cachedWorld.get(); + if (cachedWorld == null) { + cachedWorld = Bukkit.getWorld(this.worldName); + this.cachedWorld = new WeakReference<>(cachedWorld); + } + + return cachedWorld; + } + + public Location getLocation() { + return new Location(getWorld(), this.x, this.y, this.z); + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public int getZ() { + return z; + } + + public String serialize() { + return this.worldName + "," + this.x + "," + this.y + "," + this.z; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BlockPosition that = (BlockPosition) o; + return x == that.x && y == that.y && z == that.z && Objects.equals(worldName, that.worldName); + } + + @Override + public int hashCode() { + return Objects.hash(worldName, x, y, z); + } + +} diff --git a/src/main/java/com/bgsoftware/wildloaders/utils/database/QueryParameters.java b/src/main/java/com/bgsoftware/wildloaders/utils/database/QueryParameters.java index 83ef0c1..4c91be7 100644 --- a/src/main/java/com/bgsoftware/wildloaders/utils/database/QueryParameters.java +++ b/src/main/java/com/bgsoftware/wildloaders/utils/database/QueryParameters.java @@ -1,5 +1,6 @@ package com.bgsoftware.wildloaders.utils.database; +import com.bgsoftware.wildloaders.utils.BlockPosition; import org.bukkit.Location; import java.sql.PreparedStatement; @@ -12,7 +13,7 @@ public final class QueryParameters { private final Query query; private final List parameters; - public QueryParameters(Query query){ + public QueryParameters(Query query) { this.query = query; this.parameters = new ArrayList<>(query.getParametersCount()); } @@ -22,20 +23,24 @@ public final class QueryParameters { } public void executeQuery(PreparedStatement preparedStatement) throws SQLException { - for(int i = 0; i < parameters.size(); i++) + for (int i = 0; i < parameters.size(); i++) preparedStatement.setObject(i + 1, parameters.get(i)); } - public void queue(Object caller){ + public void queue(Object caller) { DatabaseQueue.queue(caller, this); } - public QueryParameters setLocation(Location loc){ + public QueryParameters setLocation(Location loc) { return setObject(loc.getWorld().getName() + "," + loc.getBlockX() + "," + loc.getBlockY() + "," + loc.getBlockZ()); } - public QueryParameters setObject(Object object){ - if(object instanceof Location) + public QueryParameters setLocation(BlockPosition blockPos) { + return setObject(blockPos.getWorldName() + "," + blockPos.getX() + "," + blockPos.getY() + "," + blockPos.getZ()); + } + + public QueryParameters setObject(Object object) { + if (object instanceof Location) return setLocation((Location) object); parameters.add(object); diff --git a/src/main/java/com/bgsoftware/wildloaders/utils/locations/LocationUtils.java b/src/main/java/com/bgsoftware/wildloaders/utils/locations/LocationUtils.java deleted file mode 100644 index 8a9efb5..0000000 --- a/src/main/java/com/bgsoftware/wildloaders/utils/locations/LocationUtils.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.bgsoftware.wildloaders.utils.locations; - -import org.bukkit.Bukkit; -import org.bukkit.Location; - -public final class LocationUtils { - - private LocationUtils(){} - - public static String getLocation(Location location){ - try { - return location.getWorld().getName() + "," + location.getBlockX() + "," + location.getBlockY() + "," + location.getBlockZ(); - }catch (Exception ex){ - System.out.println(location); - throw ex; - } - } - - public static Location getLocation(String location){ - String[] locationSections = location.split(","); - - if(locationSections.length != 4) - throw new IllegalArgumentException("Cannot parse location " + location); - - String worldName = locationSections[0]; - double x = parseDouble(locationSections[1]); - double y = parseDouble(locationSections[2]); - double z = parseDouble(locationSections[3]); - - return new Location(Bukkit.getWorld(worldName), x, y, z); - } - - private static double parseDouble(String str){ - try{ - return Double.parseDouble(str); - }catch (Exception ex){ - throw new IllegalArgumentException("Cannot parse double " + str); - } - } - -}