Created
February 6, 2024 23:47
-
-
Save IllusionTheDev/7c3a524d75677a5523f38a176a710dcc to your computer and use it in GitHub Desktop.
World storage example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.List; | |
import java.util.UUID; | |
import java.util.concurrent.CompletableFuture; | |
public interface BlockStorage<T extends WorldBlock> extends Storage { | |
CompletableFuture<List<T>> fetchChunkData(UUID worldId, Long chunkId); | |
CompletableFuture<Void> insertData(UUID worldId, Long chunkId, T data); | |
CompletableFuture<Void> deleteChunkData(UUID worldId, Long chunkId); | |
CompletableFuture<Void> deleteChunkData(UUID worldId, Long chunkId, T data); | |
CompletableFuture<Void> deleteChunkData(UUID worldId); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.bukkit.event.EventHandler; | |
import org.bukkit.event.EventPriority; | |
import org.bukkit.event.Listener; | |
import org.bukkit.event.block.BlockBreakEvent; | |
import org.bukkit.event.world.ChunkLoadEvent; | |
import org.bukkit.event.world.ChunkUnloadEvent; | |
import org.bukkit.event.world.WorldUnloadEvent; | |
public class BlockStorageListener implements Listener { | |
private final WorldBlockStorage<?> blockStorage; | |
public BlockStorageListener(WorldBlockStorage<?> blockStorage) { | |
this.blockStorage = blockStorage; | |
} | |
@EventHandler(priority = EventPriority.MONITOR) | |
private void onBreak(BlockBreakEvent event) { | |
blockStorage.removeData(event.getBlock().getLocation()); | |
} | |
@EventHandler | |
private void onLoad(ChunkLoadEvent event) { | |
blockStorage.loadChunk(event.getChunk()); | |
} | |
@EventHandler | |
private void onUnload(ChunkUnloadEvent event) { | |
blockStorage.unloadChunk(event.getChunk()); | |
} | |
@EventHandler | |
private void onWorldUnload(WorldUnloadEvent event) { | |
blockStorage.unloadWorld(event.getWorld().getUID()); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.Collection; | |
import java.util.Map; | |
import java.util.Set; | |
import java.util.concurrent.ConcurrentHashMap; | |
public class ChunkDataContainer<T> { | |
private final Map<Integer, T> storage = new ConcurrentHashMap<>(); | |
public T getAt(int x, int y, int z) { | |
return storage.get(getBlockKey(x, y, z)); | |
} | |
public void setAt(int x, int y, int z, T data) { | |
storage.put(getBlockKey(x, y, z), data); | |
} | |
public T removeAt(int x, int y, int z) { | |
return storage.remove(getBlockKey(x, y, z)); | |
} | |
public static int getBlockKey(int x, int y, int z) { | |
// x and z are 4 bits | |
// y is 8 bits | |
return ((x & 0xf) << 12) | ((y & 0xff) << 4) | (z & 0xf); | |
} | |
public Collection<T> getAll() { | |
return Set.copyOf(storage.values()); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.Collection; | |
import java.util.Map; | |
import java.util.Set; | |
import java.util.concurrent.ConcurrentHashMap; | |
import org.bukkit.Location; | |
import org.bukkit.util.BoundingBox; | |
public class ChunkDataStorage<T extends WorldBlock> { | |
private final Map<Long, ChunkDataContainer<T>> storage = new ConcurrentHashMap<>(); | |
public ChunkDataContainer<T> getAt(Location location) { | |
return getAt(location.getChunk().getX(), location.getChunk().getZ()); | |
} | |
public ChunkDataContainer<T> getAt(int x, int z) { | |
return storage.computeIfAbsent(getChunkKey(x, z), key -> new ChunkDataContainer<>()); | |
} | |
public ChunkDataContainer<T> getAt(long chunkKey) { | |
return storage.computeIfAbsent(chunkKey, key -> new ChunkDataContainer<>()); | |
} | |
public void unloadChunk(int x, int z) { | |
storage.remove(getChunkKey(x, z)); | |
} | |
public void unloadChunk(Location location) { | |
unloadChunk(location.getChunk().getX(), location.getChunk().getZ()); | |
} | |
public void unloadChunk(long chunkKey) { | |
storage.remove(chunkKey); | |
} | |
public Collection<T> getWithin(BoundingBox box) { | |
return getWithin(box.getMinX(), box.getMinY(), box.getMinZ(), box.getMaxX(), box.getMaxY(), box.getMaxZ()); | |
} | |
public Collection<T> getWithin(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { | |
return getWithin((int) minX, (int) minY, (int) minZ, (int) maxX, (int) maxY, (int) maxZ); | |
} | |
public Collection<T> getWithin(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) { | |
int minChunkX = minX >> 4; | |
int minChunkZ = minZ >> 4; | |
int maxChunkX = maxX >> 4; | |
int maxChunkZ = maxZ >> 4; | |
Set<T> data = ConcurrentHashMap.newKeySet(); | |
for (int x = minChunkX; x <= maxChunkX; x++) { | |
for (int z = minChunkZ; z <= maxChunkZ; z++) { | |
ChunkDataContainer<T> chunkDataContainer = getAt(x, z); | |
if (chunkDataContainer == null) { | |
continue; | |
} | |
for (T block : chunkDataContainer.getAll()) { | |
Location location = block.getLocation().toCenterLocation(); | |
if (location.getX() >= minX && location.getX() <= maxX | |
&& location.getY() >= minY && location.getY() <= maxY | |
&& location.getZ() >= minZ && location.getZ() <= maxZ) { | |
data.add(block); | |
} | |
} | |
} | |
} | |
return data; | |
} | |
public static long getChunkKey(int x, int z) { | |
return ((long) x << 32) | (z & 0xffffffffL); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.bukkit.Location; | |
public interface WorldBlock { | |
Location getLocation(); | |
default int getX() { | |
return getLocation().getBlockX(); | |
} | |
default int getY() { | |
return getLocation().getBlockY(); | |
} | |
default int getZ() { | |
return getLocation().getBlockZ(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.Map; | |
import java.util.UUID; | |
import java.util.concurrent.ConcurrentHashMap; | |
import org.bukkit.Bukkit; | |
import org.bukkit.Chunk; | |
import org.bukkit.Location; | |
import org.bukkit.World; | |
import org.bukkit.plugin.java.JavaPlugin; | |
public class WorldBlockStorage<T extends WorldBlock> { | |
private final Map<UUID, ChunkDataStorage<T>> storage = new ConcurrentHashMap<>(); | |
private final BlockStorage<T> blockStorage; | |
public WorldBlockStorage(JavaPlugin plugin, BlockStorage<T> blockStorage) { | |
this.blockStorage = blockStorage; | |
Bukkit.getPluginManager().registerEvents(new BlockStorageListener(this), plugin); | |
this.loadAll(); | |
} | |
public void loadAll() { | |
for(World world : Bukkit.getWorlds()) { | |
for (Chunk chunk : world.getLoadedChunks()) { | |
loadChunk(chunk); | |
} | |
} | |
} | |
public ChunkDataStorage<T> getChunkDataStorage(UUID worldId) { | |
return storage.computeIfAbsent(worldId, (key) -> new ChunkDataStorage<>()); | |
} | |
public ChunkDataStorage<T> getChunkDataStorage(World world) { | |
return getChunkDataStorage(world.getUID()); | |
} | |
private ChunkDataStorage<T> getChunkDataStorage(Location location) { | |
World world = location.getWorld(); | |
if (world == null) { | |
return null; | |
} | |
return getChunkDataStorage(world); | |
} | |
private ChunkDataContainer<T> getChunkDataContainer(Location location) { | |
ChunkDataStorage<T> chunkDataStorage = getChunkDataStorage(location); | |
if (chunkDataStorage == null) { | |
return null; | |
} | |
return chunkDataStorage.getAt(location); | |
} | |
public T getData(Location location) { | |
ChunkDataContainer<T> chunkDataContainer = getChunkDataContainer(location); | |
if (chunkDataContainer == null) { | |
return null; | |
} | |
return chunkDataContainer.getAt(location.getBlockX(), location.getBlockY(), location.getBlockZ()); | |
} | |
public void setData(T data) { | |
Location location = data.getLocation(); | |
ChunkDataContainer<T> chunkDataContainer = getChunkDataContainer(location); | |
if (chunkDataContainer == null) { | |
return; | |
} | |
chunkDataContainer.setAt(location.getBlockX(), location.getBlockY(), location.getBlockZ(), data); | |
UUID worldId = location.getWorld().getUID(); | |
Long chunkId = ChunkDataStorage.getChunkKey(location.getChunk().getX(), location.getChunk().getZ()); | |
blockStorage.insertData(worldId, chunkId, data); | |
} | |
public void removeData(Location location) { | |
ChunkDataContainer<T> chunkDataContainer = getChunkDataContainer(location); | |
if (chunkDataContainer == null) { | |
return; | |
} | |
T removed = chunkDataContainer.removeAt(location.getBlockX(), location.getBlockY(), location.getBlockZ()); | |
if (removed == null) { | |
return; | |
} | |
UUID worldId = location.getWorld().getUID(); | |
Long chunkId = ChunkDataStorage.getChunkKey(location.getChunk().getX(), location.getChunk().getZ()); | |
blockStorage.deleteChunkData(worldId, chunkId); | |
} | |
public void unloadWorld(UUID worldId) { | |
storage.remove(worldId); | |
} | |
public void loadChunk(Chunk chunk) { | |
UUID worldId = chunk.getWorld().getUID(); | |
Long chunkId = ChunkDataStorage.getChunkKey(chunk.getX(), chunk.getZ()); | |
if (blockStorage == null) { | |
return; | |
} | |
blockStorage.fetchChunkData(worldId, chunkId).thenAccept((data) -> { | |
if (data.isEmpty()) { | |
return; | |
} | |
ChunkDataStorage<T> chunkDataStorage = getChunkDataStorage(worldId); | |
ChunkDataContainer<T> chunkDataContainer = chunkDataStorage.getAt(chunk.getX(), chunk.getZ()); | |
for (T block : data) { | |
chunkDataContainer.setAt(block.getX(), block.getY(), block.getZ(), block); | |
} | |
}); | |
} | |
public void unloadChunk(Chunk chunk) { | |
UUID worldId = chunk.getWorld().getUID(); | |
long chunkId = ChunkDataStorage.getChunkKey(chunk.getX(), chunk.getZ()); | |
getChunkDataStorage(worldId).unloadChunk(chunkId); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment