diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 90dfbd92d..10309b05b 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -87,6 +87,6 @@ object Versions { } object Minestom { - const val minestom = "fb895cb899" + const val minestom = "1_21_5-4d91778331" } } diff --git a/platforms/minestom/example/src/main/java/com/dfsek/terra/minestom/TerraMinestomExample.java b/platforms/minestom/example/src/main/java/com/dfsek/terra/minestom/TerraMinestomExample.java index 1cf95a226..2da66d2c9 100644 --- a/platforms/minestom/example/src/main/java/com/dfsek/terra/minestom/TerraMinestomExample.java +++ b/platforms/minestom/example/src/main/java/com/dfsek/terra/minestom/TerraMinestomExample.java @@ -6,6 +6,7 @@ import net.minestom.server.command.builder.Command; import net.minestom.server.coordinate.Pos; import net.minestom.server.entity.GameMode; import net.minestom.server.event.player.AsyncPlayerConfigurationEvent; +import net.minestom.server.event.player.PlayerDisconnectEvent; import net.minestom.server.event.player.PlayerSpawnEvent; import net.minestom.server.instance.Instance; import net.minestom.server.instance.LightingChunk; @@ -16,7 +17,6 @@ import java.time.Duration; import java.util.concurrent.atomic.AtomicInteger; import com.dfsek.terra.minestom.world.TerraMinestomWorld; -import com.dfsek.terra.minestom.world.TerraMinestomWorldBuilder; public class TerraMinestomExample { @@ -24,6 +24,7 @@ public class TerraMinestomExample { private final MinecraftServer server = MinecraftServer.init(); private Instance instance; private TerraMinestomWorld world; + private final TerraMinestomPlatform platform = new TerraMinestomPlatform(); public void createNewInstance() { instance = MinecraftServer.getInstanceManager().createInstanceContainer(); @@ -31,8 +32,9 @@ public class TerraMinestomExample { } public void attachTerra() { - world = TerraMinestomWorldBuilder.from(instance) + world = platform.worldBuilder(instance) .defaultPack() + .doFineGrainedBiomes(false) .attach(); } @@ -121,12 +123,16 @@ public class TerraMinestomExample { private void regenerate() { instance.sendMessage(Component.text("Regenerating world")); + Instance oldInstance = instance; + platform.reload(); createNewInstance(); attachTerra(); preloadWorldAndMeasure(); MinecraftServer.getConnectionManager().getOnlinePlayers().forEach(player -> player.setInstance(instance, new Pos(0, 100, 0)) ); + + MinecraftServer.getInstanceManager().unregisterInstance(oldInstance); } } } diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/MinestomPlatform.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/TerraMinestomPlatform.java similarity index 50% rename from platforms/minestom/src/main/java/com/dfsek/terra/minestom/MinestomPlatform.java rename to platforms/minestom/src/main/java/com/dfsek/terra/minestom/TerraMinestomPlatform.java index c63378a28..527eb85be 100644 --- a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/MinestomPlatform.java +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/TerraMinestomPlatform.java @@ -2,48 +2,78 @@ package com.dfsek.terra.minestom; import com.dfsek.tectonic.api.TypeRegistry; import com.dfsek.tectonic.api.loader.type.TypeLoader; + import com.dfsek.terra.AbstractPlatform; +import com.dfsek.terra.api.addon.BaseAddon; import com.dfsek.terra.api.block.state.BlockState; import com.dfsek.terra.api.entity.EntityType; import com.dfsek.terra.api.event.events.platform.PlatformInitializationEvent; import com.dfsek.terra.api.handle.ItemHandle; import com.dfsek.terra.api.handle.WorldHandle; import com.dfsek.terra.api.world.biome.PlatformBiome; +import com.dfsek.terra.minestom.addon.MinestomAddon; +import com.dfsek.terra.minestom.config.BiomeAdditionsSoundTemplate; +import com.dfsek.terra.minestom.config.BiomeParticleConfigTemplate; import com.dfsek.terra.minestom.biome.MinestomBiomeLoader; +import com.dfsek.terra.minestom.config.KeyLoader; +import com.dfsek.terra.minestom.config.BiomeMoodSoundTemplate; +import com.dfsek.terra.minestom.config.RGBLikeLoader; +import com.dfsek.terra.minestom.config.SoundEventTemplate; import com.dfsek.terra.minestom.entity.MinestomEntityType; import com.dfsek.terra.minestom.item.MinestomItemHandle; import com.dfsek.terra.minestom.world.MinestomChunkGeneratorWrapper; import com.dfsek.terra.minestom.world.MinestomWorldHandle; -import com.dfsek.terra.registry.master.ConfigRegistry.PackLoadFailuresException; +import com.dfsek.terra.minestom.api.TerraMinestomWorldBuilder; + +import net.kyori.adventure.key.Key; +import net.kyori.adventure.util.RGBLike; import net.minestom.server.MinecraftServer; +import net.minestom.server.instance.Instance; +import net.minestom.server.sound.SoundEvent; +import net.minestom.server.world.biome.BiomeEffects; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; -import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +public final class TerraMinestomPlatform extends AbstractPlatform { + private static final Logger LOGGER = LoggerFactory.getLogger(TerraMinestomPlatform.class); + private final WorldHandle worldHandle; + private final ItemHandle itemHandle; + private final TypeLoader biomeTypeLoader; + private final ArrayList platformAddons = new ArrayList<>(List.of(new MinestomAddon(this))); -public final class MinestomPlatform extends AbstractPlatform { - private static final Logger LOGGER = LoggerFactory.getLogger(MinestomPlatform.class); - private static MinestomPlatform INSTANCE = null; - private final MinestomWorldHandle worldHandle = new MinestomWorldHandle(); - private final MinestomItemHandle itemHandle = new MinestomItemHandle(); - - private MinestomPlatform() { + public TerraMinestomPlatform(WorldHandle worldHandle, ItemHandle itemHandle, TypeLoader biomeTypeLoader, BaseAddon... extraAddons) { + this.worldHandle = worldHandle; + this.itemHandle = itemHandle; + this.biomeTypeLoader = biomeTypeLoader; + this.platformAddons.addAll(List.of(extraAddons)); load(); getEventManager().callEvent(new PlatformInitializationEvent()); } + public TerraMinestomPlatform() { + this(new MinestomWorldHandle(), new MinestomItemHandle(), new MinestomBiomeLoader()); + } + @Override public void register(TypeRegistry registry) { super.register(registry); registry - .registerLoader(PlatformBiome.class, new MinestomBiomeLoader()) + .registerLoader(PlatformBiome.class, biomeTypeLoader) + .registerLoader(RGBLike.class, new RGBLikeLoader()) + .registerLoader(Key.class, new KeyLoader()) .registerLoader(EntityType.class, (TypeLoader) (annotatedType, o, configLoader, depthTracker) -> new MinestomEntityType((String) o)) - .registerLoader(BlockState.class, (TypeLoader) (annotatedType, o, configLoader, depthTracker) -> worldHandle.createBlockState((String) o)); + .registerLoader(BlockState.class, (TypeLoader) (annotatedType, o, configLoader, depthTracker) -> worldHandle.createBlockState((String) o)) + .registerLoader(BiomeEffects.Particle.class, BiomeParticleConfigTemplate::new) + .registerLoader(BiomeEffects.MoodSound.class, BiomeMoodSoundTemplate::new) + .registerLoader(BiomeEffects.AdditionsSound.class, BiomeAdditionsSoundTemplate::new) + .registerLoader(SoundEvent.class, SoundEventTemplate::new); } @Override @@ -82,16 +112,22 @@ public final class MinestomPlatform extends AbstractPlatform { @Override public @NotNull File getDataFolder() { String pathName = System.getProperty("terra.datafolder"); - if (pathName == null) pathName = "./terra/"; + if(pathName == null) pathName = "./terra/"; File file = new File(pathName); if(!file.exists()) file.mkdirs(); return file; } - public static MinestomPlatform getInstance() { - if(INSTANCE == null) { - INSTANCE = new MinestomPlatform(); - } - return INSTANCE; + @Override + protected Iterable platformAddon() { + return platformAddons; + } + + public TerraMinestomWorldBuilder worldBuilder(Instance instance) { + return new TerraMinestomWorldBuilder(this, instance); + } + + public TerraMinestomWorldBuilder worldBuilder() { + return new TerraMinestomWorldBuilder(this, MinecraftServer.getInstanceManager().createInstanceContainer()); } } \ No newline at end of file diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/addon/MinestomAddon.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/addon/MinestomAddon.java new file mode 100644 index 000000000..4d4adc4b8 --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/addon/MinestomAddon.java @@ -0,0 +1,47 @@ +package com.dfsek.terra.minestom.addon; + +import ca.solostudios.strata.Versions; +import ca.solostudios.strata.version.Version; + +import com.dfsek.terra.api.addon.BaseAddon; + +import com.dfsek.terra.api.event.events.config.ConfigurationLoadEvent; +import com.dfsek.terra.api.event.functional.FunctionalEventHandler; +import com.dfsek.terra.api.world.biome.Biome; + +import com.dfsek.terra.minestom.TerraMinestomPlatform; + +import com.dfsek.terra.minestom.config.VanillaBiomeProperties; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +public class MinestomAddon implements BaseAddon { + private static final Version VERSION = Versions.getVersion(1, 0, 0); + private static final Logger logger = LoggerFactory.getLogger(MinestomAddon.class); + private final TerraMinestomPlatform minestomPlatform; + + public MinestomAddon(TerraMinestomPlatform minestomPlatform) { + this.minestomPlatform = minestomPlatform; + } + + @Override + public void initialize() { + minestomPlatform.getEventManager() + .getHandler(FunctionalEventHandler.class) + .register(this, ConfigurationLoadEvent.class) + .then(event -> { + if(event.is(Biome.class)) { + event.getLoadedObject(Biome.class).getContext().put(event.load(new VanillaBiomeProperties())); + } + }) + .global(); + } + + @Override + public Version getVersion() { return VERSION; } + + @Override + public String getID() { return "terra-minestom"; } +} diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/api/BiomeFactory.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/api/BiomeFactory.java new file mode 100644 index 000000000..ee59f29d5 --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/api/BiomeFactory.java @@ -0,0 +1,15 @@ +package com.dfsek.terra.minestom.api; + +import com.dfsek.terra.api.config.ConfigPack; +import com.dfsek.terra.api.world.biome.Biome; +import com.dfsek.terra.minestom.biome.UserDefinedBiome; + + +/** + * BiomeFactory serves as a contract for creating custom user-defined biomes in Terra. + * Implementations of this interface are responsible for defining the logic to convert + * configured biomes and source biome data into instances of UserDefinedBiome. + */ +public interface BiomeFactory { + UserDefinedBiome create(ConfigPack pack, Biome source); +} diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/api/BlockEntityFactory.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/api/BlockEntityFactory.java index c5fd33ca1..6ea403ddf 100644 --- a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/api/BlockEntityFactory.java +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/api/BlockEntityFactory.java @@ -8,8 +8,8 @@ import org.jetbrains.annotations.Nullable; /** * Represents a factory interface for creating instances of BlockEntity - * at a specified BlockVec position. This is not implemented directly because - * Minestom does not define a way to build block entities out of the box. + * at a specified BlockVec position. For more fine-grained control, users + * may supply their own version of this interface. */ public interface BlockEntityFactory { @Nullable BlockEntity createBlockEntity(BlockVec position); diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/api/TerraMinestomWorldBuilder.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/api/TerraMinestomWorldBuilder.java new file mode 100644 index 000000000..30a04a172 --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/api/TerraMinestomWorldBuilder.java @@ -0,0 +1,91 @@ +package com.dfsek.terra.minestom.api; + +import com.dfsek.terra.api.config.ConfigPack; + +import com.dfsek.terra.api.registry.CheckedRegistry; + +import com.dfsek.terra.minestom.TerraMinestomPlatform; +import com.dfsek.terra.minestom.biome.MinestomUserDefinedBiomeFactory; +import com.dfsek.terra.minestom.block.DefaultBlockEntityFactory; +import com.dfsek.terra.minestom.entity.DefaultEntityFactory; + +import com.dfsek.terra.minestom.world.TerraMinestomWorld; +import net.minestom.server.instance.Instance; + +import java.util.Random; +import java.util.function.Function; + + +public class TerraMinestomWorldBuilder { + private final TerraMinestomPlatform platform; + private final Instance instance; + private ConfigPack pack; + private long seed = new Random().nextLong(); + private EntityFactory entityFactory = new DefaultEntityFactory(); + private BlockEntityFactory blockEntityFactory; + private BiomeFactory biomeFactory = new MinestomUserDefinedBiomeFactory(); + private boolean doFineGrainedBiomes = true; + + public TerraMinestomWorldBuilder(TerraMinestomPlatform platform, Instance instance) { + this.platform = platform; + this.instance = instance; + this.blockEntityFactory = new DefaultBlockEntityFactory(instance); + } + + public TerraMinestomWorldBuilder pack(ConfigPack pack) { + this.pack = pack; + return this; + } + + public TerraMinestomWorldBuilder packById(String id) { + this.pack = platform.getConfigRegistry().getByID(id).orElseThrow(); + + return this; + } + + public TerraMinestomWorldBuilder findPack(Function, ConfigPack> fn) { + this.pack = fn.apply(platform.getConfigRegistry()); + return this; + } + + public TerraMinestomWorldBuilder defaultPack() { + return this.packById("OVERWORLD"); + } + + public TerraMinestomWorldBuilder seed(long seed) { + this.seed = seed; + return this; + } + + public TerraMinestomWorldBuilder entityFactory(EntityFactory factory) { + this.entityFactory = factory; + return this; + } + + public TerraMinestomWorldBuilder blockEntityFactory(BlockEntityFactory factory) { + this.blockEntityFactory = factory; + return this; + } + + public TerraMinestomWorldBuilder biomeFactory(BiomeFactory factory) { + this.biomeFactory = factory; + return this; + } + + /** + * Due to a current bug with the minestom biome encoder, sometimes, the client gets kicked when decoding a chunk + * packet with more than one biome. Until this is fixed in minestom, one can disable fine-grained biomes to prevent + * this issue. + * + * @deprecated Scheduled for removal once Minestom rolls out a fix + */ + @Deprecated + public TerraMinestomWorldBuilder doFineGrainedBiomes(boolean doFineGrainedBiomes) { + this.doFineGrainedBiomes = doFineGrainedBiomes; + return this; + } + + public TerraMinestomWorld attach() { + return new TerraMinestomWorld(platform, instance, pack, seed, entityFactory, blockEntityFactory, biomeFactory, doFineGrainedBiomes); + } +} diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/biome/MinestomBiome.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/biome/MinestomBiome.java index a4c146359..a050978d6 100644 --- a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/biome/MinestomBiome.java +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/biome/MinestomBiome.java @@ -2,16 +2,17 @@ package com.dfsek.terra.minestom.biome; import com.dfsek.terra.api.world.biome.PlatformBiome; +import net.minestom.server.registry.DynamicRegistry; import net.minestom.server.world.biome.Biome; public class MinestomBiome implements PlatformBiome { - private final Biome biome; + private final DynamicRegistry.Key biome; - public MinestomBiome(Biome biome) { this.biome = biome; } + public MinestomBiome(DynamicRegistry.Key biome) { this.biome = biome; } @Override - public Biome getHandle() { + public DynamicRegistry.Key getHandle() { return biome; } } diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/biome/MinestomBiomeLoader.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/biome/MinestomBiomeLoader.java index edaf49a50..5115397d9 100644 --- a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/biome/MinestomBiomeLoader.java +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/biome/MinestomBiomeLoader.java @@ -11,6 +11,7 @@ import net.kyori.adventure.key.Key; import net.minestom.server.MinecraftServer; import net.minestom.server.registry.DynamicRegistry; import net.minestom.server.world.biome.Biome; +import org.intellij.lang.annotations.Subst; import org.jetbrains.annotations.NotNull; import java.lang.reflect.AnnotatedType; @@ -21,6 +22,7 @@ public class MinestomBiomeLoader implements TypeLoader { @Override public PlatformBiome load(@NotNull AnnotatedType annotatedType, @NotNull Object o, @NotNull ConfigLoader configLoader, DepthTracker depthTracker) throws LoadException { + @Subst("name:value") String id = (String) o; Key key = Key.key(id); Biome biome = biomeRegistry.get(key); diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/biome/MinestomUserDefinedBiomeFactory.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/biome/MinestomUserDefinedBiomeFactory.java new file mode 100644 index 000000000..51d1fc07e --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/biome/MinestomUserDefinedBiomeFactory.java @@ -0,0 +1,73 @@ +package com.dfsek.terra.minestom.biome; + +import com.dfsek.terra.api.config.ConfigPack; +import com.dfsek.terra.minestom.api.BiomeFactory; +import com.dfsek.terra.minestom.config.VanillaBiomeProperties; + +import net.kyori.adventure.key.Key; +import net.minestom.server.MinecraftServer; +import net.minestom.server.color.Color; +import net.minestom.server.registry.DynamicRegistry; +import net.minestom.server.world.biome.Biome; +import net.minestom.server.world.biome.BiomeEffects; +import org.intellij.lang.annotations.Subst; +import org.jetbrains.annotations.NotNull; + +import java.util.Locale; +import java.util.Objects; + + +public class MinestomUserDefinedBiomeFactory implements BiomeFactory { + private final DynamicRegistry biomeRegistry = MinecraftServer.getBiomeRegistry(); + private final @NotNull Biome plainsBiome = Objects.requireNonNull(biomeRegistry.get(Key.key("minecraft:plains"))); + + @Override + public UserDefinedBiome create(ConfigPack pack, com.dfsek.terra.api.world.biome.Biome source) { + VanillaBiomeProperties properties = source.getContext().get(VanillaBiomeProperties.class); + DynamicRegistry.Key parentKey = ((MinestomBiome) source.getPlatformBiome()).getHandle(); + Biome parent = mergeNullable(biomeRegistry.get(parentKey), plainsBiome); + BiomeEffects parentEffects = parent.effects(); + Key key = Key.key("terra", createBiomeID(pack, source.getID())); + + BiomeEffects.Builder effectsBuilder = BiomeEffects.builder() + .fogColor(mergeNullable(properties.getFogColor(), parentEffects.fogColor())) + .skyColor(mergeNullable(properties.getSkyColor(), parentEffects.skyColor())) + .waterColor(mergeNullable(properties.getWaterColor(), parentEffects.waterColor())) + .waterFogColor(mergeNullable(properties.getWaterFogColor(), parentEffects.waterFogColor())) + .foliageColor(mergeNullable(properties.getFoliageColor(), parentEffects.foliageColor())) + .grassColor(mergeNullable(properties.getGrassColor(), parentEffects.grassColor())) + .grassColorModifier(mergeNullable(properties.getGrassColorModifier(), parentEffects.grassColorModifier())) + .biomeParticle(mergeNullable(properties.getParticleConfig(), parentEffects.biomeParticle())) + .ambientSound(mergeNullable(properties.getLoopSound(), parentEffects.ambientSound())) + .moodSound(mergeNullable(properties.getMoodSound(), parentEffects.moodSound())) + .additionsSound(mergeNullable(properties.getAdditionsSound(), parentEffects.additionsSound())) + // TODO music + .music(parentEffects.music()) + .musicVolume(parentEffects.musicVolume()); + + if (effectsBuilder.build().equals(BiomeEffects.PLAINS_EFFECTS)) { + effectsBuilder.fogColor(new Color(0xC0D8FE)); // circumvent a minestom bug + } + + Biome target = Biome.builder() + .downfall(mergeNullable(properties.getDownfall(), parent.downfall())) + .hasPrecipitation(mergeNullable(properties.getPrecipitation(), parent.hasPrecipitation())) + .temperature(mergeNullable(properties.getTemperature(), parent.temperature())) + .temperatureModifier(mergeNullable(properties.getTemperatureModifier(), parent.temperatureModifier())) + .effects(effectsBuilder.build()) + .build(); + + DynamicRegistry.Key registryKey = MinecraftServer.getBiomeRegistry().register(key, target); + return new UserDefinedBiome(key, registryKey, source.getID(), target); + } + + private static T mergeNullable(T first, T second) { + if (first == null) return second; + return first; + } + + @Subst("value") + protected static String createBiomeID(ConfigPack pack, String biomeId) { + return pack.getID().toLowerCase() + "/" + biomeId.toLowerCase(Locale.ROOT); + } +} diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/biome/MinestomUserDefinedBiomePool.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/biome/MinestomUserDefinedBiomePool.java new file mode 100644 index 000000000..138d98887 --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/biome/MinestomUserDefinedBiomePool.java @@ -0,0 +1,40 @@ +package com.dfsek.terra.minestom.biome; + +import com.dfsek.terra.api.config.ConfigPack; +import com.dfsek.terra.api.world.biome.Biome; +import com.dfsek.terra.minestom.api.BiomeFactory; + +import java.util.HashMap; + + +public class MinestomUserDefinedBiomePool { + private final HashMap biomes = new HashMap<>(); + private final BiomeFactory factory; + private final ConfigPack configPack; + + public MinestomUserDefinedBiomePool(ConfigPack configPack, BiomeFactory factory) { + this.configPack = configPack; + this.factory = factory; + } + + public UserDefinedBiome getBiome(Biome source) { + UserDefinedBiome userDefinedBiome = biomes.get(source.getID()); + if(userDefinedBiome != null) return userDefinedBiome; + userDefinedBiome = factory.create(configPack, source); + biomes.put(source.getID(), userDefinedBiome); + return userDefinedBiome; + } + + public void preloadBiomes(Iterable biomesToLoad) { + biomesToLoad + .forEach(biome -> { + if(!this.biomes.containsKey(biome.getID())) { + this.biomes.put(biome.getID(), factory.create(configPack, biome)); + } + }); + } + + public void invalidate() { + biomes.clear(); + } +} diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/biome/UserDefinedBiome.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/biome/UserDefinedBiome.java new file mode 100644 index 000000000..059a0cc93 --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/biome/UserDefinedBiome.java @@ -0,0 +1,9 @@ +package com.dfsek.terra.minestom.biome; + +import net.kyori.adventure.key.Key; +import net.minestom.server.registry.DynamicRegistry; +import net.minestom.server.world.biome.Biome; + + +public record UserDefinedBiome(Key key, DynamicRegistry.Key registry, String id, Biome biome) { +} diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/block/DefaultBlockEntityFactory.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/block/DefaultBlockEntityFactory.java index dbcbf2833..28712a4c0 100644 --- a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/block/DefaultBlockEntityFactory.java +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/block/DefaultBlockEntityFactory.java @@ -4,11 +4,18 @@ import com.dfsek.terra.api.block.entity.BlockEntity; import com.dfsek.terra.minestom.api.BlockEntityFactory; import net.minestom.server.coordinate.BlockVec; +import net.minestom.server.instance.Instance; public class DefaultBlockEntityFactory implements BlockEntityFactory { + private final Instance instance; + + public DefaultBlockEntityFactory(Instance instance) { + this.instance = instance; + } + @Override public BlockEntity createBlockEntity(BlockVec position) { - return null; + return new MinestomBlockEntity(instance, position); } } diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/block/MinestomBlockEntity.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/block/MinestomBlockEntity.java new file mode 100644 index 000000000..e96caf0d9 --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/block/MinestomBlockEntity.java @@ -0,0 +1,59 @@ +package com.dfsek.terra.minestom.block; + +import com.dfsek.seismic.type.vector.Vector3; + +import com.dfsek.terra.api.block.entity.BlockEntity; + +import com.dfsek.terra.api.block.state.BlockState; + +import net.minestom.server.coordinate.BlockVec; +import net.minestom.server.instance.Instance; +import net.minestom.server.instance.block.Block; + + +public class MinestomBlockEntity implements BlockEntity { + private final Instance instance; + private final BlockVec position; + private final Vector3 positionVec; + + public MinestomBlockEntity(Instance instance, BlockVec position) { + this.instance = instance; + this.position = position; + this.positionVec = Vector3.of(position.blockX(), position.blockY(), position.blockZ()); + } + + @Override + public boolean update(boolean applyPhysics) { + return false; + } + + @Override + public Vector3 getPosition() { + return positionVec; + } + + @Override + public int getX() { + return position.blockX(); + } + + @Override + public int getY() { + return position.blockY(); + } + + @Override + public int getZ() { + return position.blockZ(); + } + + @Override + public BlockState getBlockState() { + return new MinestomBlockState(instance.getBlock(position)); + } + + @Override + public Block getHandle() { + return instance.getBlock(position); + } +} diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/block/MinestomBlockType.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/block/MinestomBlockType.java index 8e4bd4360..803f13bc3 100644 --- a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/block/MinestomBlockType.java +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/block/MinestomBlockType.java @@ -41,7 +41,7 @@ public class MinestomBlockType implements BlockType { @Override public boolean equals(Object obj) { if(obj instanceof MinestomBlockType other) { - return block.id() == other.block.id(); + return block.stateId() == other.block.stateId(); } return false; } diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/chunk/CachedChunk.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/chunk/CachedChunk.java index ed9ad8622..c56336b20 100644 --- a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/chunk/CachedChunk.java +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/chunk/CachedChunk.java @@ -3,47 +3,53 @@ package com.dfsek.terra.minestom.chunk; import com.dfsek.terra.api.block.state.BlockState; +import com.dfsek.terra.api.util.Column; +import com.dfsek.terra.api.world.biome.Biome; +import com.dfsek.terra.api.world.biome.generation.BiomeProvider; import com.dfsek.terra.api.world.chunk.generation.ProtoChunk; +import com.dfsek.terra.minestom.biome.MinestomBiome; import com.dfsek.terra.minestom.block.MinestomBlockState; +import net.minestom.server.coordinate.Point; import net.minestom.server.instance.block.Block; +import net.minestom.server.instance.generator.GenerationUnit; import net.minestom.server.instance.generator.UnitModifier; import org.jetbrains.annotations.NotNull; +import java.util.Arrays; + public class CachedChunk implements ProtoChunk { private final int minHeight; private final int maxHeight; - private final Block[][][] blocks; + private final Block[] blocks; public CachedChunk(int minHeight, int maxHeight) { this.minHeight = minHeight; this.maxHeight = maxHeight; - this.blocks = new Block[16][maxHeight - minHeight + 1][16]; - - for(int x = 0; x < 16; x++) { - for(int z = 0; z < 16; z++) { - for(int y = 0; y < maxHeight - minHeight + 1; y++) { - blocks[x][y][z] = Block.AIR; - } - } - } + this.blocks = new Block[16 * (maxHeight - minHeight + 1) * 16]; + Arrays.fill(blocks, Block.AIR); } public void writeRelative(UnitModifier modifier) { - modifier.setAllRelative((x, y, z) -> blocks[x][y][z]); + modifier.setAllRelative((x, y, z) -> blocks[getIndex(x, y + minHeight, z)]); } @Override public void setBlock(int x, int y, int z, @NotNull BlockState blockState) { Block block = (Block) blockState.getHandle(); if(block == null) return; - blocks[x][y - minHeight][z] = block; + blocks[getIndex(x, y, z)] = block; + } + + private int getIndex(int x, int y, int z) { + int y_normalized = y - minHeight; + return x + (z * 16) + (y_normalized * 256); } @Override public @NotNull BlockState getBlock(int x, int y, int z) { - return new MinestomBlockState(blocks[x][y - minHeight][z]); + return new MinestomBlockState(blocks[getIndex(x, y, z)]); } @Override diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/chunk/GeneratedChunkCache.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/chunk/GeneratedChunkCache.java index 018c82c72..38064b86f 100644 --- a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/chunk/GeneratedChunkCache.java +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/chunk/GeneratedChunkCache.java @@ -22,13 +22,15 @@ public class GeneratedChunkCache { private final ServerWorld world; private final BiomeProvider biomeProvider; - public GeneratedChunkCache(DimensionType dimensionType, ChunkGenerator generator, ServerWorld world) { + public GeneratedChunkCache(DimensionType dimensionType, ChunkGenerator generator, ServerWorld world, BiomeProvider biomeProvider) { this.dimensionType = dimensionType; this.generator = generator; this.world = world; - this.biomeProvider = world.getBiomeProvider(); - this.cache = Caffeine.newBuilder().maximumSize(128).recordStats().build( - (Pair key) -> generateChunk(key.getLeft(), key.getRight())); + this.biomeProvider = biomeProvider; + this.cache = Caffeine.newBuilder() + .maximumSize(128) + .recordStats() + .build((Pair key) -> generateChunk(key.getLeft(), key.getRight())); } private CachedChunk generateChunk(int x, int z) { @@ -39,8 +41,13 @@ public class GeneratedChunkCache { public void displayStats() { CacheStats stats = cache.stats(); - log.info("Avg load time: {}ms | Hit rate: {}% | Load Count: {}", stats.averageLoadPenalty(), stats.hitRate() * 100, - stats.loadCount()); + log.info("Avg load time: %.4fms | Hit rate: %3.4f%% | Load Count: %d" + .formatted( + stats.averageLoadPenalty() / 1000000f, + stats.hitRate() * 100, + stats.loadCount() + ) + ); } public CachedChunk at(int x, int z) { diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/config/BiomeAdditionsSoundTemplate.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/config/BiomeAdditionsSoundTemplate.java new file mode 100644 index 000000000..85e4499fc --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/config/BiomeAdditionsSoundTemplate.java @@ -0,0 +1,24 @@ +package com.dfsek.terra.minestom.config; + +import com.dfsek.tectonic.api.config.template.annotations.Default; +import com.dfsek.tectonic.api.config.template.annotations.Value; +import com.dfsek.tectonic.api.config.template.object.ObjectTemplate; +import net.minestom.server.sound.SoundEvent; +import net.minestom.server.world.biome.BiomeEffects; + + +public class BiomeAdditionsSoundTemplate implements ObjectTemplate { + @Value("sound") + @Default + private SoundEvent sound = null; + + @Value("sound-chance") + @Default + private Double soundChance = null; + + @Override + public BiomeEffects.AdditionsSound get() { + if(sound == null) return null; + return new BiomeEffects.AdditionsSound(sound, soundChance); + } +} diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/config/BiomeMoodSoundTemplate.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/config/BiomeMoodSoundTemplate.java new file mode 100644 index 000000000..a3b7bb3c8 --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/config/BiomeMoodSoundTemplate.java @@ -0,0 +1,37 @@ +package com.dfsek.terra.minestom.config; + +import com.dfsek.tectonic.api.config.template.annotations.Default; +import com.dfsek.tectonic.api.config.template.annotations.Value; +import com.dfsek.tectonic.api.config.template.object.ObjectTemplate; +import net.minestom.server.sound.SoundEvent; +import net.minestom.server.world.biome.BiomeEffects; + + +public class BiomeMoodSoundTemplate implements ObjectTemplate { + @Value("sound") + @Default + private SoundEvent sound = null; + + @Value("cultivation-ticks") + @Default + private Integer soundCultivationTicks = null; + + @Value("spawn-range") + @Default + private Integer soundSpawnRange = null; + + @Value("extra-distance") + @Default + private Double soundExtraDistance = null; + + @Override + public BiomeEffects.MoodSound get() { + if(sound == null) return null; + return new BiomeEffects.MoodSound( + sound, + soundCultivationTicks, + soundSpawnRange, + soundExtraDistance + ); + } +} diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/config/BiomeParticleConfigTemplate.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/config/BiomeParticleConfigTemplate.java new file mode 100644 index 000000000..c095b7893 --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/config/BiomeParticleConfigTemplate.java @@ -0,0 +1,30 @@ +package com.dfsek.terra.minestom.config; + +import com.dfsek.tectonic.api.config.template.annotations.Default; +import com.dfsek.tectonic.api.config.template.annotations.Value; +import com.dfsek.tectonic.api.config.template.object.ObjectTemplate; +import net.minestom.server.particle.Particle; +import net.minestom.server.world.biome.BiomeEffects; + + +public class BiomeParticleConfigTemplate implements ObjectTemplate { + @Value("particle") + @Default + private String particle = null; + + @Value("probability") + @Default + private Float probability = null; + + @Override + public BiomeEffects.Particle get() { + if(particle == null || probability == null) { + return null; + } + + return new BiomeEffects.Particle( + probability, + Particle.fromKey(particle) + ); + } +} diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/config/KeyLoader.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/config/KeyLoader.java new file mode 100644 index 000000000..406e64674 --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/config/KeyLoader.java @@ -0,0 +1,32 @@ +package com.dfsek.terra.minestom.config; + +import com.dfsek.tectonic.api.depth.DepthTracker; +import com.dfsek.tectonic.api.exception.LoadException; +import com.dfsek.tectonic.api.loader.ConfigLoader; +import com.dfsek.tectonic.api.loader.type.TypeLoader; +import net.kyori.adventure.key.InvalidKeyException; +import net.kyori.adventure.key.Key; +import org.intellij.lang.annotations.Subst; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.AnnotatedType; + + +public class KeyLoader implements TypeLoader { + @Override + public Key load( + @NotNull AnnotatedType annotatedType, + @NotNull Object o, + @NotNull ConfigLoader configLoader, + DepthTracker depthTracker + ) throws LoadException { + if(!(o instanceof @Subst("a:o") String stringKey)) { + throw new LoadException("Value is not a String", depthTracker); + } + try { + return Key.key(stringKey); + } catch(InvalidKeyException e) { + throw new LoadException("Can't load key: Invalid Format", e, depthTracker); + } + } +} diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/config/RGBLikeLoader.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/config/RGBLikeLoader.java new file mode 100644 index 000000000..de998edb5 --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/config/RGBLikeLoader.java @@ -0,0 +1,30 @@ +package com.dfsek.terra.minestom.config; + +import com.dfsek.tectonic.api.depth.DepthTracker; +import com.dfsek.tectonic.api.exception.LoadException; +import com.dfsek.tectonic.api.loader.ConfigLoader; +import com.dfsek.tectonic.api.loader.type.TypeLoader; +import net.kyori.adventure.key.InvalidKeyException; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.util.RGBLike; +import net.minestom.server.color.Color; +import org.intellij.lang.annotations.Subst; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.AnnotatedType; + + +public class RGBLikeLoader implements TypeLoader { + @Override + public RGBLike load( + @NotNull AnnotatedType annotatedType, + @NotNull Object o, + @NotNull ConfigLoader configLoader, + DepthTracker depthTracker + ) throws LoadException { + if(!(o instanceof @Subst("a:o") Integer value)) { + throw new LoadException("Value is not an integer", depthTracker); + } + return new Color(value); + } +} diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/config/SoundEventTemplate.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/config/SoundEventTemplate.java new file mode 100644 index 000000000..c8bd81a13 --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/config/SoundEventTemplate.java @@ -0,0 +1,28 @@ +package com.dfsek.terra.minestom.config; + +import com.dfsek.tectonic.api.config.template.annotations.Default; +import com.dfsek.tectonic.api.config.template.annotations.Value; +import com.dfsek.tectonic.api.config.template.object.ObjectTemplate; +import net.kyori.adventure.key.Key; +import net.minestom.server.sound.SoundEvent; + + +public class SoundEventTemplate implements ObjectTemplate { + @Value("id") + @Default + private Key id = null; + + @Value("distance-to-travel") + @Default + private Float distanceToTravel = null; + + @Override + public SoundEvent get() { + if(id == null) { + return null; + } else { + // distanceToTravel is specifically allowed to be null. + return SoundEvent.of(id, distanceToTravel); + } + } +} diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/config/VanillaBiomeProperties.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/config/VanillaBiomeProperties.java new file mode 100644 index 000000000..ea82cd7e2 --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/config/VanillaBiomeProperties.java @@ -0,0 +1,140 @@ +package com.dfsek.terra.minestom.config; + +import com.dfsek.tectonic.api.config.template.ConfigTemplate; +import com.dfsek.tectonic.api.config.template.annotations.Default; +import com.dfsek.tectonic.api.config.template.annotations.Value; + +import com.dfsek.terra.api.properties.Properties; + +import net.kyori.adventure.util.RGBLike; +import net.minestom.server.sound.SoundEvent; +import net.minestom.server.world.biome.Biome.TemperatureModifier; +import net.minestom.server.world.biome.BiomeEffects; +import net.minestom.server.world.biome.BiomeEffects.GrassColorModifier; + + +public class VanillaBiomeProperties implements ConfigTemplate, Properties { + @Value("colors.grass") + @Default + private RGBLike grassColor = null; + + @Value("colors.fog") + @Default + private RGBLike fogColor = null; + + @Value("colors.water") + @Default + private RGBLike waterColor = null; + + @Value("colors.water-fog") + @Default + private RGBLike waterFogColor = null; + + @Value("colors.foliage") + @Default + private RGBLike foliageColor = null; + + @Value("colors.sky") + @Default + private RGBLike skyColor = null; + + @Value("colors.modifier") + @Default + private GrassColorModifier grassColorModifier = null; + + @Value("particles") + @Default + private BiomeEffects.Particle particleConfig = null; + + @Value("climate.precipitation") + @Default + private Boolean precipitation = null; + + @Value("climate.temperature") + @Default + private Float temperature = null; + + @Value("climate.temperature-modifier") + @Default + private TemperatureModifier temperatureModifier = null; + + @Value("climate.downfall") + @Default + private Float downfall = null; + + @Value("sound.loop-sound.sound") + @Default + private SoundEvent loopSound = null; + + @Value("sound.mood-sound") + @Default + private BiomeEffects.MoodSound moodSound = null; + + @Value("sound.additions-sound") + @Default + private BiomeEffects.AdditionsSound additionsSound = null; + +// @Value("sound.music") +// @Default +// private MusicSound music = null; + + public RGBLike getGrassColor() { + return grassColor; + } + + public RGBLike getFogColor() { + return fogColor; + } + + public RGBLike getWaterColor() { + return waterColor; + } + + public RGBLike getWaterFogColor() { + return waterFogColor; + } + + public RGBLike getFoliageColor() { + return foliageColor; + } + + public RGBLike getSkyColor() { + return skyColor; + } + + public GrassColorModifier getGrassColorModifier() { + return grassColorModifier; + } + + public BiomeEffects.Particle getParticleConfig() { + return particleConfig; + } + + public Boolean getPrecipitation() { + return precipitation; + } + + public Float getTemperature() { + return temperature; + } + + public TemperatureModifier getTemperatureModifier() { + return temperatureModifier; + } + + public Float getDownfall() { + return downfall; + } + + public SoundEvent getLoopSound() { + return loopSound; + } + + public BiomeEffects.MoodSound getMoodSound() { + return moodSound; + } + + public BiomeEffects.AdditionsSound getAdditionsSound() { + return additionsSound; + } +} diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/item/MinestomItemStack.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/item/MinestomItemStack.java index 2b488a8c8..214785016 100644 --- a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/item/MinestomItemStack.java +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/item/MinestomItemStack.java @@ -6,7 +6,7 @@ import com.dfsek.terra.api.inventory.item.Enchantment; import com.dfsek.terra.api.inventory.item.ItemMeta; import net.minestom.server.MinecraftServer; -import net.minestom.server.item.ItemComponent; +import net.minestom.server.component.DataComponents; import net.minestom.server.item.ItemStack; import net.minestom.server.item.component.EnchantmentList; import net.minestom.server.registry.DynamicRegistry; @@ -47,7 +47,7 @@ public class MinestomItemStack implements com.dfsek.terra.api.inventory.ItemStac @Override public ItemMeta getItemMeta() { HashMap enchantments = new HashMap<>(); - EnchantmentList enchantmentList = base.get(ItemComponent.ENCHANTMENTS); + EnchantmentList enchantmentList = base.get(DataComponents.ENCHANTMENTS); if(enchantmentList != null) { enchantmentList.enchantments().forEach((enchantmentKey, integer) -> { enchantments.put( @@ -67,6 +67,6 @@ public class MinestomItemStack implements com.dfsek.terra.api.inventory.ItemStac }); EnchantmentList list = new EnchantmentList(enchantments); - base = base.with(ItemComponent.ENCHANTMENTS, list); + base = base.with(DataComponents.ENCHANTMENTS, list); } } diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/MinestomChunkGeneratorWrapper.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/MinestomChunkGeneratorWrapper.java index 81529db48..6c72a3326 100644 --- a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/MinestomChunkGeneratorWrapper.java +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/MinestomChunkGeneratorWrapper.java @@ -1,16 +1,23 @@ package com.dfsek.terra.minestom.world; +import com.dfsek.terra.api.Platform; import com.dfsek.terra.api.config.ConfigPack; +import com.dfsek.terra.api.world.biome.generation.BiomeProvider; import com.dfsek.terra.api.world.chunk.generation.ChunkGenerator; import com.dfsek.terra.api.world.chunk.generation.stage.GenerationStage; import com.dfsek.terra.api.world.chunk.generation.util.GeneratorWrapper; +import com.dfsek.terra.minestom.biome.MinestomUserDefinedBiomePool; +import com.dfsek.terra.minestom.biome.UserDefinedBiome; import com.dfsek.terra.minestom.chunk.CachedChunk; import com.dfsek.terra.minestom.chunk.GeneratedChunkCache; +import net.minestom.server.MinecraftServer; import net.minestom.server.coordinate.Point; +import net.minestom.server.entity.Player; import net.minestom.server.instance.generator.GenerationUnit; import net.minestom.server.instance.generator.Generator; +import net.minestom.server.instance.generator.UnitModifier; import org.jetbrains.annotations.NotNull; @@ -18,13 +25,27 @@ public class MinestomChunkGeneratorWrapper implements Generator, GeneratorWrappe private final GeneratedChunkCache cache; private ChunkGenerator generator; private final TerraMinestomWorld world; + private final BiomeProvider biomeProvider; + private final boolean doFineGrainedBiomes; private ConfigPack pack; + private final MinestomUserDefinedBiomePool biomePool; - public MinestomChunkGeneratorWrapper(ChunkGenerator generator, TerraMinestomWorld world, ConfigPack pack) { + public MinestomChunkGeneratorWrapper( + Platform platform, + ChunkGenerator generator, + TerraMinestomWorld world, + ConfigPack pack, + MinestomUserDefinedBiomePool biomePool, + boolean doFineGrainedBiomes + ) { this.generator = generator; this.world = world; this.pack = pack; - this.cache = new GeneratedChunkCache(world.getDimensionType(), generator, world); + this.biomePool = biomePool; + this.biomeProvider = pack.getBiomeProvider(); + this.doFineGrainedBiomes = doFineGrainedBiomes; + this.cache = new GeneratedChunkCache(world.getDimensionType(), generator, world, biomeProvider); + preloadBiomes(); } public ChunkGenerator getGenerator() { @@ -36,8 +57,30 @@ public class MinestomChunkGeneratorWrapper implements Generator, GeneratorWrappe Point start = unit.absoluteStart(); int x = start.chunkX(); int z = start.chunkZ(); + int blockX = start.blockX(); + int blockZ = start.blockZ(); + int minY = world.getMinHeight(); + int maxY = world.getMaxHeight(); CachedChunk chunk = cache.at(x, z); - chunk.writeRelative(unit.modifier()); + UnitModifier modifier = unit.modifier(); + chunk.writeRelative(modifier); + + if(doFineGrainedBiomes) { + for(int y = minY; y < maxY; y++) { + for(int absoluteX = blockX; absoluteX < blockX + 16; absoluteX++) { + for(int absoluteZ = blockZ; absoluteZ < blockZ + 16; absoluteZ++) { + UserDefinedBiome userDefinedBiome = biomePool.getBiome( + biomeProvider.getBiome(absoluteX, y, absoluteZ, world.getSeed()) + ); + modifier.setBiome(absoluteX, y, absoluteZ, userDefinedBiome.registry()); + } + } + } + } else { + // TODO: remove with feature flag once minestom fixed biome encoding + UserDefinedBiome userDefinedBiome = biomePool.getBiome(biomeProvider.getBiome(blockX, 100, blockZ, world.getSeed())); + modifier.fillBiome(userDefinedBiome.registry()); + } unit.fork(setter -> { MinestomProtoWorld protoWorld = new MinestomProtoWorld(cache, x, z, world, setter); @@ -55,6 +98,15 @@ public class MinestomChunkGeneratorWrapper implements Generator, GeneratorWrappe public void setPack(ConfigPack pack) { this.pack = pack; this.generator = pack.getGeneratorProvider().newInstance(pack); + this.biomePool.invalidate(); + preloadBiomes(); + } + + private void preloadBiomes() { + this.biomePool.preloadBiomes(world.getBiomeProvider().getBiomes()); + for(Player player : MinecraftServer.getConnectionManager().getOnlinePlayers()) { + player.startConfigurationPhase(); + } } public void displayStats() { diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/MinestomProtoWorld.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/MinestomProtoWorld.java index 65ec22654..e29cc5d76 100644 --- a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/MinestomProtoWorld.java +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/MinestomProtoWorld.java @@ -5,6 +5,7 @@ import com.dfsek.terra.api.block.state.BlockState; import com.dfsek.terra.api.config.ConfigPack; import com.dfsek.terra.api.entity.Entity; import com.dfsek.terra.api.entity.EntityType; +import com.dfsek.terra.api.util.generic.pair.Pair; import com.dfsek.terra.api.world.ServerWorld; import com.dfsek.terra.api.world.biome.generation.BiomeProvider; import com.dfsek.terra.api.world.chunk.generation.ChunkGenerator; @@ -17,6 +18,11 @@ import com.dfsek.terra.minestom.entity.DeferredMinestomEntity; import net.minestom.server.instance.block.Block; import net.minestom.server.instance.block.Block.Setter; +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.HashSet; +import java.util.WeakHashMap; + public class MinestomProtoWorld implements ProtoWorld { private final GeneratedChunkCache cache; @@ -51,6 +57,10 @@ public class MinestomProtoWorld implements ProtoWorld { @Override public void setBlockState(int x, int y, int z, BlockState data, boolean physics) { modifier.setBlock(x, y, z, (Block) data.getHandle()); + int chunkX = x >> 4; + int chunkZ = z >> 4; + CachedChunk chunk = cache.at(chunkX, chunkZ); + chunk.setBlock(x & 15, y, z & 15, data); } @Override diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/TerraMinestomWorld.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/TerraMinestomWorld.java index 4f4aef091..df38b5dd2 100644 --- a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/TerraMinestomWorld.java +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/TerraMinestomWorld.java @@ -13,8 +13,12 @@ import com.dfsek.terra.api.world.chunk.generation.ChunkGenerator; import com.dfsek.terra.api.world.info.WorldProperties; +import com.dfsek.terra.minestom.TerraMinestomPlatform; import com.dfsek.terra.minestom.api.BlockEntityFactory; import com.dfsek.terra.minestom.api.EntityFactory; +import com.dfsek.terra.minestom.api.BiomeFactory; +import com.dfsek.terra.minestom.biome.MinestomUserDefinedBiomeFactory; +import com.dfsek.terra.minestom.biome.MinestomUserDefinedBiomePool; import com.dfsek.terra.minestom.block.MinestomBlockState; import com.dfsek.terra.minestom.entity.MinestomEntity; @@ -37,8 +41,16 @@ public final class TerraMinestomWorld implements ServerWorld, WorldProperties { private final EntityFactory entityFactory; private final BlockEntityFactory blockEntityFactory; - public TerraMinestomWorld(Instance instance, ConfigPack pack, long seed, EntityFactory entityFactory, - BlockEntityFactory blockEntityFactory) { + public TerraMinestomWorld( + TerraMinestomPlatform platform, + Instance instance, + ConfigPack pack, + long seed, + EntityFactory entityFactory, + BlockEntityFactory blockEntityFactory, + BiomeFactory factory, + boolean doFineGrainedBiomes + ) { this.instance = instance; this.pack = pack; this.seed = seed; @@ -46,7 +58,14 @@ public final class TerraMinestomWorld implements ServerWorld, WorldProperties { this.dimensionType = MinecraftServer.getDimensionTypeRegistry().get(instance.getDimensionType()); this.blockEntityFactory = blockEntityFactory; - this.wrapper = new MinestomChunkGeneratorWrapper(pack.getGeneratorProvider().newInstance(pack), this, pack); + this.wrapper = new MinestomChunkGeneratorWrapper( + platform, + pack.getGeneratorProvider().newInstance(pack), + this, + pack, + new MinestomUserDefinedBiomePool(pack, factory), + doFineGrainedBiomes + ); this.entityFactory = entityFactory; instance.setGenerator(this.wrapper); diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/TerraMinestomWorldBuilder.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/TerraMinestomWorldBuilder.java deleted file mode 100644 index 46ae1691c..000000000 --- a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/TerraMinestomWorldBuilder.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.dfsek.terra.minestom.world; - -import com.dfsek.terra.api.config.ConfigPack; - -import com.dfsek.terra.api.registry.CheckedRegistry; - -import com.dfsek.terra.minestom.MinestomPlatform; -import com.dfsek.terra.minestom.api.BlockEntityFactory; -import com.dfsek.terra.minestom.api.EntityFactory; -import com.dfsek.terra.minestom.block.DefaultBlockEntityFactory; -import com.dfsek.terra.minestom.entity.DefaultEntityFactory; - -import net.minestom.server.MinecraftServer; -import net.minestom.server.instance.Instance; - -import java.util.Random; -import java.util.function.Function; - - -public class TerraMinestomWorldBuilder { - private final Instance instance; - private ConfigPack pack; - private long seed = new Random().nextLong(); - private EntityFactory entityFactory = new DefaultEntityFactory(); - private BlockEntityFactory blockEntityFactory = new DefaultBlockEntityFactory(); - - private TerraMinestomWorldBuilder(Instance instance) { this.instance = instance; } - - public static TerraMinestomWorldBuilder from(Instance instance) { - return new TerraMinestomWorldBuilder(instance); - } - - public static TerraMinestomWorldBuilder builder() { - return new TerraMinestomWorldBuilder(MinecraftServer.getInstanceManager().createInstanceContainer()); - } - - public TerraMinestomWorldBuilder pack(ConfigPack pack) { - this.pack = pack; - return this; - } - - public TerraMinestomWorldBuilder packById(String id) { - this.pack = MinestomPlatform.getInstance().getConfigRegistry().getByID(id).orElseThrow(); - - return this; - } - - public TerraMinestomWorldBuilder findPack(Function, ConfigPack> fn) { - this.pack = fn.apply(MinestomPlatform.getInstance().getConfigRegistry()); - return this; - } - - public TerraMinestomWorldBuilder defaultPack() { - return this.packById("OVERWORLD"); - } - - public TerraMinestomWorldBuilder seed(long seed) { - this.seed = seed; - return this; - } - - public TerraMinestomWorldBuilder entityFactory(EntityFactory factory) { - this.entityFactory = factory; - return this; - } - - public TerraMinestomWorldBuilder blockEntityFactory(BlockEntityFactory factory) { - this.blockEntityFactory = factory; - return this; - } - - public TerraMinestomWorld attach() { - return new TerraMinestomWorld(instance, pack, seed, entityFactory, blockEntityFactory); - } -}