From fee9029bf0e90094dc5453e91add495251d4c16c Mon Sep 17 00:00:00 2001 From: Jordan Date: Thu, 27 Jun 2024 15:15:14 +0200 Subject: [PATCH] Add a BiomeCategories API (#2338) (#2777) * Add a BiomeCategories API (#2338) * Add a BiomeCategories API * licenses * Use a supplier to retain the lazy-load & dynamicness of the existing system, but still retaining the inversion of control that this PR was intended to provide * Minor fawe adapter cleanup * Actually add the new files? * Fixes --------- Co-authored-by: Maddy Miller --- .../ext/fawe/v1_19_R3/PaperweightAdapter.java | 40 ++ .../PaperweightWorldNativeAccess.java | 6 + .../PaperweightFaweWorldNativeAccess.java | 6 + .../ext/fawe/v1_20_R1/PaperweightAdapter.java | 57 ++- .../PaperweightWorldNativeAccess.java | 6 + .../PaperweightFaweWorldNativeAccess.java | 6 + .../ext/fawe/v1_20_R2/PaperweightAdapter.java | 258 ++++++++---- .../fawe/v1_20_R2/PaperweightFakePlayer.java | 2 +- .../PaperweightWorldNativeAccess.java | 23 +- .../PaperweightFaweWorldNativeAccess.java | 6 + .../ext.fawe/v1_20_R3/PaperweightAdapter.java | 253 ++++++++---- .../v1_20_R3/PaperweightFakePlayer.java | 2 +- .../PaperweightWorldNativeAccess.java | 15 +- .../PaperweightFaweWorldNativeAccess.java | 6 + .../ext.fawe/v1_20_R4/PaperweightAdapter.java | 368 ++++++++++-------- .../v1_20_R4/PaperweightFakePlayer.java | 2 +- .../PaperweightWorldNativeAccess.java | 27 +- .../PaperweightFaweWorldNativeAccess.java | 6 + .../adapter/IDelegateBukkitImplAdapter.java | 21 + .../bukkit/adapter/BukkitImplAdapter.java | 43 +- .../clipboard/io/FastSchematicReader.java | 4 - .../internal/wna/WorldNativeAccess.java | 4 +- .../sk89q/worldedit/registry/Category.java | 24 +- .../world/biome/BiomeCategories.java | 112 ++++++ .../worldedit/world/biome/BiomeCategory.java | 49 +++ 25 files changed, 975 insertions(+), 371 deletions(-) create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeCategories.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeCategory.java diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_19_R3/PaperweightAdapter.java b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_19_R3/PaperweightAdapter.java index a5c3da0e1..5b696c8f6 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_19_R3/PaperweightAdapter.java +++ b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_19_R3/PaperweightAdapter.java @@ -57,6 +57,7 @@ import com.sk89q.worldedit.util.formatting.text.TranslatableComponent; import com.sk89q.worldedit.util.io.file.SafeFiles; import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.RegenOptions; +import com.sk89q.worldedit.world.biome.BiomeCategory; import com.sk89q.worldedit.world.biome.BiomeType; import com.sk89q.worldedit.world.biome.BiomeTypes; import com.sk89q.worldedit.world.block.BaseBlock; @@ -67,6 +68,9 @@ import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.item.ItemType; import net.minecraft.Util; import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderSet; +import net.minecraft.core.Registry; import net.minecraft.core.registries.Registries; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; import net.minecraft.network.protocol.game.ClientboundEntityEventPacket; @@ -292,6 +296,14 @@ public final class PaperweightAdapter implements BukkitImplAdapter biomeRegistry = server.registryAccess().registryOrThrow(Registries.BIOME); + biomeRegistry.getTagNames().forEach(tagKey -> { + String key = tagKey.location().toString(); + if (BiomeCategory.REGISTRY.get(key) == null) { + BiomeCategory.REGISTRY.register(key, new BiomeCategory( + key, + () -> biomeRegistry.getTag(tagKey) + .stream() + .flatMap(HolderSet.Named::stream) + .map(Holder::value) + .map(this::adapt) + .collect(Collectors.toSet())) + ); + } + }); + } + // ------------------------------------------------------------------------ // Code that is less likely to break // ------------------------------------------------------------------------ diff --git a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_19_R3/PaperweightWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_19_R3/PaperweightWorldNativeAccess.java index 7923c9e69..424422ce6 100644 --- a/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_19_R3/PaperweightWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1_19_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_19_R3/PaperweightWorldNativeAccess.java @@ -154,6 +154,12 @@ public class PaperweightWorldNativeAccess implements WorldNativeAccess (net.minecraft.nbt.CompoundTag) fromNative(nbtData) - )); - }*/ - @Override public void sendFakeOP(Player player) { ((CraftPlayer) player).getHandle().connection.send(new ClientboundEntityEventPacket( @@ -858,7 +873,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter biomeRegistry = server.registryAccess().registryOrThrow(Registries.BIOME); + biomeRegistry.getTagNames().forEach(tagKey -> { + String key = tagKey.location().toString(); + if (BiomeCategory.REGISTRY.get(key) == null) { + BiomeCategory.REGISTRY.register(key, new BiomeCategory( + key, + () -> biomeRegistry.getTag(tagKey) + .stream() + .flatMap(HolderSet.Named::stream) + .map(Holder::value) + .map(this::adapt) + .collect(Collectors.toSet())) + ); + } + }); + } // ------------------------------------------------------------------------ // Code that is less likely to break // ------------------------------------------------------------------------ diff --git a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R1/PaperweightWorldNativeAccess.java b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R1/PaperweightWorldNativeAccess.java index c7f3d3f3c..ffca9e50d 100644 --- a/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R1/PaperweightWorldNativeAccess.java +++ b/worldedit-bukkit/adapters/adapter-1_20/src/main/java/com/sk89q/worldedit/bukkit/adapter/ext/fawe/v1_20_R1/PaperweightWorldNativeAccess.java @@ -154,6 +154,12 @@ public class PaperweightWorldNativeAccess implements WorldNativeAccess> 4, z >> 4); final BlockPos blockPos = new BlockPos(x, y, z); final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); - int internalId = Block.getId(blockData); - BlockState state = BlockStateIdAccess.getBlockStateById(internalId); - if (state == null) { - org.bukkit.block.Block bukkitBlock = location.getBlock(); - state = BukkitAdapter.adapt(bukkitBlock.getBlockData()); - } - - return state; + return adapt(blockData); } @Override @@ -357,8 +381,40 @@ public final class PaperweightAdapter implements BukkitImplAdapter, BiomeType> biomeTypeFromNMSCache = new HashMap<>(); @Override - public WorldNativeAccess createWorldNativeAccess(org.bukkit.World world) { - return new PaperweightWorldNativeAccess(this, new WeakReference<>(((CraftWorld) world).getHandle())); + public BiomeType getBiome(Location location) { + checkNotNull(location); + + CraftWorld craftWorld = ((CraftWorld) location.getWorld()); + int x = location.getBlockX(); + int y = location.getBlockY(); + int z = location.getBlockZ(); + + final ServerLevel handle = craftWorld.getHandle(); + LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); + + return biomeTypeFromNMSCache.computeIfAbsent(chunk.getNoiseBiome(x >> 2, y >> 2, z >> 2), b -> BiomeType.REGISTRY.get(b.unwrapKey().get().location().toString())); + } + + @Override + public void setBiome(Location location, BiomeType biome) { + checkNotNull(location); + checkNotNull(biome); + + CraftWorld craftWorld = ((CraftWorld) location.getWorld()); + int x = location.getBlockX(); + int y = location.getBlockY(); + int z = location.getBlockZ(); + + final ServerLevel handle = craftWorld.getHandle(); + LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); + chunk.setBiome(x >> 2, y >> 2, z >> 2, biomeTypeToNMSCache.computeIfAbsent(biome, b -> ((CraftServer) Bukkit.getServer()).getServer().registryAccess().registryOrThrow(Registries.BIOME).getHolderOrThrow(ResourceKey.create(Registries.BIOME, new ResourceLocation(b.id()))))); + chunk.setUnsaved(true); + } + + @Override + public WorldNativeAccess createWorldNativeAccess(World world) { + return new PaperweightWorldNativeAccess(this, + new WeakReference<>(((CraftWorld) world).getHandle())); } private static net.minecraft.core.Direction adapt(Direction face) { @@ -381,13 +437,13 @@ public final class PaperweightAdapter implements BukkitImplAdapter stateContainer, - net.minecraft.world.level.block.state.BlockState newState, - Map, Object> states + StateDefinition stateContainer, + net.minecraft.world.level.block.state.BlockState newState, + Map, Object> states ) { for (Map.Entry, Object> state : states.entrySet()) { net.minecraft.world.level.block.state.properties.Property property = - stateContainer.getProperty(state.getKey().getName()); + stateContainer.getProperty(state.getKey().getName()); Comparable value = (Comparable) state.getValue(); // we may need to adapt this value, depending on the source prop if (property instanceof DirectionProperty) { @@ -396,16 +452,16 @@ public final class PaperweightAdapter implements BukkitImplAdapter) property) - .getValue(enumName).orElseThrow(() -> - new IllegalStateException( - "Enum property " + property.getName() + " does not contain " + enumName - ) - ); + .getValue(enumName).orElseThrow(() -> + new IllegalStateException( + "Enum property " + property.getName() + " does not contain " + enumName + ) + ); } newState = newState.setValue( - (net.minecraft.world.level.block.state.properties.Property) property, - (Comparable) value + (net.minecraft.world.level.block.state.properties.Property) property, + (Comparable) value ); } return newState; @@ -502,10 +558,10 @@ public final class PaperweightAdapter implements BukkitImplAdapter) state.getPossibleValues().stream().map(e -> Direction.valueOf(((StringRepresentable) e).getSerializedName().toUpperCase(Locale.ROOT))).collect(Collectors.toList())); + (List) state.getPossibleValues().stream().map(e -> Direction.valueOf(((StringRepresentable) e).getSerializedName().toUpperCase(Locale.ROOT))).toList()); } else if (state instanceof net.minecraft.world.level.block.state.properties.EnumProperty) { return new EnumProperty(state.getName(), - (List) state.getPossibleValues().stream().map(e -> ((StringRepresentable) e).getSerializedName()).collect(Collectors.toList())); + (List) state.getPossibleValues().stream().map(e -> ((StringRepresentable) e).getSerializedName()).toList()); } else if (state instanceof net.minecraft.world.level.block.state.properties.IntegerProperty) { return new IntegerProperty(state.getName(), ImmutableList.copyOf(state.getPossibleValues())); } else { @@ -520,7 +576,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter> properties = new TreeMap<>(); Block block = getBlockFromType(blockType); StateDefinition blockStateList = - block.getStateDefinition(); + block.getStateDefinition(); for (net.minecraft.world.level.block.state.properties.Property state : blockStateList.getProperties()) { Property property = PROPERTY_CACHE.getUnchecked(state); properties.put(property.getName(), property); @@ -542,15 +598,15 @@ public final class PaperweightAdapter implements BukkitImplAdapter fakePlayers - = CacheBuilder.newBuilder().weakKeys().softValues().build(CacheLoader.from(PaperweightFakePlayer::new)); + = CacheBuilder.newBuilder().weakKeys().softValues().build(CacheLoader.from(PaperweightFakePlayer::new)); @Override public boolean simulateItemUse(org.bukkit.World world, BlockVector3 position, BaseItem item, Direction face) { @@ -583,7 +639,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter map = (Map) serverWorldsField.get(Bukkit.getServer()); + Map map = (Map) serverWorldsField.get(Bukkit.getServer()); map.remove("faweregentempworld"); } catch (IllegalAccessException ignored) { } @@ -712,7 +769,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter { // bail out early if a future fails if (chunkLoadings.stream().anyMatch(ftr -> - ftr.isDone() && Futures.getUnchecked(ftr) == null + ftr.isDone() && Futures.getUnchecked(ftr) == null )) { return false; } @@ -758,9 +815,9 @@ public final class PaperweightAdapter implements BukkitImplAdapter>) - getChunkFutureMethod.invoke(chunkManager, chunk.x(), chunk.z(), ChunkStatus.FEATURES, true)) - .thenApply(either -> either.left().orElse(null)) + ((CompletableFuture>) + getChunkFutureMethod.invoke(chunkManager, chunk.x(), chunk.z(), ChunkStatus.FEATURES, true)) + .thenApply(either -> either.left().orElse(null)) ); } catch (IllegalAccessException | InvocationTargetException e) { throw new IllegalStateException("Couldn't load chunk for regen.", e); @@ -782,12 +839,12 @@ public final class PaperweightAdapter implements BukkitImplAdapter SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet( - SideEffect.NEIGHBORS, - SideEffect.LIGHTING, - SideEffect.VALIDATION, - SideEffect.ENTITY_AI, - SideEffect.EVENTS, - SideEffect.UPDATE + SideEffect.NEIGHBORS, + SideEffect.LIGHTING, + SideEffect.VALIDATION, + SideEffect.ENTITY_AI, + SideEffect.EVENTS, + SideEffect.UPDATE ); @Override @@ -796,7 +853,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter biomeRegistry = server.registryAccess().registryOrThrow(Registries.BIOME); + biomeRegistry.getTagNames().forEach(tagKey -> { + String key = tagKey.location().toString(); + if (BiomeCategory.REGISTRY.get(key) == null) { + BiomeCategory.REGISTRY.register(key, new BiomeCategory( + key, + () -> biomeRegistry.getTag(tagKey) + .stream() + .flatMap(HolderSet.Named::stream) + .map(Holder::value) + .map(this::adapt) + .collect(Collectors.toSet())) + ); + } + }); + } + + @Override + public void sendBiomeUpdates(World world, Iterable chunks) { + ServerLevel originalWorld = ((CraftWorld) world).getHandle(); + + List nativeChunks = chunks instanceof Collection chunkCollection ? Lists.newArrayListWithCapacity(chunkCollection.size()) : Lists.newArrayList(); + for (BlockVector2 chunk : chunks) { + nativeChunks.add(originalWorld.getChunk(chunk.x(), chunk.z(), ChunkStatus.BIOMES, false)); + } + originalWorld.getChunkSource().chunkMap.resendBiomesForChunks(nativeChunks); + } + // ------------------------------------------------------------------------ // Code that is less likely to break // ------------------------------------------------------------------------ @@ -978,7 +1074,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter> 4, z >> 4); final BlockPos blockPos = new BlockPos(x, y, z); final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); - int internalId = Block.getId(blockData); - BlockState state = BlockStateIdAccess.getBlockStateById(internalId); - if (state == null) { - org.bukkit.block.Block bukkitBlock = location.getBlock(); - state = BukkitAdapter.adapt(bukkitBlock.getBlockData()); - } - - return state; + return adapt(blockData); } @Override @@ -357,7 +381,38 @@ public final class PaperweightAdapter implements BukkitImplAdapter, BiomeType> biomeTypeFromNMSCache = new HashMap<>(); @Override - public WorldNativeAccess createWorldNativeAccess(org.bukkit.World world) { + public BiomeType getBiome(Location location) { + checkNotNull(location); + + CraftWorld craftWorld = ((CraftWorld) location.getWorld()); + int x = location.getBlockX(); + int y = location.getBlockY(); + int z = location.getBlockZ(); + + final ServerLevel handle = craftWorld.getHandle(); + LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); + + return biomeTypeFromNMSCache.computeIfAbsent(chunk.getNoiseBiome(x >> 2, y >> 2, z >> 2), b -> BiomeType.REGISTRY.get(b.unwrapKey().get().location().toString())); + } + + @Override + public void setBiome(Location location, BiomeType biome) { + checkNotNull(location); + checkNotNull(biome); + + CraftWorld craftWorld = ((CraftWorld) location.getWorld()); + int x = location.getBlockX(); + int y = location.getBlockY(); + int z = location.getBlockZ(); + + final ServerLevel handle = craftWorld.getHandle(); + LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); + chunk.setBiome(x >> 2, y >> 2, z >> 2, biomeTypeToNMSCache.computeIfAbsent(biome, b -> ((CraftServer) Bukkit.getServer()).getServer().registryAccess().registryOrThrow(Registries.BIOME).getHolderOrThrow(ResourceKey.create(Registries.BIOME, new ResourceLocation(b.id()))))); + chunk.setUnsaved(true); + } + + @Override + public WorldNativeAccess createWorldNativeAccess(World world) { return new PaperweightWorldNativeAccess(this, new WeakReference<>(((CraftWorld) world).getHandle())); } @@ -381,13 +436,13 @@ public final class PaperweightAdapter implements BukkitImplAdapter stateContainer, - net.minecraft.world.level.block.state.BlockState newState, - Map, Object> states + StateDefinition stateContainer, + net.minecraft.world.level.block.state.BlockState newState, + Map, Object> states ) { for (Map.Entry, Object> state : states.entrySet()) { net.minecraft.world.level.block.state.properties.Property property = - stateContainer.getProperty(state.getKey().getName()); + stateContainer.getProperty(state.getKey().getName()); Comparable value = (Comparable) state.getValue(); // we may need to adapt this value, depending on the source prop if (property instanceof DirectionProperty) { @@ -396,16 +451,16 @@ public final class PaperweightAdapter implements BukkitImplAdapter) property) - .getValue(enumName).orElseThrow(() -> - new IllegalStateException( - "Enum property " + property.getName() + " does not contain " + enumName - ) - ); + .getValue(enumName).orElseThrow(() -> + new IllegalStateException( + "Enum property " + property.getName() + " does not contain " + enumName + ) + ); } newState = newState.setValue( - (net.minecraft.world.level.block.state.properties.Property) property, - (Comparable) value + (net.minecraft.world.level.block.state.properties.Property) property, + (Comparable) value ); } return newState; @@ -502,10 +557,10 @@ public final class PaperweightAdapter implements BukkitImplAdapter) state.getPossibleValues().stream().map(e -> Direction.valueOf(((StringRepresentable) e).getSerializedName().toUpperCase(Locale.ROOT))).collect(Collectors.toList())); + (List) state.getPossibleValues().stream().map(e -> Direction.valueOf(((StringRepresentable) e).getSerializedName().toUpperCase(Locale.ROOT))).toList()); } else if (state instanceof net.minecraft.world.level.block.state.properties.EnumProperty) { return new EnumProperty(state.getName(), - (List) state.getPossibleValues().stream().map(e -> ((StringRepresentable) e).getSerializedName()).collect(Collectors.toList())); + (List) state.getPossibleValues().stream().map(e -> ((StringRepresentable) e).getSerializedName()).toList()); } else if (state instanceof net.minecraft.world.level.block.state.properties.IntegerProperty) { return new IntegerProperty(state.getName(), ImmutableList.copyOf(state.getPossibleValues())); } else { @@ -520,7 +575,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter> properties = new TreeMap<>(); Block block = getBlockFromType(blockType); StateDefinition blockStateList = - block.getStateDefinition(); + block.getStateDefinition(); for (net.minecraft.world.level.block.state.properties.Property state : blockStateList.getProperties()) { Property property = PROPERTY_CACHE.getUnchecked(state); properties.put(property.getName(), property); @@ -542,15 +597,15 @@ public final class PaperweightAdapter implements BukkitImplAdapter map = (Map) serverWorldsField.get(Bukkit.getServer()); + Map map = (Map) serverWorldsField.get(Bukkit.getServer()); map.remove("faweregentempworld"); } catch (IllegalAccessException ignored) { } @@ -712,7 +768,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter { // bail out early if a future fails if (chunkLoadings.stream().anyMatch(ftr -> - ftr.isDone() && Futures.getUnchecked(ftr) == null + ftr.isDone() && Futures.getUnchecked(ftr) == null )) { return false; } @@ -758,9 +814,9 @@ public final class PaperweightAdapter implements BukkitImplAdapter>) - getChunkFutureMethod.invoke(chunkManager, chunk.x(), chunk.z(), ChunkStatus.FEATURES, true)) - .thenApply(either -> either.left().orElse(null)) + ((CompletableFuture>) + getChunkFutureMethod.invoke(chunkManager, chunk.x(), chunk.z(), ChunkStatus.FEATURES, true)) + .thenApply(either -> either.left().orElse(null)) ); } catch (IllegalAccessException | InvocationTargetException e) { throw new IllegalStateException("Couldn't load chunk for regen.", e); @@ -782,12 +838,12 @@ public final class PaperweightAdapter implements BukkitImplAdapter SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet( - SideEffect.NEIGHBORS, - SideEffect.LIGHTING, - SideEffect.VALIDATION, - SideEffect.ENTITY_AI, - SideEffect.EVENTS, - SideEffect.UPDATE + SideEffect.NEIGHBORS, + SideEffect.LIGHTING, + SideEffect.VALIDATION, + SideEffect.ENTITY_AI, + SideEffect.EVENTS, + SideEffect.UPDATE ); @Override @@ -796,7 +852,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter biomeRegistry = server.registryAccess().registryOrThrow(Registries.BIOME); + biomeRegistry.getTagNames().forEach(tagKey -> { + String key = tagKey.location().toString(); + if (BiomeCategory.REGISTRY.get(key) == null) { + BiomeCategory.REGISTRY.register(key, new BiomeCategory( + key, + () -> biomeRegistry.getTag(tagKey) + .stream() + .flatMap(HolderSet.Named::stream) + .map(Holder::value) + .map(this::adapt) + .collect(Collectors.toSet())) + ); + } + }); + } + + @Override + public void sendBiomeUpdates(World world, Iterable chunks) { + ServerLevel originalWorld = ((CraftWorld) world).getHandle(); + + List nativeChunks = chunks instanceof Collection chunkCollection ? Lists.newArrayListWithCapacity(chunkCollection.size()) : Lists.newArrayList(); + for (BlockVector2 chunk : chunks) { + nativeChunks.add(originalWorld.getChunk(chunk.x(), chunk.z(), ChunkStatus.BIOMES, false)); + } + originalWorld.getChunkSource().chunkMap.resendBiomesForChunks(nativeChunks); + } + // ------------------------------------------------------------------------ // Code that is less likely to break // ------------------------------------------------------------------------ @@ -978,7 +1073,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter> 4, z >> 4); final BlockPos blockPos = new BlockPos(x, y, z); final net.minecraft.world.level.block.state.BlockState blockData = chunk.getBlockState(blockPos); - int internalId = Block.getId(blockData); - BlockState state = BlockStateIdAccess.getBlockStateById(internalId); - if (state == null) { - org.bukkit.block.Block bukkitBlock = location.getBlock(); - state = BukkitAdapter.adapt(bukkitBlock.getBlockData()); - } - - return state; + return adapt(blockData); } @Override @@ -387,7 +389,38 @@ public final class PaperweightAdapter implements BukkitImplAdapter, BiomeType> biomeTypeFromNMSCache = new HashMap<>(); @Override - public WorldNativeAccess createWorldNativeAccess(org.bukkit.World world) { + public BiomeType getBiome(Location location) { + checkNotNull(location); + + CraftWorld craftWorld = ((CraftWorld) location.getWorld()); + int x = location.getBlockX(); + int y = location.getBlockY(); + int z = location.getBlockZ(); + + final ServerLevel handle = craftWorld.getHandle(); + LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); + + return biomeTypeFromNMSCache.computeIfAbsent(chunk.getNoiseBiome(x >> 2, y >> 2, z >> 2), b -> BiomeType.REGISTRY.get(b.unwrapKey().get().location().toString())); + } + + @Override + public void setBiome(Location location, BiomeType biome) { + checkNotNull(location); + checkNotNull(biome); + + CraftWorld craftWorld = ((CraftWorld) location.getWorld()); + int x = location.getBlockX(); + int y = location.getBlockY(); + int z = location.getBlockZ(); + + final ServerLevel handle = craftWorld.getHandle(); + LevelChunk chunk = handle.getChunk(x >> 4, z >> 4); + chunk.setBiome(x >> 2, y >> 2, z >> 2, biomeTypeToNMSCache.computeIfAbsent(biome, b -> ((CraftServer) Bukkit.getServer()).getServer().registryAccess().registryOrThrow(Registries.BIOME).getHolderOrThrow(ResourceKey.create(Registries.BIOME, new ResourceLocation(b.id()))))); + chunk.setUnsaved(true); + } + + @Override + public WorldNativeAccess createWorldNativeAccess(World world) { return new PaperweightWorldNativeAccess(this, new WeakReference<>(((CraftWorld) world).getHandle())); } @@ -411,13 +444,13 @@ public final class PaperweightAdapter implements BukkitImplAdapter stateContainer, - net.minecraft.world.level.block.state.BlockState newState, - Map, Object> states + StateDefinition stateContainer, + net.minecraft.world.level.block.state.BlockState newState, + Map, Object> states ) { for (Map.Entry, Object> state : states.entrySet()) { net.minecraft.world.level.block.state.properties.Property property = - stateContainer.getProperty(state.getKey().getName()); + stateContainer.getProperty(state.getKey().getName()); Comparable value = (Comparable) state.getValue(); // we may need to adapt this value, depending on the source prop if (property instanceof DirectionProperty) { @@ -426,16 +459,16 @@ public final class PaperweightAdapter implements BukkitImplAdapter) property) - .getValue(enumName).orElseThrow(() -> - new IllegalStateException( - "Enum property " + property.getName() + " does not contain " + enumName - ) - ); + .getValue(enumName).orElseThrow(() -> + new IllegalStateException( + "Enum property " + property.getName() + " does not contain " + enumName + ) + ); } newState = newState.setValue( - (net.minecraft.world.level.block.state.properties.Property) property, - (Comparable) value + (net.minecraft.world.level.block.state.properties.Property) property, + (Comparable) value ); } return newState; @@ -486,7 +519,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter> PROPERTY_CACHE = CacheBuilder - .newBuilder() - .build(new CacheLoader>() { - @Override - public Property load(net.minecraft.world.level.block.state.properties.Property state) throws Exception { - if (state instanceof net.minecraft.world.level.block.state.properties.BooleanProperty) { - return new BooleanProperty(state.getName(), ImmutableList.copyOf(state.getPossibleValues())); - } else if (state instanceof DirectionProperty) { - return new DirectionalProperty( - state.getName(), - (List) state - .getPossibleValues() - .stream() - .map(e -> Direction.valueOf(((StringRepresentable) e) - .getSerializedName() - .toUpperCase(Locale.ROOT))) - .collect(Collectors.toList()) - ); - } else if (state instanceof net.minecraft.world.level.block.state.properties.EnumProperty) { - return new EnumProperty( - state.getName(), - (List) state - .getPossibleValues() - .stream() - .map(e -> ((StringRepresentable) e).getSerializedName()) - .collect(Collectors.toList()) - ); - } else if (state instanceof net.minecraft.world.level.block.state.properties.IntegerProperty) { - return new IntegerProperty(state.getName(), ImmutableList.copyOf(state.getPossibleValues())); - } else { - throw new IllegalArgumentException("WorldEdit needs an update to support " + state - .getClass() - .getSimpleName()); - } - } - }); + @SuppressWarnings({ "unchecked", "rawtypes" }) + private static final LoadingCache> PROPERTY_CACHE = CacheBuilder.newBuilder().build(new CacheLoader>() { + @Override + public Property load(net.minecraft.world.level.block.state.properties.Property state) throws Exception { + if (state instanceof net.minecraft.world.level.block.state.properties.BooleanProperty) { + return new BooleanProperty(state.getName(), ImmutableList.copyOf(state.getPossibleValues())); + } else if (state instanceof DirectionProperty) { + return new DirectionalProperty(state.getName(), + (List) state.getPossibleValues().stream().map(e -> Direction.valueOf(((StringRepresentable) e).getSerializedName().toUpperCase(Locale.ROOT))).toList()); + } else if (state instanceof net.minecraft.world.level.block.state.properties.EnumProperty) { + return new EnumProperty(state.getName(), + (List) state.getPossibleValues().stream().map(e -> ((StringRepresentable) e).getSerializedName()).toList()); + } else if (state instanceof net.minecraft.world.level.block.state.properties.IntegerProperty) { + return new IntegerProperty(state.getName(), ImmutableList.copyOf(state.getPossibleValues())); + } else { + throw new IllegalArgumentException("WorldEdit needs an update to support " + state.getClass().getSimpleName()); + } + } + }); - @SuppressWarnings({"rawtypes"}) + @SuppressWarnings({ "rawtypes" }) @Override public Map> getProperties(BlockType blockType) { Map> properties = new TreeMap<>(); Block block = getBlockFromType(blockType); StateDefinition blockStateList = - block.getStateDefinition(); + block.getStateDefinition(); for (net.minecraft.world.level.block.state.properties.Property state : blockStateList.getProperties()) { Property property = PROPERTY_CACHE.getUnchecked(state); properties.put(property.getName(), property); @@ -579,8 +594,8 @@ public final class PaperweightAdapter implements BukkitImplAdapter fakePlayers - = CacheBuilder.newBuilder().weakKeys().softValues().build(CacheLoader.from(PaperweightFakePlayer::new)); + = CacheBuilder.newBuilder().weakKeys().softValues().build(CacheLoader.from(PaperweightFakePlayer::new)); @Override - public boolean simulateItemUse(org.bukkit.World world, BlockVector3 position, BaseItem item, Direction face) { + public boolean simulateItemUse(World world, BlockVector3 position, BaseItem item, Direction face) { CraftWorld craftWorld = (CraftWorld) world; ServerLevel worldServer = craftWorld.getHandle(); ItemStack stack = CraftItemStack.asNMSCopy(adapt( - item instanceof BaseItemStack - ? ((BaseItemStack) item) - : new BaseItemStack(item.getType(), item.getNbtReference(), 1) + item instanceof BaseItemStack + ? ((BaseItemStack) item) + : new BaseItemStack(item.getType(), item.getNbtReference(), 1) )); PaperweightFakePlayer fakePlayer; @@ -647,8 +662,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter map = (Map) serverWorldsField.get(Bukkit.getServer()); + Map map = (Map) serverWorldsField.get(Bukkit.getServer()); map.remove("faweregentempworld"); } catch (IllegalAccessException ignored) { } @@ -777,8 +781,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter> chunkLoadings = submitChunkLoadTasks(region, serverWorld); BlockableEventLoop executor; try { @@ -789,7 +792,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter { // bail out early if a future fails if (chunkLoadings.stream().anyMatch(ftr -> - ftr.isDone() && Futures.getUnchecked(ftr) == null + ftr.isDone() && Futures.getUnchecked(ftr) == null )) { return false; } @@ -835,9 +838,9 @@ public final class PaperweightAdapter implements BukkitImplAdapter>) - getChunkFutureMethod.invoke(chunkManager, chunk.x(), chunk.z(), ChunkStatus.FEATURES, true)) - .thenApply(either -> either.orElse(null)) + ((CompletableFuture>) + getChunkFutureMethod.invoke(chunkManager, chunk.x(), chunk.z(), ChunkStatus.FEATURES, true)) + .thenApply(either -> either.orElse(null)) ); } catch (IllegalAccessException | InvocationTargetException e) { throw new IllegalStateException("Couldn't load chunk for regen.", e); @@ -859,12 +862,12 @@ public final class PaperweightAdapter implements BukkitImplAdapter SUPPORTED_SIDE_EFFECTS = Sets.immutableEnumSet( - SideEffect.NEIGHBORS, - SideEffect.LIGHTING, - SideEffect.VALIDATION, - SideEffect.ENTITY_AI, - SideEffect.EVENTS, - SideEffect.UPDATE + SideEffect.NEIGHBORS, + SideEffect.LIGHTING, + SideEffect.VALIDATION, + SideEffect.ENTITY_AI, + SideEffect.EVENTS, + SideEffect.UPDATE ); @Override @@ -873,7 +876,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter biomeRegistry = server.registryAccess().registryOrThrow(Registries.BIOME); + biomeRegistry.getTagNames().forEach(tagKey -> { + String key = tagKey.location().toString(); + if (BiomeCategory.REGISTRY.get(key) == null) { + BiomeCategory.REGISTRY.register(key, new BiomeCategory( + key, + () -> biomeRegistry.getTag(tagKey) + .stream() + .flatMap(HolderSet.Named::stream) + .map(Holder::value) + .map(this::adapt) + .collect(Collectors.toSet())) + ); + } + }); + } + + @Override + public void sendBiomeUpdates(World world, Iterable chunks) { + ServerLevel originalWorld = ((CraftWorld) world).getHandle(); + + List nativeChunks = chunks instanceof Collection chunkCollection ? Lists.newArrayListWithCapacity(chunkCollection.size()) : Lists.newArrayList(); + for (BlockVector2 chunk : chunks) { + nativeChunks.add(originalWorld.getChunk(chunk.x(), chunk.z(), ChunkStatus.BIOMES, false)); + } + originalWorld.getChunkSource().chunkMap.resendBiomesForChunks(nativeChunks); + } + // ------------------------------------------------------------------------ // Code that is less likely to break // ------------------------------------------------------------------------ @@ -1022,7 +1064,6 @@ public final class PaperweightAdapter implements BukkitImplAdapter extends BukkitImplAdapter { return getParent().getInternalBlockStateId(state); } + @Override + default boolean clearContainerBlockContents(World world, BlockVector3 pt) { + return getParent().clearContainerBlockContents(world, pt); + } + + @Override + default void setBiome(Location location, BiomeType biome) { + getParent().setBiome(location, biome); + } + + @Override + default BiomeType getBiome(Location location) { + return getParent().getBiome(location); + } + + @Override + default void sendBiomeUpdates(World world, Iterable chunks) { + getParent().sendBiomeUpdates(world, chunks); + } + @Override default BlockMaterial getMaterial(BlockType blockType) { return getParent().getMaterial(blockType); diff --git a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java index d656e874a..8fee3b417 100644 --- a/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java +++ b/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/adapter/BukkitImplAdapter.java @@ -35,6 +35,7 @@ import com.sk89q.worldedit.bukkit.BukkitAdapter; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.internal.wna.WorldNativeAccess; +import com.sk89q.worldedit.math.BlockVector2; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.registry.state.Property; @@ -110,7 +111,7 @@ public interface BukkitImplAdapter extends IBukkitAdapter { BlockState getBlock(Location location); /** - * Get the block at the given location. + * Get the block with NBT data at the given location. * * @param location the location * @return the block @@ -280,6 +281,46 @@ public interface BukkitImplAdapter extends IBukkitAdapter { throw new UnsupportedOperationException("This adapter does not support clearing block contents."); } + /** + * Set the biome at a location. + * + * @param location the location + * @param biome the new biome + */ + default void setBiome(Location location, BiomeType biome) { + throw new UnsupportedOperationException("This adapter does not support custom biomes."); + } + + /** + * Gets the current biome at a location. + * + * @param location the location + * @return the biome + */ + default BiomeType getBiome(Location location) { + throw new UnsupportedOperationException("This adapter does not support custom biomes."); + } + + /** + * Initialize registries that require NMS access. + */ + default void initializeRegistries() { + + } + + /** + * Sends biome updates for the given chunks. + * + *

This doesn't modify biomes at all, it just sends the current state of the biomes + * in the world to all of the nearby players, updating the visual representation of the + * biomes on their clients.

+ * + * @param world the world + * @param chunks a list of chunk coordinates to send biome updates for + */ + default void sendBiomeUpdates(World world, Iterable chunks) { + } + //FAWE start default BlockMaterial getMaterial(BlockType blockType) { return getMaterial(blockType.getDefaultState()); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/io/FastSchematicReader.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/io/FastSchematicReader.java index b32d42c04..71d139789 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/io/FastSchematicReader.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/extent/clipboard/io/FastSchematicReader.java @@ -109,26 +109,22 @@ public class FastSchematicReader extends NBTSchematicReader { if (fixer == null || dataVersion == -1) { return tag; } - //FAWE start - LinTag return (CompoundTag) LinBusConverter.fromLinBus(fixer.fixUp( DataFixer.FixTypes.BLOCK_ENTITY, tag.toLinTag(), dataVersion )); - //FAWE end } private CompoundTag fixEntity(CompoundTag tag) { if (fixer == null || dataVersion == -1) { return tag; } - //FAWE start - LinTag return (CompoundTag) LinBusConverter.fromLinBus(fixer.fixUp( DataFixer.FixTypes.ENTITY, tag.toLinTag(), dataVersion )); - //FAWE end } private String fixBiome(String biomePalettePart) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/WorldNativeAccess.java b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/WorldNativeAccess.java index 56cab805f..097db6964 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/WorldNativeAccess.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/internal/wna/WorldNativeAccess.java @@ -146,7 +146,9 @@ public interface WorldNativeAccess { void markBlockChanged(NC chunk, NP position); - void notifyNeighbors(NP pos, NBS oldState, NBS newState); + void notifyNeighbors(NP pos, NBS oldState, NBS newState);; + + void updateBlock(NP pos, NBS oldState, NBS newState); void updateNeighbors(NP pos, NBS oldState, NBS newState, int recursionLimit); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Category.java b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Category.java index 92a859652..e18499c04 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Category.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/registry/Category.java @@ -23,17 +23,25 @@ import com.fastasyncworldedit.core.registry.RegistryItem; import java.util.HashSet; import java.util.Set; +import java.util.function.Supplier; //FAWE start - implements RegistryItem public abstract class Category implements RegistryItem, Keyed { //FAWE end private final Set set = new HashSet<>(); + private final Supplier> supplier; protected final String id; private boolean empty = true; - protected Category(final String id) { + public Category(final String id) { this.id = id; + this.supplier = null; + } + + public Category(String id, Supplier> contentSupplier) { + this.id = id; + this.supplier = contentSupplier; } @Override @@ -43,7 +51,11 @@ public abstract class Category implements RegistryItem, Keyed { public final Set getAll() { if (this.empty) { - this.set.addAll(this.load()); + if (supplier != null) { + this.set.addAll(this.supplier.get()); + } else { + this.set.addAll(this.load()); + } this.empty = false; } return this.set; @@ -62,6 +74,14 @@ public abstract class Category implements RegistryItem, Keyed { return internalId; } + /** + * Loads the contents of this category from the platform. + * + * @return The loaded contents of the category + * @deprecated The load system will be removed in a future WorldEdit release. The registries should be populated by + * the platforms via the supplier constructor. + */ + @Deprecated protected abstract Set load(); //FAWE end diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeCategories.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeCategories.java new file mode 100644 index 000000000..ee1cca1c0 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeCategories.java @@ -0,0 +1,112 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.world.biome; + +/** + * Stores a list of common {@link BiomeCategory BiomeCategories}. + * + * @see BiomeCategory + */ +@SuppressWarnings("unused") +public final class BiomeCategories { + public static final BiomeCategory ALLOWS_SURFACE_SLIME_SPAWNS = get("minecraft:allows_surface_slime_spawns"); + public static final BiomeCategory ALLOWS_TROPICAL_FISH_SPAWNS_AT_ANY_HEIGHT = get("minecraft:allows_tropical_fish_spawns_at_any_height"); + public static final BiomeCategory HAS_CLOSER_WATER_FOG = get("minecraft:has_closer_water_fog"); + public static final BiomeCategory HAS_STRUCTURE_ANCIENT_CITY = get("minecraft:has_structure/ancient_city"); + public static final BiomeCategory HAS_STRUCTURE_BASTION_REMNANT = get("minecraft:has_structure/bastion_remnant"); + public static final BiomeCategory HAS_STRUCTURE_BURIED_TREASURE = get("minecraft:has_structure/buried_treasure"); + public static final BiomeCategory HAS_STRUCTURE_DESERT_PYRAMID = get("minecraft:has_structure/desert_pyramid"); + public static final BiomeCategory HAS_STRUCTURE_END_CITY = get("minecraft:has_structure/end_city"); + public static final BiomeCategory HAS_STRUCTURE_IGLOO = get("minecraft:has_structure/igloo"); + public static final BiomeCategory HAS_STRUCTURE_JUNGLE_TEMPLE = get("minecraft:has_structure/jungle_temple"); + public static final BiomeCategory HAS_STRUCTURE_MINESHAFT = get("minecraft:has_structure/mineshaft"); + public static final BiomeCategory HAS_STRUCTURE_MINESHAFT_MESA = get("minecraft:has_structure/mineshaft_mesa"); + public static final BiomeCategory HAS_STRUCTURE_NETHER_FORTRESS = get("minecraft:has_structure/nether_fortress"); + public static final BiomeCategory HAS_STRUCTURE_NETHER_FOSSIL = get("minecraft:has_structure/nether_fossil"); + public static final BiomeCategory HAS_STRUCTURE_OCEAN_MONUMENT = get("minecraft:has_structure/ocean_monument"); + public static final BiomeCategory HAS_STRUCTURE_OCEAN_RUIN_COLD = get("minecraft:has_structure/ocean_ruin_cold"); + public static final BiomeCategory HAS_STRUCTURE_OCEAN_RUIN_WARM = get("minecraft:has_structure/ocean_ruin_warm"); + public static final BiomeCategory HAS_STRUCTURE_PILLAGER_OUTPOST = get("minecraft:has_structure/pillager_outpost"); + public static final BiomeCategory HAS_STRUCTURE_RUINED_PORTAL_DESERT = get("minecraft:has_structure/ruined_portal_desert"); + public static final BiomeCategory HAS_STRUCTURE_RUINED_PORTAL_JUNGLE = get("minecraft:has_structure/ruined_portal_jungle"); + public static final BiomeCategory HAS_STRUCTURE_RUINED_PORTAL_MOUNTAIN = get("minecraft:has_structure/ruined_portal_mountain"); + public static final BiomeCategory HAS_STRUCTURE_RUINED_PORTAL_NETHER = get("minecraft:has_structure/ruined_portal_nether"); + public static final BiomeCategory HAS_STRUCTURE_RUINED_PORTAL_OCEAN = get("minecraft:has_structure/ruined_portal_ocean"); + public static final BiomeCategory HAS_STRUCTURE_RUINED_PORTAL_STANDARD = get("minecraft:has_structure/ruined_portal_standard"); + public static final BiomeCategory HAS_STRUCTURE_RUINED_PORTAL_SWAMP = get("minecraft:has_structure/ruined_portal_swamp"); + public static final BiomeCategory HAS_STRUCTURE_SHIPWRECK = get("minecraft:has_structure/shipwreck"); + public static final BiomeCategory HAS_STRUCTURE_SHIPWRECK_BEACHED = get("minecraft:has_structure/shipwreck_beached"); + public static final BiomeCategory HAS_STRUCTURE_STRONGHOLD = get("minecraft:has_structure/stronghold"); + public static final BiomeCategory HAS_STRUCTURE_SWAMP_HUT = get("minecraft:has_structure/swamp_hut"); + public static final BiomeCategory HAS_STRUCTURE_TRAIL_RUINS = get("minecraft:has_structure/trail_ruins"); + public static final BiomeCategory HAS_STRUCTURE_VILLAGE_DESERT = get("minecraft:has_structure/village_desert"); + public static final BiomeCategory HAS_STRUCTURE_VILLAGE_PLAINS = get("minecraft:has_structure/village_plains"); + public static final BiomeCategory HAS_STRUCTURE_VILLAGE_SAVANNA = get("minecraft:has_structure/village_savanna"); + public static final BiomeCategory HAS_STRUCTURE_VILLAGE_SNOWY = get("minecraft:has_structure/village_snowy"); + public static final BiomeCategory HAS_STRUCTURE_VILLAGE_TAIGA = get("minecraft:has_structure/village_taiga"); + public static final BiomeCategory HAS_STRUCTURE_WOODLAND_MANSION = get("minecraft:has_structure/woodland_mansion"); + public static final BiomeCategory INCREASED_FIRE_BURNOUT = get("minecraft:increased_fire_burnout"); + public static final BiomeCategory IS_BADLANDS = get("minecraft:is_badlands"); + public static final BiomeCategory IS_BEACH = get("minecraft:is_beach"); + public static final BiomeCategory IS_DEEP_OCEAN = get("minecraft:is_deep_ocean"); + public static final BiomeCategory IS_END = get("minecraft:is_end"); + public static final BiomeCategory IS_FOREST = get("minecraft:is_forest"); + public static final BiomeCategory IS_HILL = get("minecraft:is_hill"); + public static final BiomeCategory IS_JUNGLE = get("minecraft:is_jungle"); + public static final BiomeCategory IS_MOUNTAIN = get("minecraft:is_mountain"); + public static final BiomeCategory IS_NETHER = get("minecraft:is_nether"); + public static final BiomeCategory IS_OCEAN = get("minecraft:is_ocean"); + public static final BiomeCategory IS_OVERWORLD = get("minecraft:is_overworld"); + public static final BiomeCategory IS_RIVER = get("minecraft:is_river"); + public static final BiomeCategory IS_SAVANNA = get("minecraft:is_savanna"); + public static final BiomeCategory IS_TAIGA = get("minecraft:is_taiga"); + public static final BiomeCategory MINESHAFT_BLOCKING = get("minecraft:mineshaft_blocking"); + public static final BiomeCategory MORE_FREQUENT_DROWNED_SPAWNS = get("minecraft:more_frequent_drowned_spawns"); + public static final BiomeCategory PLAYS_UNDERWATER_MUSIC = get("minecraft:plays_underwater_music"); + public static final BiomeCategory POLAR_BEARS_SPAWN_ON_ALTERNATE_BLOCKS = get("minecraft:polar_bears_spawn_on_alternate_blocks"); + public static final BiomeCategory PRODUCES_CORALS_FROM_BONEMEAL = get("minecraft:produces_corals_from_bonemeal"); + public static final BiomeCategory REDUCE_WATER_AMBIENT_SPAWNS = get("minecraft:reduce_water_ambient_spawns"); + public static final BiomeCategory REQUIRED_OCEAN_MONUMENT_SURROUNDING = get("minecraft:required_ocean_monument_surrounding"); + public static final BiomeCategory SNOW_GOLEM_MELTS = get("minecraft:snow_golem_melts"); + public static final BiomeCategory SPAWNS_COLD_VARIANT_FROGS = get("minecraft:spawns_cold_variant_frogs"); + public static final BiomeCategory SPAWNS_GOLD_RABBITS = get("minecraft:spawns_gold_rabbits"); + public static final BiomeCategory SPAWNS_SNOW_FOXES = get("minecraft:spawns_snow_foxes"); + public static final BiomeCategory SPAWNS_WARM_VARIANT_FROGS = get("minecraft:spawns_warm_variant_frogs"); + public static final BiomeCategory SPAWNS_WHITE_RABBITS = get("minecraft:spawns_white_rabbits"); + public static final BiomeCategory STRONGHOLD_BIASED_TO = get("minecraft:stronghold_biased_to"); + public static final BiomeCategory WATER_ON_MAP_OUTLINES = get("minecraft:water_on_map_outlines"); + public static final BiomeCategory WITHOUT_PATROL_SPAWNS = get("minecraft:without_patrol_spawns"); + public static final BiomeCategory WITHOUT_WANDERING_TRADER_SPAWNS = get("minecraft:without_wandering_trader_spawns"); + public static final BiomeCategory WITHOUT_ZOMBIE_SIEGES = get("minecraft:without_zombie_sieges"); + + private BiomeCategories() { + } + + /** + * Gets the {@link BiomeCategory} associated with the given id. + */ + public static BiomeCategory get(String id) { + BiomeCategory entry = BiomeCategory.REGISTRY.get(id); + if (entry == null) { + return new BiomeCategory(id); + } + return entry; + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeCategory.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeCategory.java new file mode 100644 index 000000000..a51a86823 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/biome/BiomeCategory.java @@ -0,0 +1,49 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.world.biome; + +import com.sk89q.worldedit.registry.Category; +import com.sk89q.worldedit.registry.Keyed; +import com.sk89q.worldedit.registry.NamespacedRegistry; + +import java.util.Set; +import java.util.function.Supplier; + +/** + * A category of biomes. + */ +public class BiomeCategory extends Category implements Keyed { + + public static final NamespacedRegistry REGISTRY = new NamespacedRegistry<>("biome tag", true); + + public BiomeCategory(final String id) { + super(id); + } + + public BiomeCategory(final String id, final Supplier> contentSupplier) { + super(id, contentSupplier); + } + + @Override + protected Set load() { + return Set.of(); + } + +}