Merge branch 'dev/7.0-2' into dev/layered-generator

This commit is contained in:
Zoë Gidiere
2025-06-03 13:44:03 -06:00
798 changed files with 11273 additions and 10014 deletions
+9
View File
@@ -0,0 +1,9 @@
# Allay platform
## Resource files
Current mapping version: je 1.21.4 to be 1.21.50
- `mapping/biomes.json` and `mapping/items.json` obtain from [GeyserMC/mappings](https://github.com/GeyserMC/mappings).
- `mapping/blocks.json` generated by using [GeyserMC/mappings-generator](https://github.com/GeyserMC/mappings-generator), and it's origin name is `generator_blocks.json`.
- `je_block_default_states.json` converted from [Block state values](https://zh.minecraft.wiki/w/Module:Block_state_values).
+37
View File
@@ -0,0 +1,37 @@
repositories {
ivy {
url = uri("https://raw.githubusercontent.com/")
patternLayout {
artifact("[organisation]/[revision]/[artifact].([ext])")
setM2compatible(true)
}
metadataSources {
artifact()
}
}
}
val geyserMappings: Configuration by configurations.register("geyserMappings") {
isCanBeConsumed = false
}
dependencies {
shadedApi(project(":common:implementation:base"))
implementation("com.google.code.gson", "gson", Versions.Allay.gson)
compileOnly("org.allaymc.allay", "api", Versions.Allay.api)
geyserMappings("GeyserMC.mappings", "items", Versions.Allay.mappings, ext = "json")
geyserMappings("GeyserMC.mappings", "biomes", Versions.Allay.mappings, ext = "json")
geyserMappings("GeyserMC.mappings-generator", "generator_blocks", Versions.Allay.mappingsGenerator, ext = "json")
}
tasks.processResources {
from(geyserMappings) {
into("mapping")
// rather jank, but whatever
rename("(?:generator_)?([^-]+)-(.*)\\.json", "$1.json")
}
}
@@ -0,0 +1,93 @@
package com.dfsek.terra.allay;
import com.dfsek.tectonic.api.TypeRegistry;
import com.dfsek.tectonic.api.depth.DepthTracker;
import com.dfsek.tectonic.api.exception.LoadException;
import org.allaymc.api.server.Server;
import org.allaymc.api.world.biome.BiomeId;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
import com.dfsek.terra.AbstractPlatform;
import com.dfsek.terra.allay.delegate.AllayBiome;
import com.dfsek.terra.allay.generator.AllayGeneratorWrapper;
import com.dfsek.terra.allay.handle.AllayItemHandle;
import com.dfsek.terra.allay.handle.AllayWorldHandle;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.handle.ItemHandle;
import com.dfsek.terra.api.handle.WorldHandle;
import com.dfsek.terra.api.world.biome.PlatformBiome;
/**
* @author daoge_cmd
*/
public class AllayPlatform extends AbstractPlatform {
public static final Set<AllayGeneratorWrapper> GENERATOR_WRAPPERS = new HashSet<>();
protected static final AllayWorldHandle ALLAY_WORLD_HANDLE = new AllayWorldHandle();
protected static final AllayItemHandle ALLAY_ITEM_HANDLE = new AllayItemHandle();
public AllayPlatform() {
load();
}
@Override
public boolean reload() {
getTerraConfig().load(this);
boolean succeed = loadConfigPacks();
GENERATOR_WRAPPERS.forEach(wrapper -> {
getConfigRegistry().get(wrapper.getConfigPack().getRegistryKey()).ifPresent(pack -> {
wrapper.setConfigPack(pack);
var dimension = wrapper.getAllayWorldGenerator().getDimension();
TerraAllayPlugin.INSTANCE.getPluginLogger().info(
"Replaced pack in chunk generator for world {}",
dimension.getWorld().getWorldData().getDisplayName() + ":" + dimension.getDimensionInfo().dimensionId()
);
});
});
return succeed;
}
@Override
public @NotNull String platformName() {
return "Allay";
}
@Override
public @NotNull WorldHandle getWorldHandle() {
return ALLAY_WORLD_HANDLE;
}
@Override
public @NotNull ItemHandle getItemHandle() {
return ALLAY_ITEM_HANDLE;
}
@Override
public @NotNull File getDataFolder() {
return TerraAllayPlugin.INSTANCE.getPluginContainer().dataFolder().toFile();
}
@Override
public void runPossiblyUnsafeTask(@NotNull Runnable task) {
Server.getInstance().getScheduler().runLater(Server.getInstance(), task);
}
@Override
public void register(TypeRegistry registry) {
super.register(registry);
registry.registerLoader(BlockState.class, (type, o, loader, depthTracker) -> ALLAY_WORLD_HANDLE.createBlockState((String) o))
.registerLoader(PlatformBiome.class, (type, o, loader, depthTracker) -> parseBiome((String) o, depthTracker));
}
protected AllayBiome parseBiome(String id, DepthTracker depthTracker) throws LoadException {
if(!id.startsWith("minecraft:")) throw new LoadException("Invalid biome identifier " + id, depthTracker);
return new AllayBiome(BiomeId.fromId(Mapping.biomeIdJeToBe(id)));
}
}
@@ -0,0 +1,79 @@
package com.dfsek.terra.allay;
import org.allaymc.api.utils.HashUtils;
import java.util.Map;
import java.util.TreeMap;
/**
* @author daoge_cmd
*/
public class JeBlockState {
protected final String identifier;
protected final TreeMap<String, String> properties;
protected int hash = Integer.MAX_VALUE;
public static JeBlockState fromString(String data) {
return new JeBlockState(data);
}
public static JeBlockState create(String identifier, TreeMap<String, String> properties) {
return new JeBlockState(identifier, properties);
}
private JeBlockState(String data) {
String[] strings = data.replace("[", ",").replace("]", ",").replace(" ", "").split(",");
this.identifier = strings[0];
this.properties = new TreeMap<>();
if (strings.length > 1) {
for (int i = 1; i < strings.length; i++) {
final String tmp = strings[i];
final int index = tmp.indexOf("=");
properties.put(tmp.substring(0, index), tmp.substring(index + 1));
}
}
completeMissingProperties();
}
public String getPropertyValue(String key) {
return properties.get(key);
}
private void completeMissingProperties() {
Map<String, String> defaultProperties = Mapping.getJeBlockDefaultProperties(identifier);
if(properties.size() == defaultProperties.size()) {
return;
}
defaultProperties.entrySet().stream().filter(entry -> !properties.containsKey(entry.getKey())).forEach(
entry -> properties.put(entry.getKey(), entry.getValue()));
}
private JeBlockState(String identifier, TreeMap<String, String> properties) {
this.identifier = identifier;
this.properties = properties;
}
public String toString(boolean includeProperties) {
if(!includeProperties) return identifier;
StringBuilder builder = new StringBuilder(identifier).append(";");
properties.forEach((k, v) -> builder.append(k).append("=").append(v).append(";"));
String str = builder.toString();
if (hash == Integer.MAX_VALUE) {
hash = HashUtils.fnv1a_32(str.getBytes());
}
return str;
}
public int getHash() {
if (hash == Integer.MAX_VALUE) {
hash = HashUtils.fnv1a_32(toString(true).getBytes());
}
return hash;
}
@Override
public String toString() {
return toString(true);
}
}
@@ -0,0 +1,272 @@
package com.dfsek.terra.allay;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import org.allaymc.api.block.type.BlockState;
import org.allaymc.api.block.type.BlockStateSafeGetter;
import org.allaymc.api.block.type.BlockTypes;
import org.allaymc.api.item.type.ItemType;
import org.allaymc.api.item.type.ItemTypeSafeGetter;
import javax.annotation.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.TreeMap;
/**
* @author daoge_cmd
* @author IWareQ
*/
public final class Mapping {
private static final Gson GSON = new GsonBuilder()
.registerTypeAdapterFactory(new IgnoreFailureTypeAdapterFactory())
.create();
private static final Map<String, Map<String, String>> JE_BLOCK_DEFAULT_PROPERTIES = new Object2ObjectOpenHashMap<>();
private static final Map<BlockState, JeBlockState> BE_BLOCK_STATE_TO_JE = new Object2ObjectOpenHashMap<>();
private static final Map<Integer, BlockState> JE_BLOCK_STATE_HASH_TO_BE = new Int2ObjectOpenHashMap<>();
private static final Map<String, ItemType<?>> JE_ITEM_ID_TO_BE = new Object2ObjectOpenHashMap<>();
private static final Map<String, Integer> JE_BIOME_ID_TO_BE = new Object2IntOpenHashMap<>();
private static final BlockState BE_AIR_STATE = BlockTypes.AIR.getDefaultState();
public static void init() {
if(!initBlockStateMapping()) error();
if(!initJeBlockDefaultProperties()) error();
if(!initItemMapping()) error();
if(!initBiomeMapping()) error();
}
public static JeBlockState blockStateBeToJe(BlockState beBlockState) {
return BE_BLOCK_STATE_TO_JE.get(beBlockState);
}
public static BlockState blockStateJeToBe(JeBlockState jeBlockState) {
BlockState result = JE_BLOCK_STATE_HASH_TO_BE.get(jeBlockState.getHash());
if(result == null) {
TerraAllayPlugin.INSTANCE.getPluginLogger().warn("Failed to find be block state for {}", jeBlockState);
return BE_AIR_STATE;
}
return result;
}
public static ItemType<?> itemIdJeToBe(String jeItemId) {
return JE_ITEM_ID_TO_BE.get(jeItemId);
}
// Enchantment identifiers are same in both versions
public static String enchantmentIdBeToJe(String beEnchantmentId) {
return beEnchantmentId;
}
public static String enchantmentIdJeToBe(String jeEnchantmentId) {
return jeEnchantmentId;
}
public static int biomeIdJeToBe(String jeBiomeId) {
return JE_BIOME_ID_TO_BE.get(jeBiomeId);
}
public static Map<String, String> getJeBlockDefaultProperties(String jeBlockIdentifier) {
var defaultProperties = JE_BLOCK_DEFAULT_PROPERTIES.get(jeBlockIdentifier);
if(defaultProperties == null) {
TerraAllayPlugin.INSTANCE.getPluginLogger().warn("Failed to find default properties for {}", jeBlockIdentifier);
return Map.of();
}
return defaultProperties;
}
private static void error() {
throw new RuntimeException("Mapping not initialized");
}
private static boolean initBiomeMapping() {
try(InputStream stream = Mapping.class.getClassLoader().getResourceAsStream("mapping/biomes.json")) {
if(stream == null) {
TerraAllayPlugin.INSTANCE.getPluginLogger().error("biomes mapping not found");
return false;
}
Map<String, BiomeMapping> mappings = from(stream, new TypeToken<>() {});
mappings.forEach((javaId, mapping) -> JE_BIOME_ID_TO_BE.put(javaId, mapping.bedrockId()));
} catch(IOException e) {
TerraAllayPlugin.INSTANCE.getPluginLogger().error("Failed to load biomes mapping", e);
return false;
}
return true;
}
private static boolean initItemMapping() {
try(InputStream stream = Mapping.class.getClassLoader().getResourceAsStream("mapping/items.json")) {
if(stream == null) {
TerraAllayPlugin.INSTANCE.getPluginLogger().error("items mapping not found");
return false;
}
Map<String, ItemMapping> mappings = from(stream, new TypeToken<>() {});
mappings.forEach((javaId, mapping) -> {
ItemType<?> itemType = ItemTypeSafeGetter
.name(mapping.bedrockId())
.meta(mapping.bedrockData())
.itemType();
JE_ITEM_ID_TO_BE.put(javaId, itemType);
});
} catch(IOException e) {
TerraAllayPlugin.INSTANCE.getPluginLogger().error("Failed to load items mapping", e);
return false;
}
return true;
}
private static boolean initBlockStateMapping() {
try(InputStream stream = Mapping.class.getClassLoader().getResourceAsStream("mapping/blocks.json")) {
if(stream == null) {
TerraAllayPlugin.INSTANCE.getPluginLogger().error("blocks mapping not found");
return false;
}
Map<String, List<BlockMapping>> root = from(stream, new TypeToken<>() {});
List<BlockMapping> mappings = root.get("mappings");
mappings.forEach(mapping -> {
JeBlockState jeState = createJeBlockState(mapping.javaState());
BlockState beState = createBeBlockState(mapping.bedrockState());
BE_BLOCK_STATE_TO_JE.put(beState, jeState);
JE_BLOCK_STATE_HASH_TO_BE.put(jeState.getHash(), beState);
});
} catch(IOException e) {
TerraAllayPlugin.INSTANCE.getPluginLogger().error("Failed to load blocks mapping", e);
return false;
}
return true;
}
private static boolean initJeBlockDefaultProperties() {
try(InputStream stream = Mapping.class.getClassLoader().getResourceAsStream("je_block_default_states.json")) {
if(stream == null) {
TerraAllayPlugin.INSTANCE.getPluginLogger().error("je_block_default_states.json not found");
return false;
}
Map<String, Map<String, String>> states = from(stream, new TypeToken<>() {});
JE_BLOCK_DEFAULT_PROPERTIES.putAll(states);
} catch(IOException e) {
throw new RuntimeException(e);
}
return true;
}
private static JeBlockState createJeBlockState(BlockMapping.JavaState state) {
Map<String, String> properties = state.properties() == null ? Map.of() : state.properties();
return JeBlockState.create(state.name(), new TreeMap<>(properties));
}
private static BlockState createBeBlockState(BlockMapping.BedrockState state) {
BlockStateSafeGetter.Getter getter = BlockStateSafeGetter.name("minecraft:" + state.bedrockId());
if(state.state() != null) {
convertValueType(state.state()).forEach(getter::property);
}
return getter.blockState();
}
private static Map<String, Object> convertValueType(Map<String, Object> data) {
Map<String, Object> result = new TreeMap<>();
for(Entry<String, Object> entry : data.entrySet()) {
if(entry.getValue() instanceof Number number) {
// Convert double to int because the number in json is double
result.put(entry.getKey(), number.intValue());
} else {
result.put(entry.getKey(), entry.getValue());
}
}
return result;
}
public static <V> V from(InputStream inputStream, TypeToken<V> typeToken) {
JsonReader reader = new JsonReader(new InputStreamReader(Objects.requireNonNull(inputStream)));
return GSON.fromJson(reader, typeToken.getType());
}
public record BiomeMapping(
@SerializedName("bedrock_id")
int bedrockId
) {
}
public record ItemMapping(
@SerializedName("bedrock_identifier")
String bedrockId,
@SerializedName("bedrock_data")
int bedrockData
) {
}
public record BlockMapping(
@SerializedName("java_state")
BlockMapping.JavaState javaState,
@SerializedName("bedrock_state")
BlockMapping.BedrockState bedrockState
) {
public record JavaState(
@SerializedName("Name")
String name,
@Nullable
@SerializedName("Properties")
Map<String, String> properties
) {
}
public record BedrockState(
@SerializedName("bedrock_identifier")
String bedrockId,
@Nullable
Map<String, Object> state
) {
}
}
// see https://stackoverflow.com/questions/59655279/is-there-an-easy-way-to-make-gson-skip-a-field-if-theres-an-error-deserializing
public static class IgnoreFailureTypeAdapterFactory implements TypeAdapterFactory {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
TypeAdapter<T> delegate = gson.getDelegateAdapter(this, typeToken);
return new TypeAdapter<>() {
@Override
public void write(JsonWriter writer, T value) throws IOException {
delegate.write(writer, value);
}
@Override
public T read(JsonReader reader) throws IOException {
try {
return delegate.read(reader);
} catch(Exception e) {
reader.skipValue();
return null;
}
}
};
}
}
}
@@ -0,0 +1,75 @@
package com.dfsek.terra.allay;
import org.allaymc.api.eventbus.EventHandler;
import org.allaymc.api.eventbus.event.world.WorldUnloadEvent;
import org.allaymc.api.plugin.Plugin;
import org.allaymc.api.registry.Registries;
import org.allaymc.api.server.Server;
import com.dfsek.terra.allay.generator.AllayGeneratorWrapper;
import com.dfsek.terra.api.event.events.platform.PlatformInitializationEvent;
/**
* @author daoge_cmd
*/
public class TerraAllayPlugin extends Plugin {
public static TerraAllayPlugin INSTANCE;
public static AllayPlatform PLATFORM;
{
INSTANCE = this;
}
@Override
public void onLoad() {
pluginLogger.info("Starting Terra...");
pluginLogger.info("Loading mapping...");
Mapping.init();
pluginLogger.info("Initializing allay platform...");
PLATFORM = new AllayPlatform();
PLATFORM.getEventManager().callEvent(new PlatformInitializationEvent());
// TODO: adapt command manager
pluginLogger.info("Registering generator...");
Registries.WORLD_GENERATOR_FACTORIES.register("TERRA", preset -> {
try {
AllayGeneratorWrapper wrapper = new AllayGeneratorWrapper(preset);
AllayPlatform.GENERATOR_WRAPPERS.add(wrapper);
return wrapper.getAllayWorldGenerator();
} catch(IllegalArgumentException e) {
TerraAllayPlugin.INSTANCE.getPluginLogger().error("Fail to create world generator with preset: {}", preset, e);
return Registries.WORLD_GENERATOR_FACTORIES.get("FLAT").apply("");
}
});
pluginLogger.info("Terra started");
}
@Override
public void onEnable() {
Server.getInstance().getEventBus().registerListener(this);
}
@Override
public boolean isReloadable() {
return true;
}
@Override
public void reload() {
if(PLATFORM.reload()) {
pluginLogger.info("Terra reloaded successfully.");
} else {
pluginLogger.error("Terra failed to reload.");
}
}
@EventHandler
private void onWorldUnload(WorldUnloadEvent event) {
AllayPlatform.GENERATOR_WRAPPERS.removeIf(wrapper -> wrapper.getAllayWorldGenerator().getDimension().getWorld() == event.getWorld());
}
}
@@ -0,0 +1,15 @@
package com.dfsek.terra.allay.delegate;
import org.allaymc.api.world.biome.BiomeType;
import com.dfsek.terra.api.world.biome.PlatformBiome;
/**
* @author daoge_cmd
*/
public record AllayBiome(BiomeType allayBiome) implements PlatformBiome {
@Override
public BiomeType getHandle() {
return allayBiome;
}
}
@@ -0,0 +1,74 @@
package com.dfsek.terra.allay.delegate;
import org.allaymc.api.block.type.BlockState;
import org.allaymc.api.block.type.BlockTypes;
import com.dfsek.terra.allay.JeBlockState;
import com.dfsek.terra.api.block.BlockType;
import com.dfsek.terra.api.block.state.properties.Property;
/**
* @author daoge_cmd
*/
public final class AllayBlockState implements com.dfsek.terra.api.block.state.BlockState {
public static final AllayBlockState AIR = new AllayBlockState(BlockTypes.AIR.getDefaultState(),
JeBlockState.fromString("minecraft:air"));
private final BlockState allayBlockState;
private final JeBlockState jeBlockState;
private final boolean containsWater;
public AllayBlockState(BlockState allayBlockState, JeBlockState jeBlockState) {
this.allayBlockState = allayBlockState;
this.jeBlockState = jeBlockState;
this.containsWater = "true".equals(jeBlockState.getPropertyValue("waterlogged"));
}
@Override
public boolean matches(com.dfsek.terra.api.block.state.BlockState o) {
AllayBlockState other = ((AllayBlockState) o);
return other.allayBlockState == this.allayBlockState && other.containsWater == this.containsWater;
}
@Override
public <T extends Comparable<T>> boolean has(Property<T> property) {
return false;
}
@Override
public <T extends Comparable<T>> T get(Property<T> property) {
return null;
}
@Override
public <T extends Comparable<T>> com.dfsek.terra.api.block.state.BlockState set(Property<T> property, T value) {
return null;
}
@Override
public BlockType getBlockType() {
return new AllayBlockType(allayBlockState.getBlockType());
}
@Override
public String getAsString(boolean properties) {
return jeBlockState.toString(properties);
}
@Override
public boolean isAir() {
return allayBlockState.getBlockType() == BlockTypes.AIR;
}
@Override
public BlockState getHandle() {
return allayBlockState;
}
public BlockState allayBlockState() { return allayBlockState; }
public boolean containsWater() { return containsWater; }
public JeBlockState jeBlockState() { return jeBlockState; }
}
@@ -0,0 +1,32 @@
package com.dfsek.terra.allay.delegate;
import org.allaymc.api.block.tag.BlockTags;
import org.allaymc.api.block.type.BlockType;
import com.dfsek.terra.allay.Mapping;
import com.dfsek.terra.api.block.state.BlockState;
/**
* @author daoge_cmd
*/
public record AllayBlockType(BlockType<?> allayBlockType) implements com.dfsek.terra.api.block.BlockType {
@Override
public BlockState getDefaultState() {
return new AllayBlockState(allayBlockType.getDefaultState(), Mapping.blockStateBeToJe(allayBlockType.getDefaultState()));
}
@Override
public boolean isSolid() {
return allayBlockType.getDefaultState().getBlockStateData().isSolid();
}
@Override
public boolean isWater() {
return allayBlockType.hasBlockTag(BlockTags.WATER);
}
@Override
public BlockType<?> getHandle() {
return allayBlockType;
}
}
@@ -0,0 +1,54 @@
package com.dfsek.terra.allay.delegate;
import org.allaymc.api.block.property.type.BlockPropertyTypes;
import org.allaymc.api.block.tag.BlockTags;
import org.allaymc.api.block.type.BlockTypes;
import org.allaymc.api.world.chunk.Chunk;
import org.jetbrains.annotations.NotNull;
import com.dfsek.terra.allay.Mapping;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.world.ServerWorld;
/**
* @author daoge_cmd
*/
public record AllayChunk(ServerWorld world, Chunk allayChunk) implements com.dfsek.terra.api.world.chunk.Chunk {
private static final org.allaymc.api.block.type.BlockState WATER = BlockTypes.WATER.ofState(BlockPropertyTypes.LIQUID_DEPTH.createValue(0));
@Override
public void setBlock(int x, int y, int z, BlockState data, boolean physics) {
AllayBlockState allayBlockState = (AllayBlockState) data;
allayChunk.setBlockState(x, y, z, allayBlockState.allayBlockState());
if (allayBlockState.containsWater() || allayChunk.getBlockState(x, y, z).getBlockType().hasBlockTag(BlockTags.WATER)) {
allayChunk.setBlockState(x, y, z, WATER, 1);
}
}
@Override
public @NotNull BlockState getBlock(int x, int y, int z) {
org.allaymc.api.block.type.BlockState blockState = allayChunk.getBlockState(x, y, z);
return new AllayBlockState(blockState, Mapping.blockStateBeToJe(blockState));
}
@Override
public int getX() {
return allayChunk.getX();
}
@Override
public int getZ() {
return allayChunk.getZ();
}
@Override
public ServerWorld getWorld() {
return world;
}
@Override
public Chunk getHandle() {
return allayChunk;
}
}
@@ -0,0 +1,37 @@
package com.dfsek.terra.allay.delegate;
import org.allaymc.api.item.enchantment.EnchantmentType;
import com.dfsek.terra.allay.Mapping;
import com.dfsek.terra.api.inventory.ItemStack;
import com.dfsek.terra.api.inventory.item.Enchantment;
/**
* @author daoge_cmd
*/
public record AllayEnchantment(EnchantmentType allayEnchantment) implements Enchantment {
@Override
public boolean canEnchantItem(ItemStack itemStack) {
return ((AllayItemStack)itemStack).allayItemStack().checkEnchantmentCompatibility(allayEnchantment);
}
@Override
public boolean conflictsWith(Enchantment other) {
return ((AllayEnchantment)other).allayEnchantment.isIncompatibleWith(allayEnchantment);
}
@Override
public String getID() {
return Mapping.enchantmentIdBeToJe(allayEnchantment.getIdentifier().toString());
}
@Override
public int getMaxLevel() {
return allayEnchantment.getMaxLevel();
}
@Override
public EnchantmentType getHandle() {
return allayEnchantment;
}
}
@@ -0,0 +1,47 @@
package com.dfsek.terra.allay.delegate;
import com.dfsek.terra.api.entity.Entity;
import com.dfsek.seismic.type.vector.Vector3;
import com.dfsek.terra.api.world.ServerWorld;
/**
* NOTICE: Entity is not supported currently, and this is a fake implementation.
*
* @author daoge_cmd
*/
public final class AllayFakeEntity implements Entity {
private final Object fakeHandle = new Object();
private Vector3 position;
private ServerWorld world;
public AllayFakeEntity(Vector3 position, ServerWorld world) {
this.position = position;
this.world = world;
}
@Override
public Vector3 position() {
return position;
}
@Override
public void position(Vector3 position) {
this.position = position;
}
@Override
public void world(ServerWorld world) {
this.world = world;
}
@Override
public ServerWorld world() {
return world;
}
@Override
public Object getHandle() {
return fakeHandle;
}
}
@@ -0,0 +1,36 @@
package com.dfsek.terra.allay.delegate;
import org.allaymc.api.item.ItemStack;
import org.allaymc.api.item.enchantment.EnchantmentInstance;
import org.allaymc.api.item.enchantment.EnchantmentType;
import java.util.HashMap;
import java.util.Map;
import com.dfsek.terra.api.inventory.item.Enchantment;
import com.dfsek.terra.api.inventory.item.ItemMeta;
/**
* @author daoge_cmd
*/
public record AllayItemMeta(ItemStack allayItemStack) implements ItemMeta {
@Override
public void addEnchantment(Enchantment enchantment, int level) {
EnchantmentType allayEnchantment = ((AllayEnchantment) enchantment).allayEnchantment();
allayItemStack.addEnchantment(allayEnchantment, (short) level);
}
@Override
public Map<Enchantment, Integer> getEnchantments() {
Map<Enchantment, Integer> results = new HashMap<>();
for (EnchantmentInstance allayEnchantmentInstance : allayItemStack.getEnchantments()) {
results.put(new AllayEnchantment(allayEnchantmentInstance.getType()), allayEnchantmentInstance.getLevel());
}
return results;
}
@Override
public ItemStack getHandle() {
return allayItemStack;
}
}
@@ -0,0 +1,50 @@
package com.dfsek.terra.allay.delegate;
import org.allaymc.api.item.ItemStack;
import org.allaymc.api.item.enchantment.EnchantmentInstance;
import com.dfsek.terra.api.inventory.Item;
import com.dfsek.terra.api.inventory.item.ItemMeta;
/**
* @author daoge_cmd
*/
public record AllayItemStack(ItemStack allayItemStack) implements com.dfsek.terra.api.inventory.ItemStack{
@Override
public int getAmount() {
return allayItemStack.getCount();
}
@Override
public void setAmount(int i) {
allayItemStack.setCount(i);
}
@Override
public Item getType() {
return new AllayItemType(allayItemStack.getItemType());
}
@Override
public ItemMeta getItemMeta() {
return new AllayItemMeta(allayItemStack);
}
@Override
public void setItemMeta(ItemMeta meta) {
ItemStack targetItem = ((AllayItemMeta) meta).allayItemStack();
allayItemStack.removeAllEnchantments();
for (EnchantmentInstance enchantment : targetItem.getEnchantments()) {
allayItemStack.addEnchantment(enchantment.getType(), enchantment.getLevel());
}
allayItemStack.setLore(targetItem.getLore());
allayItemStack.setDurability(targetItem.getDurability());
allayItemStack.setCustomName(targetItem.getCustomName());
allayItemStack.setMeta(targetItem.getMeta());
}
@Override
public ItemStack getHandle() {
return allayItemStack;
}
}
@@ -0,0 +1,34 @@
package com.dfsek.terra.allay.delegate;
import org.allaymc.api.item.type.ItemType;
import com.dfsek.terra.api.inventory.Item;
/**
* @author daoge_cmd
*/
public final class AllayItemType implements Item {
private final ItemType<?> allayItemType;
private final double maxDurability;
public AllayItemType(ItemType<?> allayItemType) {
this.allayItemType = allayItemType;
this.maxDurability = allayItemType.getItemData().maxDamage();
}
@Override
public com.dfsek.terra.api.inventory.ItemStack newItemStack(int amount) {
return new AllayItemStack(allayItemType.createItemStack(amount));
}
@Override
public double getMaxDurability() {
return maxDurability;
}
@Override
public ItemType<?> getHandle() {
return allayItemType;
}
}
@@ -0,0 +1,44 @@
package com.dfsek.terra.allay.delegate;
import org.allaymc.api.block.property.type.BlockPropertyTypes;
import org.allaymc.api.block.tag.BlockTags;
import org.allaymc.api.block.type.BlockTypes;
import org.allaymc.api.world.chunk.UnsafeChunk;
import org.jetbrains.annotations.NotNull;
import com.dfsek.terra.allay.Mapping;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.world.chunk.generation.ProtoChunk;
/**
* @author daoge_cmd
*/
public record AllayProtoChunk(UnsafeChunk allayChunk) implements ProtoChunk {
private static final org.allaymc.api.block.type.BlockState WATER = BlockTypes.WATER.ofState(BlockPropertyTypes.LIQUID_DEPTH.createValue(0));
@Override
public int getMaxHeight() {
return allayChunk.getDimensionInfo().maxHeight();
}
@Override
public void setBlock(int x, int y, int z, @NotNull BlockState blockState) {
AllayBlockState allayBlockState = (AllayBlockState) blockState;
allayChunk.setBlockState(x, y, z, allayBlockState.allayBlockState());
if(allayBlockState.containsWater() || allayChunk.getBlockState(x, y, z).getBlockType().hasBlockTag(BlockTags.WATER)) {
allayChunk.setBlockState(x, y, z, WATER, 1);
}
}
@Override
public @NotNull BlockState getBlock(int x, int y, int z) {
org.allaymc.api.block.type.BlockState blockState = allayChunk.getBlockState(x, y, z);
return new AllayBlockState(blockState, Mapping.blockStateBeToJe(blockState));
}
@Override
public UnsafeChunk getHandle() {
return allayChunk;
}
}
@@ -0,0 +1,101 @@
package com.dfsek.terra.allay.delegate;
import org.allaymc.api.block.property.type.BlockPropertyTypes;
import org.allaymc.api.block.tag.BlockTags;
import org.allaymc.api.block.type.BlockTypes;
import org.allaymc.api.world.generator.context.OtherChunkAccessibleContext;
import com.dfsek.terra.allay.Mapping;
import com.dfsek.terra.api.block.entity.BlockEntity;
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.seismic.type.vector.Vector3;
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;
import com.dfsek.terra.api.world.chunk.generation.ProtoWorld;
/**
* @author daoge_cmd
*/
public record AllayProtoWorld(AllayServerWorld allayServerWorld, OtherChunkAccessibleContext context) implements ProtoWorld {
private static final org.allaymc.api.block.type.BlockState WATER = BlockTypes.WATER.ofState(BlockPropertyTypes.LIQUID_DEPTH.createValue(0));
@Override
public int centerChunkX() {
return context.getCurrentChunk().getX();
}
@Override
public int centerChunkZ() {
return context.getCurrentChunk().getZ();
}
@Override
public ServerWorld getWorld() {
return allayServerWorld;
}
@Override
public void setBlockState(int x, int y, int z, BlockState data, boolean physics) {
AllayBlockState allayBlockState = (AllayBlockState) data;
context.setBlockState(x, y, z, allayBlockState.allayBlockState());
if(allayBlockState.containsWater() || context.getBlockState(x, y, z).getBlockType().hasBlockTag(BlockTags.WATER)) {
context.setBlockState(x, y, z, WATER, 1);
}
}
@Override
public BlockState getBlockState(int x, int y, int z) {
org.allaymc.api.block.type.BlockState blockState = context.getBlockState(x, y, z);
return new AllayBlockState(blockState, Mapping.blockStateBeToJe(blockState));
}
@Override
public Entity spawnEntity(double x, double y, double z, EntityType entityType) {
return new AllayFakeEntity(Vector3.of(x, y, z), allayServerWorld);
}
@Override
public BlockEntity getBlockEntity(int x, int y, int z) {
return null;
}
@Override
public ChunkGenerator getGenerator() {
return allayServerWorld.getGenerator();
}
@Override
public BiomeProvider getBiomeProvider() {
return allayServerWorld.getBiomeProvider();
}
@Override
public ConfigPack getPack() {
return allayServerWorld.getPack();
}
@Override
public long getSeed() {
return allayServerWorld.getSeed();
}
@Override
public int getMaxHeight() {
return allayServerWorld.getMaxHeight();
}
@Override
public int getMinHeight() {
return allayServerWorld.getMinHeight();
}
@Override
public AllayServerWorld getHandle() {
return allayServerWorld;
}
}
@@ -0,0 +1,83 @@
package com.dfsek.terra.allay.delegate;
import org.allaymc.api.world.Dimension;
import com.dfsek.terra.allay.Mapping;
import com.dfsek.terra.allay.generator.AllayGeneratorWrapper;
import com.dfsek.terra.api.block.entity.BlockEntity;
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.seismic.type.vector.Vector3;
import com.dfsek.terra.api.world.ServerWorld;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
import com.dfsek.terra.api.world.chunk.Chunk;
import com.dfsek.terra.api.world.chunk.generation.ChunkGenerator;
/**
* @author daoge_cmd
*/
public record AllayServerWorld(AllayGeneratorWrapper allayGeneratorWrapper, Dimension allayDimension) implements ServerWorld {
@Override
public Chunk getChunkAt(int x, int z) {
return new AllayChunk(this, allayDimension.getChunkService().getChunk(x, z));
}
@Override
public void setBlockState(int x, int y, int z, BlockState data, boolean physics) {
// In dimension#setBlockState() method, Water will be moved to layer 1 if it is placed at layer 0
allayDimension.setBlockState(x, y, z, ((AllayBlockState) data).allayBlockState());
}
@Override
public Entity spawnEntity(double x, double y, double z, EntityType entityType) {
return new AllayFakeEntity(Vector3.of(x, y, z), this);
}
@Override
public BlockState getBlockState(int x, int y, int z) {
org.allaymc.api.block.type.BlockState allayBlockState = allayDimension.getBlockState(x, y, z);
return new AllayBlockState(allayBlockState, Mapping.blockStateBeToJe(allayBlockState));
}
@Override
public BlockEntity getBlockEntity(int x, int y, int z) {
return null;
}
@Override
public ChunkGenerator getGenerator() {
return allayGeneratorWrapper.getHandle();
}
@Override
public BiomeProvider getBiomeProvider() {
return allayGeneratorWrapper.getBiomeProvider();
}
@Override
public ConfigPack getPack() {
return allayGeneratorWrapper.getConfigPack();
}
@Override
public long getSeed() {
return allayGeneratorWrapper.getSeed();
}
@Override
public int getMaxHeight() {
return allayDimension.getDimensionInfo().maxHeight();
}
@Override
public int getMinHeight() {
return allayDimension.getDimensionInfo().minHeight();
}
@Override
public Object getHandle() {
return allayDimension;
}
}
@@ -0,0 +1,180 @@
package com.dfsek.terra.allay.generator;
import org.allaymc.api.utils.AllayStringUtils;
import org.allaymc.api.world.biome.BiomeType;
import org.allaymc.api.world.chunk.UnsafeChunk;
import org.allaymc.api.world.generator.WorldGenerator;
import org.allaymc.api.world.generator.context.NoiseContext;
import org.allaymc.api.world.generator.context.PopulateContext;
import org.allaymc.api.world.generator.function.Noiser;
import org.allaymc.api.world.generator.function.Populator;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import com.dfsek.terra.allay.TerraAllayPlugin;
import com.dfsek.terra.allay.delegate.AllayProtoChunk;
import com.dfsek.terra.allay.delegate.AllayProtoWorld;
import com.dfsek.terra.allay.delegate.AllayServerWorld;
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.api.world.info.WorldProperties;
/**
* @author daoge_cmd
*/
public class AllayGeneratorWrapper implements GeneratorWrapper {
protected static final String OPTION_PACK_NAME = "pack";
protected static final String OPTION_SEED = "seed";
protected final BiomeProvider biomeProvider;
protected final long seed;
protected final WorldGenerator allayWorldGenerator;
protected ChunkGenerator chunkGenerator;
protected ConfigPack configPack;
protected WorldProperties worldProperties;
protected AllayServerWorld allayServerWorld;
public AllayGeneratorWrapper(String preset) {
Map<String, String> options = AllayStringUtils.parseOptions(preset);
String packName = options.get(OPTION_PACK_NAME);
if(packName == null) {
throw new IllegalArgumentException("Missing config pack name");
}
this.seed = Long.parseLong(options.getOrDefault(OPTION_SEED, "0"));
this.configPack = getConfigPack(packName);
this.chunkGenerator = createGenerator(this.configPack);
this.biomeProvider = this.configPack.getBiomeProvider();
this.allayWorldGenerator = WorldGenerator
.builder()
.name("TERRA")
.preset(preset)
.noisers(new AllayNoiser())
.populators(new AllayPopulator())
.onDimensionSet(dimension -> {
this.allayServerWorld = new AllayServerWorld(this, dimension);
this.worldProperties = new WorldProperties() {
private final Object fakeHandle = new Object();
@Override
public long getSeed() {
return seed;
}
@Override
public int getMaxHeight() {
return dimension.getDimensionInfo().maxHeight();
}
@Override
public int getMinHeight() {
return dimension.getDimensionInfo().minHeight();
}
@Override
public Object getHandle() {
return fakeHandle;
}
};
})
.build();
}
@Override
public ChunkGenerator getHandle() {
return chunkGenerator;
}
public BiomeProvider getBiomeProvider() {
return this.biomeProvider;
}
public ConfigPack getConfigPack() {
return this.configPack;
}
public void setConfigPack(ConfigPack configPack) {
this.configPack = configPack;
this.chunkGenerator = createGenerator(this.configPack);
}
public long getSeed() {
return this.seed;
}
public WorldGenerator getAllayWorldGenerator() {
return this.allayWorldGenerator;
}
protected class AllayNoiser implements Noiser {
@Override
public boolean apply(NoiseContext context) {
UnsafeChunk chunk = context.getCurrentChunk();
int chunkX = chunk.getX();
int chunkZ = chunk.getZ();
chunkGenerator.generateChunkData(
new AllayProtoChunk(chunk),
worldProperties, biomeProvider,
chunkX, chunkZ
);
int minHeight = context.getDimensionInfo().minHeight();
int maxHeight = context.getDimensionInfo().maxHeight();
for(int x = 0; x < 16; x++) {
for(int y = minHeight; y < maxHeight; y++) {
for(int z = 0; z < 16; z++) {
chunk.setBiome(
x, y, z,
(BiomeType) biomeProvider.getBiome(chunkX * 16 + x, y, chunkZ * 16 + z, seed).getPlatformBiome().getHandle()
);
}
}
}
return true;
}
@Override
public String getName() {
return "TERRA_NOISER";
}
}
protected class AllayPopulator implements Populator {
@Override
public boolean apply(PopulateContext context) {
AllayProtoWorld tmp = new AllayProtoWorld(allayServerWorld, context);
try {
for(GenerationStage generationStage : configPack.getStages()) {
generationStage.populate(tmp);
}
} catch(Exception e) {
TerraAllayPlugin.INSTANCE.getPluginLogger().error("Error while populating chunk", e);
}
return true;
}
@Override
public String getName() {
return "TERRA_POPULATOR";
}
}
protected static ConfigPack getConfigPack(String packName) {
Optional<ConfigPack> byId = TerraAllayPlugin.PLATFORM.getConfigRegistry().getByID(packName);
return byId.orElseGet(
() -> TerraAllayPlugin.PLATFORM.getConfigRegistry().getByID(packName.toUpperCase(Locale.ENGLISH))
.orElseThrow(() -> new IllegalArgumentException("Cant find terra config pack named " + packName))
);
}
protected static ChunkGenerator createGenerator(ConfigPack configPack) {
return configPack.getGeneratorProvider().newInstance(configPack);
}
}
@@ -0,0 +1,36 @@
package com.dfsek.terra.allay.handle;
import org.allaymc.api.registry.Registries;
import org.allaymc.api.utils.Identifier;
import java.util.Set;
import java.util.stream.Collectors;
import com.dfsek.terra.allay.Mapping;
import com.dfsek.terra.allay.delegate.AllayEnchantment;
import com.dfsek.terra.allay.delegate.AllayItemType;
import com.dfsek.terra.api.handle.ItemHandle;
import com.dfsek.terra.api.inventory.Item;
import com.dfsek.terra.api.inventory.item.Enchantment;
/**
* @author daoge_cmd
*/
public class AllayItemHandle implements ItemHandle {
@Override
public Item createItem(String data) {
return new AllayItemType(Mapping.itemIdJeToBe(data));
}
@Override
public Enchantment getEnchantment(String id) {
return new AllayEnchantment(Registries.ENCHANTMENTS.getByK2(new Identifier(Mapping.enchantmentIdJeToBe(id))));
}
@Override
public Set<Enchantment> getEnchantments() {
return Registries.ENCHANTMENTS.getContent().m1().values().stream()
.map(AllayEnchantment::new)
.collect(Collectors.toSet());
}
}
@@ -0,0 +1,38 @@
package com.dfsek.terra.allay.handle;
import org.jetbrains.annotations.NotNull;
import com.dfsek.terra.allay.JeBlockState;
import com.dfsek.terra.allay.Mapping;
import com.dfsek.terra.allay.delegate.AllayBlockState;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.entity.EntityType;
import com.dfsek.terra.api.handle.WorldHandle;
/**
* @author daoge_cmd
*/
public class AllayWorldHandle implements WorldHandle {
@Override
public @NotNull BlockState createBlockState(@NotNull String data) {
JeBlockState jeBlockState = JeBlockState.fromString(data);
return new AllayBlockState(Mapping.blockStateJeToBe(jeBlockState), jeBlockState);
}
@Override
public @NotNull BlockState air() {
return AllayBlockState.AIR;
}
@Override
public @NotNull EntityType getEntity(@NotNull String id) {
return new EntityType() {
private final Object fakeEntityType = new Object();
@Override
public Object getHandle() {
return fakeEntityType;
}
};
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,18 @@
{
"entrance": "com.dfsek.terra.allay.TerraAllayPlugin",
"name": "Terra",
"authors": [
"daoge_cmd",
"IWareQ",
"dfsek",
"duplexsystem",
"Astrash",
"solonovamax",
"Sancires",
"Aureus",
"RogueShade"
],
"version": "@VERSION@",
"description": "@DESCRIPTION@",
"website": "@WIKI@"
}
+1 -1
View File
@@ -4,7 +4,7 @@ plugins {
dependencies {
shaded(project(":platforms:bukkit:common"))
shaded(project(":platforms:bukkit:nms:v1_21", configuration = "reobf"))
shaded(project(":platforms:bukkit:nms:v1_21_3", configuration = "reobf"))
shaded("xyz.jpenilla", "reflection-remapper", Versions.Bukkit.reflectionRemapper)
}
+1 -1
View File
@@ -11,5 +11,5 @@ dependencies {
shadedApi("com.google.guava", "guava", Versions.Libraries.Internal.guava)
shadedApi("org.incendo", "cloud-paper", Versions.Libraries.cloudPaper)
shadedApi("org.incendo", "cloud-paper", Versions.Bukkit.cloud)
}
@@ -4,18 +4,15 @@ 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.events.config.pack.ConfigPackPreLoadEvent;
import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.bukkit.config.PreLoadCompatibilityOptions;
import com.dfsek.terra.bukkit.config.VanillaBiomeProperties;
public class BukkitAddon implements BaseAddon {
private static final Version VERSION = Versions.getVersion(1, 0, 0);
private final PlatformImpl terraBukkitPlugin;
protected final PlatformImpl terraBukkitPlugin;
public BukkitAddon(PlatformImpl terraBukkitPlugin) {
this.terraBukkitPlugin = terraBukkitPlugin;
@@ -28,16 +25,6 @@ public class BukkitAddon implements BaseAddon {
.register(this, ConfigPackPreLoadEvent.class)
.then(event -> event.getPack().getContext().put(event.loadTemplate(new PreLoadCompatibilityOptions())))
.global();
terraBukkitPlugin.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
@@ -21,7 +21,7 @@ import io.papermc.lib.PaperLib;
import org.bukkit.Location;
import com.dfsek.terra.api.entity.Entity;
import com.dfsek.terra.api.util.vector.Vector3;
import com.dfsek.seismic.type.vector.Vector3;
import com.dfsek.terra.api.world.ServerWorld;
import com.dfsek.terra.bukkit.world.BukkitAdapter;
@@ -21,7 +21,7 @@ import io.papermc.lib.PaperLib;
import org.bukkit.Location;
import com.dfsek.terra.api.entity.Player;
import com.dfsek.terra.api.util.vector.Vector3;
import com.dfsek.seismic.type.vector.Vector3;
import com.dfsek.terra.api.world.ServerWorld;
import com.dfsek.terra.bukkit.world.BukkitAdapter;
@@ -20,6 +20,9 @@ package com.dfsek.terra.bukkit;
import com.dfsek.tectonic.api.TypeRegistry;
import com.dfsek.tectonic.api.depth.DepthTracker;
import com.dfsek.tectonic.api.exception.LoadException;
import com.dfsek.terra.bukkit.nms.Initializer;
import org.bukkit.Bukkit;
import org.bukkit.entity.EntityType;
import org.jetbrains.annotations.NotNull;
@@ -51,7 +54,13 @@ public class PlatformImpl extends AbstractPlatform {
private final TerraBukkitPlugin plugin;
private int generationThreads;
public PlatformImpl(TerraBukkitPlugin plugin) {
generationThreads = getGenerationThreadsWithReflection("ca.spottedleaf.moonrise.common.util.MoonriseCommon", "WORKER_THREADS", "Moonrise");
if (generationThreads == 0) {
generationThreads = 1;
}
this.plugin = plugin;
load();
}
@@ -63,8 +72,7 @@ public class PlatformImpl extends AbstractPlatform {
@Override
public boolean reload() {
getTerraConfig().load(this);
getRawConfigRegistry().clear();
boolean succeed = getRawConfigRegistry().loadAll(this);
boolean succeed = loadConfigPacks();
Bukkit.getWorlds().forEach(world -> {
if(world.getGenerator() instanceof BukkitChunkGeneratorWrapper wrapper) {
@@ -90,7 +98,7 @@ public class PlatformImpl extends AbstractPlatform {
@Override
protected Iterable<BaseAddon> platformAddon() {
return List.of(new BukkitAddon(this));
return List.of(Initializer.nmsAddon(this));
}
@Override
@@ -108,6 +116,11 @@ public class PlatformImpl extends AbstractPlatform {
return itemHandle;
}
@Override
public int getGenerationThreads() {
return generationThreads;
}
@Override
public void register(TypeRegistry registry) {
super.register(registry);
@@ -194,7 +194,7 @@ public class TerraBukkitPlugin extends JavaPlugin {
@Override
public @Nullable
ChunkGenerator getDefaultWorldGenerator(@NotNull String worldName, String id) {
if(id == null || id.trim().equals("")) { return null; }
if(id == null || id.trim().isEmpty()) { return null; }
return new BukkitChunkGeneratorWrapper(generatorMap.computeIfAbsent(worldName, name -> {
ConfigPack pack = platform.getConfigRegistry().getByID(id).orElseThrow(
() -> new IllegalArgumentException("No such config pack \"" + id + "\""));
@@ -1,58 +0,0 @@
package com.dfsek.terra.bukkit.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;
public class VanillaBiomeProperties implements ConfigTemplate, Properties {
@Value("colors.grass")
@Default
private Integer grassColor = null;
@Value("colors.fog")
@Default
private Integer fogColor = null;
@Value("colors.water")
@Default
private Integer waterColor = null;
@Value("colors.water-fog")
@Default
private Integer waterFogColor = null;
@Value("colors.foliage")
@Default
private Integer foliageColor = null;
@Value("colors.sky")
@Default
private Integer skyColor = null;
public Integer getFogColor() {
return fogColor;
}
public Integer getFoliageColor() {
return foliageColor;
}
public Integer getGrassColor() {
return grassColor;
}
public Integer getWaterColor() {
return waterColor;
}
public Integer getWaterFogColor() {
return waterFogColor;
}
public Integer getSkyColor() {
return skyColor;
}
}
@@ -40,13 +40,6 @@ public class BukkitWorldHandle implements WorldHandle {
@Override
public synchronized @NotNull BlockState createBlockState(@NotNull String data) {
if(data.equals("minecraft:grass")) { //TODO: remove in 7.0
data = "minecraft:short_grass";
logger.warn(
"Translating minecraft:grass to minecraft:short_grass. In 1.20.3 minecraft:grass was renamed to minecraft:short_grass" +
". You are advised to perform this rename in your config backs as this translation will be removed in the next major " +
"version of Terra.");
}
org.bukkit.block.data.BlockData bukkitData = Bukkit.createBlockData(
data); // somehow bukkit managed to make this not thread safe! :)
return BukkitBlockState.newInstance(bukkitData);
@@ -1,5 +1,7 @@
package com.dfsek.terra.bukkit.nms;
import com.dfsek.terra.bukkit.BukkitAddon;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -13,20 +15,11 @@ public interface Initializer {
static boolean init(PlatformImpl platform) {
Logger logger = LoggerFactory.getLogger(Initializer.class);
try {
String packageVersion = NMS;
if(NMS.equals("v1_21_1")) {
packageVersion = "v1_21";
}
Class<?> initializerClass = Class.forName(TERRA_PACKAGE + "." + packageVersion + ".NMSInitializer");
try {
Initializer initializer = (Initializer) initializerClass.getConstructor().newInstance();
initializer.initialize(platform);
} catch(ReflectiveOperationException e) {
throw new RuntimeException("Error initializing NMS bindings. Report this to Terra.", e);
}
} catch(ClassNotFoundException e) {
Initializer initializer = constructInitializer();
if(initializer != null) {
initializer.initialize(platform);
} else {
logger.error("NMS bindings for version {} do not exist. Support for this version is limited.", NMS);
logger.error("This is usually due to running Terra on an unsupported Minecraft version.");
String bypassKey = "IKnowThereAreNoNMSBindingsFor" + NMS + "ButIWillProceedAnyway";
@@ -49,8 +42,34 @@ public interface Initializer {
logger.error("Since you enabled the \"{}\" flag, we won't disable Terra. But be warned.", bypassKey);
}
}
return true;
}
static BukkitAddon nmsAddon(PlatformImpl platform) {
Initializer initializer = constructInitializer();
return initializer != null ? initializer.getNMSAddon(platform) : new BukkitAddon(platform);
}
private static Initializer constructInitializer() {
try {
String packageVersion = NMS;
if (NMS.equals("v1_21_4")) {
packageVersion = "v1_21_3";
}
Class<?> initializerClass = Class.forName(TERRA_PACKAGE + "." + packageVersion + ".NMSInitializer");
try {
return (Initializer) initializerClass.getConstructor().newInstance();
} catch(ReflectiveOperationException e) {
throw new RuntimeException("Error initializing NMS bindings. Report this to Terra.", e);
}
} catch(ClassNotFoundException e) {
return null;
}
}
void initialize(PlatformImpl plugin);
BukkitAddon getNMSAddon(PlatformImpl plugin);
}
@@ -20,14 +20,6 @@ public class BukkitUtils {
}
public static EntityType getEntityType(String id) {
if(!id.contains(":")) { //TODO: remove in 7.0
String newid = "minecraft:" + id.toLowerCase();
;
logger.warn(
"Translating " + id + " to " + newid + ". In 1.20.3 entity parsing was reworked" +
". You are advised to perform this rename in your config backs as this translation will be removed in the next major " +
"version of Terra.");
}
if(!id.startsWith("minecraft:")) throw new IllegalArgumentException("Invalid entity identifier " + id);
String entityID = id.toUpperCase(Locale.ROOT).substring(10);
@@ -34,7 +34,7 @@ import com.dfsek.terra.api.command.CommandSender;
import com.dfsek.terra.api.entity.Entity;
import com.dfsek.terra.api.inventory.ItemStack;
import com.dfsek.terra.api.inventory.item.Enchantment;
import com.dfsek.terra.api.util.vector.Vector3;
import com.dfsek.seismic.type.vector.Vector3;
import com.dfsek.terra.api.world.ServerWorld;
import com.dfsek.terra.api.world.chunk.Chunk;
import com.dfsek.terra.api.world.info.WorldProperties;
@@ -1,9 +1,9 @@
package com.dfsek.terra.bukkit.world;
import com.dfsek.terra.api.world.info.WorldProperties;
import org.bukkit.generator.WorldInfo;
import com.dfsek.terra.api.world.info.WorldProperties;
public class BukkitWorldProperties implements WorldProperties {
private final WorldInfo delegate;
@@ -23,7 +23,7 @@ import org.bukkit.block.Sign;
import com.dfsek.terra.api.block.entity.BlockEntity;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.util.vector.Vector3;
import com.dfsek.seismic.type.vector.Vector3;
import com.dfsek.terra.bukkit.world.BukkitAdapter;
import com.dfsek.terra.bukkit.world.block.data.BukkitBlockState;
@@ -1,78 +0,0 @@
package com.dfsek.terra.bukkit.nms.v1_21;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeSpecialEffects;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.bukkit.config.VanillaBiomeProperties;
public class NMSBiomeInjector {
public static <T> Optional<Holder<T>> getEntry(Registry<T> registry, ResourceLocation identifier) {
return registry.getOptional(identifier)
.flatMap(registry::getResourceKey)
.flatMap(registry::getHolder);
}
public static Biome createBiome(com.dfsek.terra.api.world.biome.Biome biome, Biome vanilla)
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
Biome.BiomeBuilder builder = new Biome.BiomeBuilder();
builder
.downfall(vanilla.climateSettings.downfall())
.temperature(vanilla.getBaseTemperature())
.mobSpawnSettings(vanilla.getMobSettings())
.generationSettings(vanilla.getGenerationSettings());
BiomeSpecialEffects.Builder effects = new BiomeSpecialEffects.Builder();
effects.grassColorModifier(vanilla.getSpecialEffects().getGrassColorModifier());
VanillaBiomeProperties vanillaBiomeProperties = biome.getContext().get(VanillaBiomeProperties.class);
effects.fogColor(Objects.requireNonNullElse(vanillaBiomeProperties.getFogColor(), vanilla.getFogColor()))
.waterColor(Objects.requireNonNullElse(vanillaBiomeProperties.getWaterColor(), vanilla.getWaterColor()))
.waterFogColor(Objects.requireNonNullElse(vanillaBiomeProperties.getWaterFogColor(), vanilla.getWaterFogColor()))
.skyColor(Objects.requireNonNullElse(vanillaBiomeProperties.getSkyColor(), vanilla.getSkyColor()));
if(vanillaBiomeProperties.getFoliageColor() == null) {
vanilla.getSpecialEffects().getFoliageColorOverride().ifPresent(effects::foliageColorOverride);
} else {
effects.foliageColorOverride(vanillaBiomeProperties.getFoliageColor());
}
if(vanillaBiomeProperties.getGrassColor() == null) {
vanilla.getSpecialEffects().getGrassColorOverride().ifPresent(effects::grassColorOverride);
} else {
// grass
effects.grassColorOverride(vanillaBiomeProperties.getGrassColor());
}
vanilla.getAmbientLoop().ifPresent(effects::ambientLoopSound);
vanilla.getAmbientAdditions().ifPresent(effects::ambientAdditionsSound);
vanilla.getAmbientMood().ifPresent(effects::ambientMoodSound);
vanilla.getBackgroundMusic().ifPresent(effects::backgroundMusic);
vanilla.getAmbientParticle().ifPresent(effects::ambientParticle);
builder.specialEffects(effects.build());
return builder.build();
}
public static String createBiomeID(ConfigPack pack, com.dfsek.terra.api.registry.key.RegistryKey biomeID) {
return pack.getID()
.toLowerCase() + "/" + biomeID.getNamespace().toLowerCase(Locale.ROOT) + "/" + biomeID.getID().toLowerCase(Locale.ROOT);
}
}
@@ -1,63 +0,0 @@
package com.dfsek.terra.bukkit.nms.v1_21;
import net.minecraft.core.Holder;
import net.minecraft.core.Holder.Reference;
import net.minecraft.core.MappedRegistry;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.StructureManager;
import net.minecraft.world.level.chunk.status.WorldGenContext;
import xyz.jpenilla.reflectionremapper.ReflectionRemapper;
import xyz.jpenilla.reflectionremapper.proxy.ReflectionProxyFactory;
import xyz.jpenilla.reflectionremapper.proxy.annotation.FieldGetter;
import xyz.jpenilla.reflectionremapper.proxy.annotation.FieldSetter;
import xyz.jpenilla.reflectionremapper.proxy.annotation.MethodName;
import xyz.jpenilla.reflectionremapper.proxy.annotation.Proxies;
public class Reflection {
public static final MappedRegistryProxy MAPPED_REGISTRY;
public static final StructureManagerProxy STRUCTURE_MANAGER;
public static final ReferenceProxy REFERENCE;
public static final ChunkMapProxy CHUNKMAP;
static {
ReflectionRemapper reflectionRemapper = ReflectionRemapper.forReobfMappingsInPaperJar();
ReflectionProxyFactory reflectionProxyFactory = ReflectionProxyFactory.create(reflectionRemapper,
Reflection.class.getClassLoader());
MAPPED_REGISTRY = reflectionProxyFactory.reflectionProxy(MappedRegistryProxy.class);
STRUCTURE_MANAGER = reflectionProxyFactory.reflectionProxy(StructureManagerProxy.class);
REFERENCE = reflectionProxyFactory.reflectionProxy(ReferenceProxy.class);
CHUNKMAP = reflectionProxyFactory.reflectionProxy(ChunkMapProxy.class);
}
@Proxies(MappedRegistry.class)
public interface MappedRegistryProxy {
@FieldSetter("frozen")
void setFrozen(MappedRegistry<?> instance, boolean frozen);
}
@Proxies(StructureManager.class)
public interface StructureManagerProxy {
@FieldGetter("level")
LevelAccessor getLevel(StructureManager instance);
}
@Proxies(Holder.Reference.class)
public interface ReferenceProxy {
@MethodName("bindValue")
<T> void invokeBindValue(Reference<T> instance, T value);
}
@Proxies(ChunkMap.class)
public interface ChunkMapProxy {
@FieldSetter("worldGenContext")
void setWorldGenContext(ChunkMap instance, WorldGenContext worldGenContext);
}
}
@@ -1,8 +1,10 @@
apply(plugin = "io.papermc.paperweight.userdev")
plugins {
id("io.papermc.paperweight.userdev")
}
dependencies {
api(project(":platforms:bukkit:common"))
paperDevBundle(Versions.Bukkit.paperDevBundle)
paperweight.paperDevBundle(Versions.Bukkit.paperDevBundle)
implementation("xyz.jpenilla", "reflection-remapper", Versions.Bukkit.reflectionRemapper)
}
@@ -1,11 +1,13 @@
package com.dfsek.terra.bukkit.nms.v1_21;
package com.dfsek.terra.bukkit.nms.v1_21_3;
import com.dfsek.terra.bukkit.nms.v1_21_3.config.VanillaBiomeProperties;
import com.google.common.collect.ImmutableMap;
import net.minecraft.core.Holder;
import net.minecraft.core.Holder.Reference;
import net.minecraft.core.HolderSet;
import net.minecraft.core.HolderSet.Named;
import net.minecraft.core.MappedRegistry;
import net.minecraft.core.RegistrationInfo;
import net.minecraft.core.WritableRegistry;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
@@ -17,9 +19,11 @@ import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import com.dfsek.terra.bukkit.world.BukkitPlatformBiome;
import com.dfsek.terra.registry.master.ConfigRegistry;
@@ -33,17 +37,22 @@ public class AwfulBukkitHacks {
public static void registerBiomes(ConfigRegistry configRegistry) {
try {
LOGGER.info("Hacking biome registry...");
WritableRegistry<Biome> biomeRegistry = (WritableRegistry<Biome>) RegistryFetcher.biomeRegistry();
MappedRegistry<Biome> biomeRegistry = (MappedRegistry<Biome>) RegistryFetcher.biomeRegistry();
Reflection.MAPPED_REGISTRY.setFrozen((MappedRegistry<?>) biomeRegistry, false);
// Unfreeze the biome registry to allow modification
Reflection.MAPPED_REGISTRY.setFrozen(biomeRegistry, false);
// Register the terra biomes to the registry
configRegistry.forEach(pack -> pack.getRegistry(com.dfsek.terra.api.world.biome.Biome.class).forEach((key, biome) -> {
try {
BukkitPlatformBiome platformBiome = (BukkitPlatformBiome) biome.getPlatformBiome();
NamespacedKey vanillaBukkitKey = platformBiome.getHandle().getKey();
ResourceLocation vanillaMinecraftKey = ResourceLocation.fromNamespaceAndPath(vanillaBukkitKey.getNamespace(),
vanillaBukkitKey.getKey());
Biome platform = NMSBiomeInjector.createBiome(biome, Objects.requireNonNull(biomeRegistry.get(vanillaMinecraftKey)));
VanillaBiomeProperties vanillaBiomeProperties = biome.getContext().get(VanillaBiomeProperties.class);
Biome platform = NMSBiomeInjector.createBiome(biomeRegistry.get(vanillaMinecraftKey).orElseThrow().value(), vanillaBiomeProperties);
ResourceKey<Biome> delegateKey = ResourceKey.create(
Registries.BIOME,
@@ -63,14 +72,14 @@ public class AwfulBukkitHacks {
}
}));
Reflection.MAPPED_REGISTRY.setFrozen((MappedRegistry<?>) biomeRegistry, true); // freeze registry again :)
Reflection.MAPPED_REGISTRY.setFrozen(biomeRegistry, true); // freeze registry again :)
LOGGER.info("Doing tag garbage....");
Map<TagKey<Biome>, List<Holder<Biome>>> collect = biomeRegistry
.getTags() // streamKeysAndEntries
.collect(HashMap::new,
(map, pair) ->
map.put(pair.getFirst(), new ArrayList<>(pair.getSecond().stream().toList())),
map.put(pair.key(), new ArrayList<>(Reflection.HOLDER_SET.invokeContents(pair).stream().toList())),
HashMap::putAll);
terraBiomeMap
@@ -91,12 +100,51 @@ public class AwfulBukkitHacks {
() -> LOGGER.error("No such biome: {}", tb))),
() -> LOGGER.error("No vanilla biome: {}", vb)));
biomeRegistry.resetTags();
biomeRegistry.bindTags(ImmutableMap.copyOf(collect));
resetTags(biomeRegistry);
bindTags(biomeRegistry, collect);
} catch(SecurityException | IllegalArgumentException exception) {
throw new RuntimeException(exception);
}
}
private static <T> void bindTags(MappedRegistry<T> registry, Map<TagKey<T>, List<Holder<T>>> tagEntries) {
Map<Holder.Reference<T>, List<TagKey<T>>> map = new IdentityHashMap<>();
Reflection.MAPPED_REGISTRY.getByKey(registry).values().forEach(entry -> map.put(entry, new ArrayList<>()));
tagEntries.forEach((tag, entries) -> {
for (Holder<T> holder : entries) {
// if (!holder.canSerializeIn(registry.asLookup())) {
// throw new IllegalStateException("Can't create named set " + tag + " containing value " + holder + " from outside registry " + this);
// }
if (!(holder instanceof Holder.Reference<T> reference)) {
throw new IllegalStateException("Found direct holder " + holder + " value in tag " + tag);
}
map.get(reference).add(tag);
}
});
// Set<TagKey<T>> set = Sets.difference(registry.tags.keySet(), tagEntries.keySet());
// if (!set.isEmpty()) {
// LOGGER.warn(
// "Not all defined tags for registry {} are present in data pack: {}",
// registry.key(),
// set.stream().map(tag -> tag.location().toString()).sorted().collect(Collectors.joining(", "))
// );
// }
Map<TagKey<T>, HolderSet.Named<T>> map2 = new IdentityHashMap<>(registry.getTags().collect(Collectors.toMap(
Named::key,
(named) -> named
)));
tagEntries.forEach((tag, entries) -> Reflection.HOLDER_SET.invokeBind(map2.computeIfAbsent(tag, key -> Reflection.MAPPED_REGISTRY.invokeCreateTag(registry, key)), entries));
map.forEach(Reflection.HOLDER_REFERENCE::invokeBindTags);
Reflection.MAPPED_REGISTRY.setAllTags(registry, Reflection.MAPPED_REGISTRY_TAG_SET.invokeFromMap(map2));
}
private static void resetTags(MappedRegistry<?> registry) {
registry.getTags().forEach(entryList -> Reflection.HOLDER_SET.invokeBind(entryList, List.of()));
Reflection.MAPPED_REGISTRY.getByKey(registry).values().forEach(entry -> Reflection.HOLDER_REFERENCE.invokeBindTags(entry, Set.of()));
}
}
@@ -0,0 +1,31 @@
package com.dfsek.terra.bukkit.nms.v1_21_3;
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.bukkit.BukkitAddon;
import com.dfsek.terra.bukkit.PlatformImpl;
import com.dfsek.terra.bukkit.nms.v1_21_3.config.VanillaBiomeProperties;
public class NMSAddon extends BukkitAddon {
public NMSAddon(PlatformImpl platform) {
super(platform);
}
@Override
public void initialize() {
super.initialize();
terraBukkitPlugin.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();
}
}
@@ -1,4 +1,4 @@
package com.dfsek.terra.bukkit.nms.v1_21;
package com.dfsek.terra.bukkit.nms.v1_21_3;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.biome.Biome;
@@ -0,0 +1,102 @@
package com.dfsek.terra.bukkit.nms.v1_21_3;
import net.minecraft.core.Holder;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeGenerationSettings;
import net.minecraft.world.level.biome.BiomeSpecialEffects;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.bukkit.nms.v1_21_3.config.VanillaBiomeProperties;
public class NMSBiomeInjector {
public static <T> Optional<Holder<T>> getEntry(Registry<T> registry, ResourceLocation identifier) {
return registry.getOptional(identifier)
.flatMap(registry::getResourceKey)
.flatMap(registry::get);
}
public static Biome createBiome(Biome vanilla, VanillaBiomeProperties vanillaBiomeProperties)
throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
Biome.BiomeBuilder builder = new Biome.BiomeBuilder();
BiomeSpecialEffects.Builder effects = new BiomeSpecialEffects.Builder();
effects.fogColor(Objects.requireNonNullElse(vanillaBiomeProperties.getFogColor(), vanilla.getFogColor()))
.waterColor(Objects.requireNonNullElse(vanillaBiomeProperties.getWaterColor(), vanilla.getWaterColor()))
.waterFogColor(Objects.requireNonNullElse(vanillaBiomeProperties.getWaterFogColor(), vanilla.getWaterFogColor()))
.skyColor(Objects.requireNonNullElse(vanillaBiomeProperties.getSkyColor(), vanilla.getSkyColor()))
.grassColorModifier(Objects.requireNonNullElse(vanillaBiomeProperties.getGrassColorModifier(), vanilla.getSpecialEffects().getGrassColorModifier()));
// .grassColorOverride(Objects.requireNonNullElse(vanillaBiomeProperties.getGrassColor(), vanilla.getSpecialEffects().getGrassColorOverride().orElseGet(() -> Reflection.BIOME.invokeGrassColorFromTexture(vanilla))))
// .foliageColorOverride(Objects.requireNonNullElse(vanillaBiomeProperties.getFoliageColor(), vanilla.getFoliageColor()));
if(vanillaBiomeProperties.getGrassColor() == null) {
vanilla.getSpecialEffects().getGrassColorOverride().ifPresent(effects::grassColorOverride);
} else {
effects.grassColorOverride(vanillaBiomeProperties.getGrassColor());
}
if(vanillaBiomeProperties.getFoliageColor() == null) {
vanilla.getSpecialEffects().getFoliageColorOverride().ifPresent(effects::foliageColorOverride);
} else {
effects.foliageColorOverride(vanillaBiomeProperties.getFoliageColor());
}
if(vanillaBiomeProperties.getParticleConfig() == null) {
vanilla.getSpecialEffects().getAmbientParticleSettings().ifPresent(effects::ambientParticle);
} else {
effects.ambientParticle(vanillaBiomeProperties.getParticleConfig());
}
if(vanillaBiomeProperties.getLoopSound() == null) {
vanilla.getSpecialEffects().getAmbientLoopSoundEvent().ifPresent(effects::ambientLoopSound);
} else {
RegistryFetcher.soundEventRegistry().get(vanillaBiomeProperties.getLoopSound().location()).ifPresent(effects::ambientLoopSound);
}
if(vanillaBiomeProperties.getMoodSound() == null) {
vanilla.getSpecialEffects().getAmbientMoodSettings().ifPresent(effects::ambientMoodSound);
} else {
effects.ambientMoodSound(vanillaBiomeProperties.getMoodSound());
}
if(vanillaBiomeProperties.getAdditionsSound() == null) {
vanilla.getSpecialEffects().getAmbientAdditionsSettings().ifPresent(effects::ambientAdditionsSound);
} else {
effects.ambientAdditionsSound(vanillaBiomeProperties.getAdditionsSound());
}
if(vanillaBiomeProperties.getMusic() == null) {
vanilla.getSpecialEffects().getBackgroundMusic().ifPresent(effects::backgroundMusic);
} else {
effects.backgroundMusic(vanillaBiomeProperties.getMusic());
}
builder.hasPrecipitation(Objects.requireNonNullElse(vanillaBiomeProperties.getPrecipitation(), vanilla.hasPrecipitation()));
builder.temperature(Objects.requireNonNullElse(vanillaBiomeProperties.getTemperature(), vanilla.getBaseTemperature()));
builder.downfall(Objects.requireNonNullElse(vanillaBiomeProperties.getDownfall(), vanilla.climateSettings.downfall()));
builder.temperatureAdjustment(Objects.requireNonNullElse(vanillaBiomeProperties.getTemperatureModifier(), vanilla.climateSettings.temperatureModifier()));
builder.mobSpawnSettings(Objects.requireNonNullElse(vanillaBiomeProperties.getSpawnSettings(), vanilla.getMobSettings()));
return builder
.specialEffects(effects.build())
.generationSettings(new BiomeGenerationSettings.PlainBuilder().build())
.build();
}
public static String createBiomeID(ConfigPack pack, com.dfsek.terra.api.registry.key.RegistryKey biomeID) {
return pack.getID()
.toLowerCase() + "/" + biomeID.getNamespace().toLowerCase(Locale.ROOT) + "/" + biomeID.getID().toLowerCase(Locale.ROOT);
}
}
@@ -1,4 +1,4 @@
package com.dfsek.terra.bukkit.nms.v1_21;
package com.dfsek.terra.bukkit.nms.v1_21_3;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.Holder;
@@ -29,7 +29,7 @@ public class NMSBiomeProvider extends BiomeSource {
protected Stream<Holder<Biome>> collectPossibleBiomes() {
return delegate.stream()
.map(biome -> RegistryFetcher.biomeRegistry()
.getHolderOrThrow(((BukkitPlatformBiome) biome.getPlatformBiome()).getContext()
.getOrThrow(((BukkitPlatformBiome) biome.getPlatformBiome()).getContext()
.get(NMSBiomeInfo.class)
.biomeKey()));
}
@@ -45,7 +45,7 @@ public class NMSBiomeProvider extends BiomeSource {
@Override
public @NotNull Holder<Biome> getNoiseBiome(int x, int y, int z, @NotNull Sampler sampler) {
return biomeRegistry.getHolderOrThrow(((BukkitPlatformBiome) delegate.getBiome(x << 2, y << 2, z << 2, seed)
return biomeRegistry.getOrThrow(((BukkitPlatformBiome) delegate.getBiome(x << 2, y << 2, z << 2, seed)
.getPlatformBiome()).getContext()
.get(NMSBiomeInfo.class)
.biomeKey());
@@ -1,4 +1,4 @@
package com.dfsek.terra.bukkit.nms.v1_21;
package com.dfsek.terra.bukkit.nms.v1_21_3;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.BlockPos;
@@ -15,7 +15,6 @@ import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.levelgen.Beardifier;
import net.minecraft.world.level.levelgen.DensityFunction.SinglePointContext;
import net.minecraft.world.level.levelgen.GenerationStep.Carving;
import net.minecraft.world.level.levelgen.Heightmap.Types;
import net.minecraft.world.level.levelgen.RandomState;
import net.minecraft.world.level.levelgen.blending.Blender;
@@ -59,7 +58,7 @@ public class NMSChunkGeneratorDelegate extends ChunkGenerator {
@Override
public void applyCarvers(@NotNull WorldGenRegion chunkRegion, long seed, @NotNull RandomState noiseConfig, @NotNull BiomeManager world,
@NotNull StructureManager structureAccessor, @NotNull ChunkAccess chunk, @NotNull Carving carverStep) {
@NotNull StructureManager structureAccessor, @NotNull ChunkAccess chunk) {
// no-op
}
@@ -156,7 +155,7 @@ public class NMSChunkGeneratorDelegate extends ChunkGenerator {
BlockState[] array = new BlockState[world.getHeight()];
WorldProperties properties = new NMSWorldProperties(seed, world);
BiomeProvider biomeProvider = pack.getBiomeProvider();
for(int y = properties.getMaxHeight() - 1; y >= properties.getMinHeight(); y--) {
for(int y = properties.getMaxHeight(); y >= properties.getMinHeight(); y--) {
array[y - properties.getMinHeight()] = ((CraftBlockData) delegate.getBlock(properties, x, y, z, biomeProvider)
.getHandle()).getState();
}
@@ -1,4 +1,6 @@
package com.dfsek.terra.bukkit.nms.v1_21;
package com.dfsek.terra.bukkit.nms.v1_21_3;
import com.dfsek.terra.bukkit.BukkitAddon;
import org.bukkit.Bukkit;
@@ -12,4 +14,9 @@ public class NMSInitializer implements Initializer {
AwfulBukkitHacks.registerBiomes(platform.getRawConfigRegistry());
Bukkit.getPluginManager().registerEvents(new NMSInjectListener(), platform.getPlugin());
}
@Override
public BukkitAddon getNMSAddon(PlatformImpl plugin) {
return new NMSAddon(plugin);
}
}
@@ -1,4 +1,4 @@
package com.dfsek.terra.bukkit.nms.v1_21;
package com.dfsek.terra.bukkit.nms.v1_21_3;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel;
@@ -17,7 +17,6 @@ import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.util.reflection.ReflectionUtil;
import com.dfsek.terra.bukkit.generator.BukkitChunkGeneratorWrapper;
@@ -41,13 +40,14 @@ public class NMSInjectListener implements Listener {
ChunkGenerator vanilla = serverWorld.getChunkSource().getGenerator();
NMSBiomeProvider provider = new NMSBiomeProvider(pack.getBiomeProvider(), craftWorld.getSeed());
ChunkMap chunkMap = serverWorld.getChunkSource().chunkMap;
WorldGenContext worldGenContext = chunkMap.worldGenContext;
WorldGenContext worldGenContext = Reflection.CHUNKMAP.getWorldGenContext(chunkMap);
Reflection.CHUNKMAP.setWorldGenContext(chunkMap, new WorldGenContext(
worldGenContext.level(),
new NMSChunkGeneratorDelegate(vanilla, pack, provider, craftWorld.getSeed()),
worldGenContext.structureManager(),
worldGenContext.lightEngine(),
worldGenContext.mainThreadMailBox()
worldGenContext.mainThreadExecutor(),
worldGenContext.unsavedListener()
));
LOGGER.info("Successfully injected into world.");
@@ -1,4 +1,4 @@
package com.dfsek.terra.bukkit.nms.v1_21;
package com.dfsek.terra.bukkit.nms.v1_21_3;
import net.minecraft.world.level.LevelHeightAccessor;
@@ -26,11 +26,11 @@ public class NMSWorldProperties implements WorldProperties {
@Override
public int getMaxHeight() {
return height.getMaxBuildHeight();
return height.getMaxY();
}
@Override
public int getMinHeight() {
return height.getMinBuildHeight();
return height.getMinY();
}
}
@@ -0,0 +1,124 @@
package com.dfsek.terra.bukkit.nms.v1_21_3;
import net.minecraft.core.Holder;
import net.minecraft.core.Holder.Reference;
import net.minecraft.core.HolderSet;
import net.minecraft.core.MappedRegistry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.tags.TagKey;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.StructureManager;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.chunk.status.WorldGenContext;
import xyz.jpenilla.reflectionremapper.ReflectionRemapper;
import xyz.jpenilla.reflectionremapper.proxy.ReflectionProxyFactory;
import xyz.jpenilla.reflectionremapper.proxy.annotation.FieldGetter;
import xyz.jpenilla.reflectionremapper.proxy.annotation.FieldSetter;
import xyz.jpenilla.reflectionremapper.proxy.annotation.MethodName;
import xyz.jpenilla.reflectionremapper.proxy.annotation.Proxies;
import xyz.jpenilla.reflectionremapper.proxy.annotation.Static;
import java.util.Collection;
import java.util.List;
import java.util.Map;
public class Reflection {
public static final MappedRegistryProxy MAPPED_REGISTRY;
public static final MappedRegistryTagSetProxy MAPPED_REGISTRY_TAG_SET;
public static final StructureManagerProxy STRUCTURE_MANAGER;
public static final ReferenceProxy REFERENCE;
public static final ChunkMapProxy CHUNKMAP;
public static final HolderReferenceProxy HOLDER_REFERENCE;
public static final HolderSetNamedProxy HOLDER_SET;
public static final BiomeProxy BIOME;
static {
ReflectionRemapper reflectionRemapper = ReflectionRemapper.forReobfMappingsInPaperJar();
ReflectionProxyFactory reflectionProxyFactory = ReflectionProxyFactory.create(reflectionRemapper,
Reflection.class.getClassLoader());
MAPPED_REGISTRY = reflectionProxyFactory.reflectionProxy(MappedRegistryProxy.class);
MAPPED_REGISTRY_TAG_SET = reflectionProxyFactory.reflectionProxy(MappedRegistryTagSetProxy.class);
STRUCTURE_MANAGER = reflectionProxyFactory.reflectionProxy(StructureManagerProxy.class);
REFERENCE = reflectionProxyFactory.reflectionProxy(ReferenceProxy.class);
CHUNKMAP = reflectionProxyFactory.reflectionProxy(ChunkMapProxy.class);
HOLDER_REFERENCE = reflectionProxyFactory.reflectionProxy(HolderReferenceProxy.class);
HOLDER_SET = reflectionProxyFactory.reflectionProxy(HolderSetNamedProxy.class);
BIOME = reflectionProxyFactory.reflectionProxy(BiomeProxy.class);
}
@Proxies(MappedRegistry.class)
public interface MappedRegistryProxy {
@FieldGetter("byKey")
<T> Map<ResourceKey<T>, Reference<T>> getByKey(MappedRegistry<T> instance);
@FieldSetter("allTags")
<T> void setAllTags(MappedRegistry<T> instance, Object obj);
@FieldSetter("frozen")
void setFrozen(MappedRegistry<?> instance, boolean frozen);
@MethodName("createTag")
<T> HolderSet.Named<T> invokeCreateTag(MappedRegistry<T> instance, TagKey<T> tag);
}
@Proxies(className = "net.minecraft.core.MappedRegistry$TagSet")
public interface MappedRegistryTagSetProxy {
@MethodName("fromMap")
@Static
<T> Object invokeFromMap(Map<TagKey<T>, HolderSet.Named<T>> map);
}
@Proxies(StructureManager.class)
public interface StructureManagerProxy {
@FieldGetter("level")
LevelAccessor getLevel(StructureManager instance);
}
@Proxies(Holder.Reference.class)
public interface ReferenceProxy {
@MethodName("bindValue")
<T> void invokeBindValue(Reference<T> instance, T value);
@MethodName("bindTags")
<T> void invokeBindTags(Reference<T> instance, Collection<TagKey<T>> tags);
}
@Proxies(ChunkMap.class)
public interface ChunkMapProxy {
@FieldGetter("worldGenContext")
WorldGenContext getWorldGenContext(ChunkMap instance);
@FieldSetter("worldGenContext")
void setWorldGenContext(ChunkMap instance, WorldGenContext worldGenContext);
}
@Proxies(Holder.Reference.class)
public interface HolderReferenceProxy {
@MethodName("bindTags")
<T> void invokeBindTags(Holder.Reference<T> instance, Collection<TagKey<T>> tags);
}
@Proxies(HolderSet.Named.class)
public interface HolderSetNamedProxy {
@MethodName("bind")
<T> void invokeBind(HolderSet.Named<T> instance, List<Holder<T>> entries);
@MethodName("contents")
<T> List<Holder<T>> invokeContents(HolderSet.Named<T> instance);
}
@Proxies(Biome.class)
public interface BiomeProxy {
@MethodName("getGrassColorFromTexture")
int invokeGrassColorFromTexture(Biome instance);
}
}
@@ -1,24 +1,25 @@
package com.dfsek.terra.bukkit.nms.v1_21;
package com.dfsek.terra.bukkit.nms.v1_21_3;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.MinecraftServer;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.level.biome.Biome;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.CraftServer;
public class RegistryFetcher {
private static <T> Registry<T> getRegistry(ResourceKey<Registry<T>> key) {
CraftServer craftserver = (CraftServer) Bukkit.getServer();
DedicatedServer dedicatedserver = craftserver.getServer();
return dedicatedserver
return MinecraftServer.getServer()
.registryAccess()
.registryOrThrow(key);
.lookupOrThrow(key);
}
public static Registry<Biome> biomeRegistry() {
return getRegistry(Registries.BIOME);
}
public static Registry<SoundEvent> soundEventRegistry() {
return getRegistry(Registries.SOUND_EVENT);
}
}
@@ -0,0 +1,28 @@
package com.dfsek.terra.bukkit.nms.v1_21_3.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.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.level.biome.AmbientAdditionsSettings;
public class BiomeAdditionsSoundTemplate implements ObjectTemplate<AmbientAdditionsSettings> {
@Value("sound")
@Default
private SoundEvent sound = null;
@Value("sound-chance")
@Default
private Double soundChance = null;
@Override
public AmbientAdditionsSettings get() {
if(sound == null || soundChance == null) {
return null;
} else {
return new AmbientAdditionsSettings(BuiltInRegistries.SOUND_EVENT.wrapAsHolder(sound), soundChance);
}
}
}
@@ -0,0 +1,36 @@
package com.dfsek.terra.bukkit.nms.v1_21_3.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.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.level.biome.AmbientMoodSettings;
public class BiomeMoodSoundTemplate implements ObjectTemplate<AmbientMoodSettings> {
@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 AmbientMoodSettings get() {
if(sound == null || soundCultivationTicks == null || soundSpawnRange == null || soundExtraDistance == null) {
return null;
} else {
return new AmbientMoodSettings(BuiltInRegistries.SOUND_EVENT.wrapAsHolder(sound), soundCultivationTicks, soundSpawnRange, soundExtraDistance);
}
}
}
@@ -0,0 +1,36 @@
package com.dfsek.terra.bukkit.nms.v1_21_3.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 com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.commands.arguments.ParticleArgument;
import net.minecraft.core.HolderLookup.Provider;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.world.level.biome.AmbientParticleSettings;
public class BiomeParticleConfigTemplate implements ObjectTemplate<AmbientParticleSettings> {
@Value("particle")
@Default
private String particle = null;
@Value("probability")
@Default
private Integer probability = null;
@Override
public AmbientParticleSettings get() {
if(particle == null || probability == null) {
return null;
}
try {
return new AmbientParticleSettings(ParticleArgument.readParticle(new StringReader(particle),
(Provider) BuiltInRegistries.PARTICLE_TYPE.asHolderIdMap()), probability);
} catch(CommandSyntaxException e) {
throw new RuntimeException(e);
}
}
}
@@ -0,0 +1,20 @@
package com.dfsek.terra.bukkit.nms.v1_21_3.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.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.EntityType;
public class EntityTypeTemplate implements ObjectTemplate<EntityType<?>> {
@Value("id")
@Default
private ResourceLocation id = null;
@Override
public EntityType<?> get() {
return BuiltInRegistries.ENTITY_TYPE.get(id).orElseThrow().value();
}
}
@@ -0,0 +1,36 @@
package com.dfsek.terra.bukkit.nms.v1_21_3.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.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.sounds.Music;
import net.minecraft.sounds.SoundEvent;
public class MusicSoundTemplate implements ObjectTemplate<Music> {
@Value("sound")
@Default
private SoundEvent sound = null;
@Value("min-delay")
@Default
private Integer minDelay = null;
@Value("max-delay")
@Default
private Integer maxDelay = null;
@Value("replace-current-music")
@Default
private Boolean replaceCurrentMusic = null;
@Override
public Music get() {
if(sound == null || minDelay == null || maxDelay == null || replaceCurrentMusic == null) {
return null;
} else {
return new Music(BuiltInRegistries.SOUND_EVENT.wrapAsHolder(sound), minDelay, maxDelay, replaceCurrentMusic);
}
}
}
@@ -0,0 +1,29 @@
package com.dfsek.terra.bukkit.nms.v1_21_3.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.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvent;
public class SoundEventTemplate implements ObjectTemplate<SoundEvent> {
@Value("id")
@Default
private ResourceLocation id = null;
@Value("distance-to-travel")
@Default
private Float distanceToTravel = null;
@Override
public SoundEvent get() {
if(id == null) {
return null;
} else if(distanceToTravel == null) {
return SoundEvent.createVariableRangeEvent(id);
} else {
return SoundEvent.createFixedRangeEvent(id, distanceToTravel);
}
}
}
@@ -0,0 +1,38 @@
package com.dfsek.terra.bukkit.nms.v1_21_3.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.minecraft.world.entity.EntityType;
public class SpawnCostConfig implements ObjectTemplate<SpawnCostConfig> {
@Value("type")
@Default
private EntityType<?> type = null;
@Value("mass")
@Default
private Double mass = null;
@Value("gravity")
@Default
private Double gravity = null;
public EntityType<?> getType() {
return type;
}
public Double getMass() {
return mass;
}
public Double getGravity() {
return gravity;
}
@Override
public SpawnCostConfig get() {
return this;
}
}
@@ -0,0 +1,31 @@
package com.dfsek.terra.bukkit.nms.v1_21_3.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.minecraft.world.entity.EntityType;
import net.minecraft.world.level.biome.MobSpawnSettings.SpawnerData;
public class SpawnEntryTemplate implements ObjectTemplate<SpawnerData> {
@Value("type")
@Default
private EntityType<?> type = null;
@Value("weight")
@Default
private Integer weight = null;
@Value("min-group-size")
@Default
private Integer minGroupSize = null;
@Value("max-group-size")
@Default
private Integer maxGroupSize = null;
@Override
public SpawnerData get() {
return new SpawnerData(type, weight, minGroupSize, maxGroupSize);
}
}
@@ -0,0 +1,49 @@
package com.dfsek.terra.bukkit.nms.v1_21_3.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 java.util.List;
import net.minecraft.world.entity.MobCategory;
import net.minecraft.world.level.biome.MobSpawnSettings;
import net.minecraft.world.level.biome.MobSpawnSettings.SpawnerData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SpawnSettingsTemplate implements ObjectTemplate<MobSpawnSettings> {
private static final Logger logger = LoggerFactory.getLogger(SpawnTypeConfig.class);
private static boolean used = false;
@Value("spawns")
@Default
private List<SpawnTypeConfig> spawns = null;
@Value("costs")
@Default
private List<SpawnCostConfig> costs = null;
@Value("probability")
@Default
private Float probability = null;
@Override
public MobSpawnSettings get() {
MobSpawnSettings.Builder builder = new MobSpawnSettings.Builder();
for(SpawnTypeConfig spawn : spawns) {
MobCategory group = spawn.getGroup();
for(SpawnerData entry : spawn.getEntries()) {
builder.addSpawn(group, entry);
}
}
for(SpawnCostConfig cost : costs) {
builder.addMobCharge(cost.getType(), cost.getMass(), cost.getGravity());
}
if(probability != null) {
builder.creatureGenerationProbability(probability);
}
return builder.build();
}
}
@@ -0,0 +1,32 @@
package com.dfsek.terra.bukkit.nms.v1_21_3.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 java.util.List;
import net.minecraft.world.entity.MobCategory;
import net.minecraft.world.level.biome.MobSpawnSettings.SpawnerData;
public class SpawnTypeConfig implements ObjectTemplate<SpawnTypeConfig> {
@Value("group")
@Default
private MobCategory group = null;
@Value("entries")
@Default
private List<SpawnerData> entries = null;
public MobCategory getGroup() {
return group;
}
public List<SpawnerData> getEntries() {
return entries;
}
@Override
public SpawnTypeConfig get() {
return this;
}
}
@@ -0,0 +1,164 @@
package com.dfsek.terra.bukkit.nms.v1_21_3.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.minecraft.sounds.Music;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.entity.npc.VillagerType;
import net.minecraft.world.level.biome.AmbientAdditionsSettings;
import net.minecraft.world.level.biome.AmbientMoodSettings;
import net.minecraft.world.level.biome.AmbientParticleSettings;
import net.minecraft.world.level.biome.Biome.TemperatureModifier;
import net.minecraft.world.level.biome.BiomeSpecialEffects.GrassColorModifier;
import net.minecraft.world.level.biome.MobSpawnSettings;
public class VanillaBiomeProperties implements ConfigTemplate, Properties {
@Value("colors.grass")
@Default
private Integer grassColor = null;
@Value("colors.fog")
@Default
private Integer fogColor = null;
@Value("colors.water")
@Default
private Integer waterColor = null;
@Value("colors.water-fog")
@Default
private Integer waterFogColor = null;
@Value("colors.foliage")
@Default
private Integer foliageColor = null;
@Value("colors.sky")
@Default
private Integer skyColor = null;
@Value("colors.modifier")
@Default
private GrassColorModifier grassColorModifier = null;
@Value("particles")
@Default
private AmbientParticleSettings particleConfig = null;
@Value("climate.precipitation")
@Default
private Boolean precipitation = true;
@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 AmbientMoodSettings moodSound = null;
@Value("sound.additions-sound")
@Default
private AmbientAdditionsSettings additionsSound = null;
@Value("sound.music")
@Default
private Music music = null;
@Value("spawning")
@Default
private MobSpawnSettings spawnSettings = null;
@Value("villager-type")
@Default
private VillagerType villagerType = null;
public Integer getFogColor() {
return fogColor;
}
public Integer getFoliageColor() {
return foliageColor;
}
public Integer getGrassColor() {
return grassColor;
}
public Integer getWaterColor() {
return waterColor;
}
public Integer getWaterFogColor() {
return waterFogColor;
}
public Integer getSkyColor() {
return skyColor;
}
public GrassColorModifier getGrassColorModifier() {
return grassColorModifier;
}
public AmbientParticleSettings 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 AmbientMoodSettings getMoodSound() {
return moodSound;
}
public AmbientAdditionsSettings getAdditionsSound() {
return additionsSound;
}
public Music getMusic() {
return music;
}
public MobSpawnSettings getSpawnSettings() {
return spawnSettings;
}
public VillagerType getVillagerType() {
return villagerType;
}
}
@@ -0,0 +1,20 @@
package com.dfsek.terra.bukkit.nms.v1_21_3.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.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.npc.VillagerType;
public class VillagerTypeTemplate implements ObjectTemplate<VillagerType> {
@Value("id")
@Default
private ResourceLocation id = null;
@Override
public VillagerType get() {
return BuiltInRegistries.VILLAGER_TYPE.get(id).orElseThrow().value();
}
}
+10 -1
View File
@@ -8,7 +8,10 @@ dependencies {
shadedApi(project(":common:implementation:base"))
shadedApi("commons-io", "commons-io", Versions.Libraries.Internal.apacheIO)
shadedApi("com.github.Querz", "NBT", Versions.CLI.nbt)
shadedApi("com.github.Querz", "NBT", Versions.Libraries.Internal.nbt)
shadedImplementation("info.picocli", "picocli", Versions.CLI.picocli)
annotationProcessor("info.picocli", "picocli-codegen", Versions.CLI.picocli)
shadedImplementation("com.google.guava", "guava", Versions.Libraries.Internal.guava)
@@ -26,6 +29,12 @@ tasks.withType<Jar> {
}
}
tasks.withType<JavaCompile> {
doFirst {
options.compilerArgs.add("-Aproject=${project.group}/${project.name}")
}
}
application {
mainClass.set(javaMainClass)
}
@@ -22,6 +22,8 @@ public class CLIPlatform extends AbstractPlatform {
private final CLIWorldHandle worldHandle = new CLIWorldHandle();
private final CLIItemHandle itemHandle = new CLIItemHandle();
private final int generationThreads = Runtime.getRuntime().availableProcessors() - 1;
public CLIPlatform() {
LOGGER.info("Root directory: {}", getDataFolder().getAbsoluteFile());
load();
@@ -58,4 +60,9 @@ public class CLIPlatform extends AbstractPlatform {
super.register(registry);
registry.registerLoader(PlatformBiome.class, (TypeLoader<PlatformBiome>) (annotatedType, o, configLoader, depthTracker) -> () -> o);
}
@Override
public int getGenerationThreads() {
return generationThreads;
}
}
@@ -5,41 +5,74 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.concurrent.Callable;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.event.events.platform.PlatformInitializationEvent;
import com.dfsek.terra.api.util.vector.Vector2Int;
import com.dfsek.seismic.type.vector.Vector2Int;
import com.dfsek.terra.cli.world.CLIWorld;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
public final class TerraCLI {
private static final Logger LOGGER = LoggerFactory.getLogger(TerraCLI.class);
public static void main(String... args) {
//TODO auto pull in version
@Command(name = "TerraCLI", mixinStandardHelpOptions = true, version = "6.6.0",
description = "Generates a Terra World and saves it in minecraft region format.")
public final class TerraCLI implements Callable<Integer> {
@Option(names = { "-s", "--size"}, description = "Number of regions to generate.")
private int size = 2;
@Option(names = { "-p", "--pack"}, description = "Config pack to use.")
private String pack = "OVERWORLD";
@Option(names = { "--seed"}, description = "Seed for world generation.")
private long seed = 0;
@Option(names = { "--max-height"}, description = "Maximum height of the world.")
private int maxHeight = 320;
@Option(names = { "--min-height"}, description = "Minimum height of the world.")
private int minHeight = -64;
@Option(names = { "--no-save"}, description = "Don't save the world to disk.")
private boolean noSave = false;
@Override
public Integer call() {
Logger LOGGER = LoggerFactory.getLogger(TerraCLI.class);
LOGGER.info("Starting Terra CLI...");
CLIPlatform platform = new CLIPlatform();
platform.getEventManager().callEvent(new PlatformInitializationEvent());
ConfigPack generate = platform.getConfigRegistry().getByID("OVERWORLD").orElseThrow(); // TODO: make this a cli argument
ConfigPack generate = platform.getConfigRegistry().getByID(pack).orElseThrow();
CLIWorld world = new CLIWorld(2, 2, 384, -64, generate);
CLIWorld world = new CLIWorld(size, seed, maxHeight, minHeight, generate, noSave);
world.generate();
world.serialize().parallel().forEach(mcaFile -> {
Vector2Int pos = mcaFile.getLeft();
String name = MCAUtil.createNameFromRegionLocation(pos.getX(), pos.getZ());
LOGGER.info("Writing region ({}, {}) to {}", pos.getX(), pos.getZ(), name);
if(!noSave) {
world.serialize().parallel().forEach(mcaFile -> {
Vector2Int pos = mcaFile.getLeft();
String name = MCAUtil.createNameFromRegionLocation(pos.getX(), pos.getZ());
LOGGER.info("Writing region ({}, {}) to {}", pos.getX(), pos.getZ(), name);
try {
MCAUtil.write(mcaFile.getRight(), name);
} catch(IOException e) {
e.printStackTrace();
}
LOGGER.info("Wrote region to file.");
});
try {
MCAUtil.write(mcaFile.getRight(), name);
} catch(IOException e) {
e.printStackTrace();
}
LOGGER.info("Wrote region to file.");
});
}
LOGGER.info("Done.");
System.exit(0);
return 0;
}
public static void main(String... args) {
int exitCode = new CommandLine(new TerraCLI()).execute(args);
System.exit(exitCode);
}
}
@@ -22,8 +22,8 @@ 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.util.vector.Vector2Int;
import com.dfsek.terra.api.util.vector.Vector3;
import com.dfsek.seismic.type.vector.Vector2Int;
import com.dfsek.seismic.type.vector.Vector3;
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;
@@ -43,6 +43,7 @@ public class CLIWorld implements ServerWorld, NBTSerializable<Stream<Pair<Vector
private final ChunkGenerator chunkGenerator;
private final BiomeProvider biomeProvider;
private final ConfigPack pack;
private final boolean noSave;
private final AtomicInteger amount = new AtomicInteger(0);
private final ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() - 1);
@@ -51,7 +52,7 @@ public class CLIWorld implements ServerWorld, NBTSerializable<Stream<Pair<Vector
long seed,
int maxHeight,
int minHeight,
ConfigPack pack) {
ConfigPack pack, boolean noSave) {
this.size = size;
this.maxHeight = maxHeight;
this.minHeight = minHeight;
@@ -59,6 +60,7 @@ public class CLIWorld implements ServerWorld, NBTSerializable<Stream<Pair<Vector
this.chunkGenerator = pack.getGeneratorProvider().newInstance(pack);
this.biomeProvider = pack.getBiomeProvider();
this.pack = pack;
this.noSave = noSave;
size += 1;
@@ -73,6 +75,7 @@ public class CLIWorld implements ServerWorld, NBTSerializable<Stream<Pair<Vector
}
public void generate() {
ArrayList<Double> CPSHistory = new ArrayList<>();
int sizeChunks = size * 32;
List<Future<?>> futures = new ArrayList<>();
final AtomicLong start = new AtomicLong(System.nanoTime());
@@ -83,7 +86,13 @@ public class CLIWorld implements ServerWorld, NBTSerializable<Stream<Pair<Vector
futures.add(executor.submit(() -> {
try {
int num = amount.getAndIncrement();
CLIChunk chunk = getChunkAt(finalX, finalZ);
CLIChunk chunk;
if (!noSave) {
chunk = getChunkAt(finalX, finalZ);
} else {
chunk = new CLIChunk(Math.floorMod(finalX, 32), Math.floorMod(finalZ, 32), this);
}
BiomeProvider cachingBiomeProvider = pack.getBiomeProvider();
chunkGenerator.generateChunkData(chunk, this, cachingBiomeProvider, finalX, finalZ);
CLIProtoWorld protoWorld = new CLIProtoWorld(this, cachingBiomeProvider, finalX, finalZ);
@@ -91,6 +100,7 @@ public class CLIWorld implements ServerWorld, NBTSerializable<Stream<Pair<Vector
if(num % 240 == 239) {
long time = System.nanoTime();
double cps = num / ((double) (time - start.get()) / 1000000000);
CPSHistory.add(cps);
LOGGER.info("Generating chunk at ({}, {}), generated {} chunks at {}cps", finalX, finalZ, num, cps);
amount.set(0);
start.set(System.nanoTime());
@@ -109,6 +119,8 @@ public class CLIWorld implements ServerWorld, NBTSerializable<Stream<Pair<Vector
e.printStackTrace();
}
}
LOGGER.info("Average CPS: {}", CPSHistory.stream().mapToDouble(d -> d).average().orElse(0));
}
@Override
+2 -2
View File
@@ -26,8 +26,8 @@ dependencies {
modImplementation("net.fabricmc:fabric-loader:${Versions.Mod.fabricLoader}")
modImplementation("org.incendo", "cloud-fabric", Versions.Libraries.cloudFabric)
include("org.incendo", "cloud-fabric", Versions.Libraries.cloudFabric)
modImplementation("org.incendo", "cloud-fabric", Versions.Fabric.cloud)
include("org.incendo", "cloud-fabric", Versions.Fabric.cloud)
modRuntimeOnly("net.fabricmc.fabric-api", "fabric-api", Versions.Fabric.fabricAPI)
}
@@ -32,9 +32,9 @@
"terra.common.mixins.json"
],
"depends": {
"fabricloader": ">=0.16.5",
"fabricloader": ">=0.16.10",
"java": ">=21",
"minecraft": ">=1.21.1",
"minecraft": ">=1.21.4",
"fabric": "*"
}
}
@@ -20,6 +20,9 @@ package com.dfsek.terra.forge;
import ca.solostudios.strata.Versions;
import ca.solostudios.strata.parser.tokenizer.ParseException;
import ca.solostudios.strata.version.Version;
import com.dfsek.terra.registry.master.ConfigRegistry.PackLoadFailuresException;
import net.minecraft.MinecraftVersion;
import net.minecraft.registry.Registry;
import net.minecraft.server.MinecraftServer;
@@ -34,6 +37,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@@ -62,8 +66,7 @@ public class ForgePlatform extends ModPlatform {
@Override
public boolean reload() {
getTerraConfig().load(this);
getRawConfigRegistry().clear();
boolean succeed = getRawConfigRegistry().loadAll(this);
boolean succeed = loadConfigPacks();
MinecraftServer server = getServer();
@@ -1,8 +1,8 @@
package com.dfsek.terra.forge.mixin.lifecycle;
import net.minecraft.registry.RegistryEntryLookup;
import net.minecraft.util.math.noise.DoublePerlinNoiseSampler.NoiseParameters;
import net.minecraft.world.biome.source.util.MultiNoiseUtil.MultiNoiseSampler;
import net.minecraft.util.math.noise.DoublePerlinSampler.NoiseParameters;
import net.minecraft.world.biome.source.util.MultiNoiseUtil.MultiSampler;
import net.minecraft.world.gen.chunk.ChunkGeneratorSettings;
import net.minecraft.world.gen.noise.NoiseConfig;
import org.spongepowered.asm.mixin.Final;
@@ -22,13 +22,13 @@ import com.dfsek.terra.mod.util.SeedHack;
public class NoiseConfigMixin {
@Shadow
@Final
private MultiNoiseSampler multiNoiseSampler;
private MultiSampler multiSampler;
@Inject(method = "<init>(Lnet/minecraft/world/gen/chunk/ChunkGeneratorSettings;Lnet/minecraft/registry/RegistryEntryLookup;J)V",
at = @At("TAIL"))
private void mapMultiNoise(ChunkGeneratorSettings chunkGeneratorSettings, RegistryEntryLookup<NoiseParameters> noiseParametersLookup,
long seed,
CallbackInfo ci) {
SeedHack.register(multiNoiseSampler, seed);
SeedHack.register(multiSampler, seed);
}
}
@@ -60,13 +60,13 @@ public final class BiomeUtil {
} else {
VanillaBiomeProperties vanillaBiomeProperties = biome.getContext().get(VanillaBiomeProperties.class);
net.minecraft.world.biome.Biome minecraftBiome = MinecraftUtil.createBiome(biome,
net.minecraft.world.biome.Biome minecraftBiome = com.dfsek.terra.mod.util.BiomeUtil.createBiome(biome,
ForgeRegistries.BIOMES.getDelegateOrThrow(
vanilla.getKey().orElseThrow())
.value(),
vanillaBiomeProperties);
Identifier identifier = new Identifier("terra", MinecraftUtil.createBiomeID(pack, id));
Identifier identifier = new Identifier("terra", com.dfsek.terra.mod.util.BiomeUtil.createBiomeID(pack, id));
if(ForgeRegistries.BIOMES.containsKey(identifier)) {
((ProtoPlatformBiome) biome.getPlatformBiome()).setDelegate(ForgeRegistries.BIOMES.getHolder(identifier)
@@ -83,7 +83,7 @@ public final class BiomeUtil {
Objects.requireNonNullElse(vanillaBiomeProperties.getVillagerType(),
villagerMap.getOrDefault(vanilla.getKey().orElseThrow(), VillagerType.PLAINS)));
MinecraftUtil.TERRA_BIOME_MAP.computeIfAbsent(vanilla.getKey().orElseThrow().getValue(), i -> new ArrayList<>()).add(
com.dfsek.terra.mod.util.BiomeUtil.TERRA_BIOME_MAP.computeIfAbsent(vanilla.getKey().orElseThrow().getValue(), i -> new ArrayList<>()).add(
identifier);
}
}
+11
View File
@@ -0,0 +1,11 @@
dependencies {
shadedApi(project(":common:implementation:base"))
shadedApi("com.github.ben-manes.caffeine", "caffeine", Versions.Libraries.caffeine)
shadedImplementation("com.google.guava", "guava", Versions.Libraries.Internal.guava)
compileOnly("net.minestom", "minestom-snapshots", Versions.Minestom.minestom)
}
tasks.named("jar") {
finalizedBy("installAddonsIntoDefaultJar")
}
@@ -0,0 +1,28 @@
plugins {
application
}
val javaMainClass = "com.dfsek.terra.minestom.TerraMinestomExample"
dependencies {
shadedApi(project(":platforms:minestom"))
implementation("net.minestom", "minestom-snapshots", Versions.Minestom.minestom)
implementation("org.slf4j", "slf4j-simple", Versions.Libraries.slf4j)
}
tasks.withType<Jar> {
entryCompression = ZipEntryCompression.STORED
manifest {
attributes(
"Main-Class" to javaMainClass,
)
}
}
application {
mainClass.set(javaMainClass)
}
tasks.getByName("run").setProperty("workingDir", file("./run"))
addonDir(project.file("./run/terra/addons"), tasks.named("run").get())
@@ -0,0 +1,132 @@
package com.dfsek.terra.minestom;
import net.kyori.adventure.text.Component;
import net.minestom.server.MinecraftServer;
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.PlayerSpawnEvent;
import net.minestom.server.instance.Instance;
import net.minestom.server.instance.LightingChunk;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 {
private static final Logger logger = LoggerFactory.getLogger(TerraMinestomExample.class);
private final MinecraftServer server = MinecraftServer.init();
private Instance instance;
private TerraMinestomWorld world;
public void createNewInstance() {
instance = MinecraftServer.getInstanceManager().createInstanceContainer();
instance.setChunkSupplier(LightingChunk::new);
}
public void attachTerra() {
world = TerraMinestomWorldBuilder.from(instance)
.defaultPack()
.attach();
}
private void sendProgressBar(int current, int max) {
String left = "#".repeat((int) ((((float) current) / max) * 20));
String right = ".".repeat(20 - left.length());
int percent = (int) (((float) current) / max * 100);
String percentString = percent + "%";
percentString = " ".repeat(4 - percentString.length()) + percentString;
String message = percentString + " |" + left + right + "| " + current + "/" + max;
logger.info(message);
}
public void preloadWorldAndMeasure() {
int radius = 12;
int chunksLoading = (radius * 2 + 1) * (radius * 2 + 1);
AtomicInteger chunksLeft = new AtomicInteger(chunksLoading);
long start = System.nanoTime();
for(int x = -radius; x <= radius; x++) {
for(int z = -radius; z <= radius; z++) {
instance.loadChunk(x, z).thenAccept(chunk -> {
int left = chunksLeft.decrementAndGet();
if(left == 0) {
long end = System.nanoTime();
sendProgressBar(chunksLoading - left, chunksLoading);
double chunksPerSecond = chunksLoading / ((end - start) / 1000000000.0);
logger.info(
"Preloaded {} chunks in world in {}ms. That's {} Chunks/s",
chunksLoading,
(end - start) / 1000000.0,
chunksPerSecond
);
world.displayStats();
} else if(left % 60 == 0) {
sendProgressBar(chunksLoading - left, chunksLoading);
}
});
}
}
}
public void addListeners() {
MinecraftServer.getGlobalEventHandler().addListener(AsyncPlayerConfigurationEvent.class, event -> {
event.setSpawningInstance(instance);
event.getPlayer().setRespawnPoint(new Pos(0.0, 100.0, 0.0));
});
MinecraftServer.getGlobalEventHandler().addListener(PlayerSpawnEvent.class, event -> {
event.getPlayer().setGameMode(GameMode.SPECTATOR);
});
}
public void addScheduler() {
MinecraftServer.getSchedulerManager().buildTask(() -> world.displayStats())
.repeat(Duration.ofSeconds(10))
.schedule();
}
public void addCommands() {
MinecraftServer.getCommandManager().register(new RegenerateCommand());
}
public void bind() {
logger.info("Starting server on port 25565");
server.start("localhost", 25565);
}
public static void main(String[] args) {
TerraMinestomExample example = new TerraMinestomExample();
example.createNewInstance();
example.attachTerra();
example.preloadWorldAndMeasure();
example.addScheduler();
example.addListeners();
example.addCommands();
example.bind();
}
public class RegenerateCommand extends Command {
public RegenerateCommand() {
super("regenerate");
setDefaultExecutor((sender, context) -> regenerate());
}
private void regenerate() {
instance.sendMessage(Component.text("Regenerating world"));
createNewInstance();
attachTerra();
preloadWorldAndMeasure();
MinecraftServer.getConnectionManager().getOnlinePlayers().forEach(player ->
player.setInstance(instance, new Pos(0, 100, 0))
);
}
}
}
@@ -0,0 +1,17 @@
package com.dfsek.terra.minestom;
import com.dfsek.seismic.type.vector.Vector3;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Pos;
public class MinestomAdapter {
public static Vector3 adapt(Point point) {
return Vector3.of(point.x(), point.y(), point.z());
}
public static Pos adapt(Vector3 vector) {
return new Pos(vector.getX(), vector.getY(), vector.getZ());
}
}
@@ -0,0 +1,93 @@
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.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.biome.MinestomBiomeLoader;
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 net.minestom.server.MinecraftServer;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
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() {
load();
getEventManager().callEvent(new PlatformInitializationEvent());
}
@Override
public void register(TypeRegistry registry) {
super.register(registry);
registry
.registerLoader(PlatformBiome.class, new MinestomBiomeLoader())
.registerLoader(EntityType.class, (TypeLoader<EntityType>) (annotatedType, o, configLoader, depthTracker) -> new MinestomEntityType((String) o))
.registerLoader(BlockState.class, (TypeLoader<BlockState>) (annotatedType, o, configLoader, depthTracker) -> worldHandle.createBlockState((String) o));
}
@Override
public boolean reload() {
getTerraConfig().load(this);
boolean succeed = loadConfigPacks();
MinecraftServer.getInstanceManager().getInstances().forEach(world -> {
if(world.generator() instanceof MinestomChunkGeneratorWrapper wrapper) {
getConfigRegistry().get(wrapper.getPack().getRegistryKey()).ifPresent(pack -> {
wrapper.setPack(pack);
LOGGER.info("Replaced pack in chunk generator for instance {}", world.getUniqueId());
});
}
});
return succeed;
}
@Override
public @NotNull WorldHandle getWorldHandle() {
return worldHandle;
}
@Override
public @NotNull ItemHandle getItemHandle() {
return itemHandle;
}
@Override
public @NotNull String platformName() {
return "Minestom";
}
@Override
public @NotNull File getDataFolder() {
String pathName = System.getProperty("terra.datafolder");
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;
}
}
@@ -0,0 +1,16 @@
package com.dfsek.terra.minestom.api;
import com.dfsek.terra.api.block.entity.BlockEntity;
import net.minestom.server.coordinate.BlockVec;
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.
*/
public interface BlockEntityFactory {
@Nullable BlockEntity createBlockEntity(BlockVec position);
}
@@ -0,0 +1,13 @@
package com.dfsek.terra.minestom.api;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.EntityType;
/**
* Allows adding AI to generated entities using custom entity types
*/
public interface EntityFactory {
Entity createEntity(EntityType type);
}
@@ -0,0 +1,17 @@
package com.dfsek.terra.minestom.biome;
import com.dfsek.terra.api.world.biome.PlatformBiome;
import net.minestom.server.world.biome.Biome;
public class MinestomBiome implements PlatformBiome {
private final Biome biome;
public MinestomBiome(Biome biome) { this.biome = biome; }
@Override
public Biome getHandle() {
return biome;
}
}
@@ -0,0 +1,31 @@
package com.dfsek.terra.minestom.biome;
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 com.dfsek.terra.api.world.biome.PlatformBiome;
import net.minestom.server.MinecraftServer;
import net.minestom.server.registry.DynamicRegistry;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.world.biome.Biome;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.AnnotatedType;
public class MinestomBiomeLoader implements TypeLoader<PlatformBiome> {
private final DynamicRegistry<Biome> biomeRegistry = MinecraftServer.getBiomeRegistry();
@Override
public PlatformBiome load(@NotNull AnnotatedType annotatedType, @NotNull Object o, @NotNull ConfigLoader configLoader,
DepthTracker depthTracker) throws LoadException {
String id = (String) o;
NamespaceID biomeID = NamespaceID.from(id);
Biome biome = biomeRegistry.get(biomeID);
if(biome == null) throw new LoadException("Biome %s does not exist in registry".formatted(id), depthTracker);
return new MinestomBiome(biome);
}
}
@@ -0,0 +1,14 @@
package com.dfsek.terra.minestom.block;
import com.dfsek.terra.api.block.entity.BlockEntity;
import com.dfsek.terra.minestom.api.BlockEntityFactory;
import net.minestom.server.coordinate.BlockVec;
public class DefaultBlockEntityFactory implements BlockEntityFactory {
@Override
public BlockEntity createBlockEntity(BlockVec position) {
return null;
}
}
@@ -0,0 +1,98 @@
package com.dfsek.terra.minestom.block;
import com.dfsek.terra.api.block.BlockType;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.block.state.properties.Property;
import net.minestom.server.instance.block.Block;
import java.util.HashMap;
import java.util.Objects;
import java.util.stream.Collectors;
public class MinestomBlockState implements BlockState {
private final Block block;
public MinestomBlockState(Block block) {
if(block == null) {
this.block = Block.AIR;
} else {
this.block = block;
}
}
public MinestomBlockState(String data) {
if(!data.contains("[")) {
block = Block.fromNamespaceId(data);
return;
}
String[] split = data.split("\\[");
String namespaceId = split[0];
String properties = split[1].substring(0, split[1].length() - 1);
Block block = Block.fromNamespaceId(namespaceId);
HashMap<String, String> propertiesMap = new HashMap<>();
for(String property : properties.split(",")) {
String[] kv = property.split("=");
propertiesMap.put(kv[0].strip(), kv[1].strip());
}
assert block != null;
this.block = block.withProperties(propertiesMap);
}
@Override
public boolean matches(BlockState other) {
return ((MinestomBlockState) other).block.compare(block);
}
@Override
public <T extends Comparable<T>> boolean has(Property<T> property) {
return false;
}
@Override
public <T extends Comparable<T>> T get(Property<T> property) {
return null;
}
@Override
public <T extends Comparable<T>> BlockState set(Property<T> property, T value) {
return null;
}
@Override
public BlockType getBlockType() {
return new MinestomBlockType(block);
}
@Override
public String getAsString(boolean properties) {
String name = block.namespace().asString();
if(!properties || block.properties().isEmpty()) {
return name;
}
name += "[" + block.properties().entrySet().stream().map(entry -> entry.getKey() + "=" + entry.getValue()).collect(
Collectors.joining(",")) + "]";
return name;
}
@Override
public boolean isAir() {
return block.isAir();
}
@Override
public Object getHandle() {
return block;
}
@Override
public int hashCode() {
return Objects.hashCode(block.id());
}
}
@@ -0,0 +1,48 @@
package com.dfsek.terra.minestom.block;
import com.dfsek.terra.api.block.BlockType;
import com.dfsek.terra.api.block.state.BlockState;
import net.minestom.server.instance.block.Block;
public class MinestomBlockType implements BlockType {
private final Block block;
public MinestomBlockType(Block block) {
this.block = block;
}
@Override
public BlockState getDefaultState() {
return new MinestomBlockState(block);
}
@Override
public boolean isSolid() {
return block.isSolid();
}
@Override
public boolean isWater() {
return block.isLiquid();
}
@Override
public Object getHandle() {
return block;
}
@Override
public int hashCode() {
return block.id();
}
@Override
public boolean equals(Object obj) {
if(obj instanceof MinestomBlockType other) {
return block.id() == other.block.id();
}
return false;
}
}
@@ -0,0 +1,58 @@
package com.dfsek.terra.minestom.chunk;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.world.chunk.generation.ProtoChunk;
import com.dfsek.terra.minestom.block.MinestomBlockState;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.UnitModifier;
import org.jetbrains.annotations.NotNull;
public class CachedChunk implements ProtoChunk {
private final int minHeight;
private final int maxHeight;
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;
}
}
}
}
public void writeRelative(UnitModifier modifier) {
modifier.setAllRelative((x, y, z) -> blocks[x][y][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;
}
@Override
public @NotNull BlockState getBlock(int x, int y, int z) {
return new MinestomBlockState(blocks[x][y - minHeight][z]);
}
@Override
public Object getHandle() {
return blocks;
}
@Override
public int getMaxHeight() {
return maxHeight;
}
}
@@ -0,0 +1,49 @@
package com.dfsek.terra.minestom.chunk;
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;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.github.benmanes.caffeine.cache.stats.CacheStats;
import net.minestom.server.world.DimensionType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GeneratedChunkCache {
private static final Logger log = LoggerFactory.getLogger(GeneratedChunkCache.class);
private final LoadingCache<Pair<Integer, Integer>, CachedChunk> cache;
private final DimensionType dimensionType;
private final ChunkGenerator generator;
private final ServerWorld world;
private final BiomeProvider biomeProvider;
public GeneratedChunkCache(DimensionType dimensionType, ChunkGenerator generator, ServerWorld world) {
this.dimensionType = dimensionType;
this.generator = generator;
this.world = world;
this.biomeProvider = world.getBiomeProvider();
this.cache = Caffeine.newBuilder().maximumSize(128).recordStats().build(
(Pair<Integer, Integer> key) -> generateChunk(key.getLeft(), key.getRight()));
}
private CachedChunk generateChunk(int x, int z) {
CachedChunk chunk = new CachedChunk(dimensionType.minY(), dimensionType.maxY());
generator.generateChunkData(chunk, world, biomeProvider, x, z);
return chunk;
}
public void displayStats() {
CacheStats stats = cache.stats();
log.info("Avg load time: {}ms | Hit rate: {}% | Load Count: {}", stats.averageLoadPenalty(), stats.hitRate() * 100,
stats.loadCount());
}
public CachedChunk at(int x, int z) {
return cache.get(Pair.of(x, z));
}
}
@@ -0,0 +1,51 @@
package com.dfsek.terra.minestom.chunk;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.world.ServerWorld;
import com.dfsek.terra.api.world.chunk.Chunk;
import com.dfsek.terra.minestom.block.MinestomBlockState;
import net.minestom.server.instance.block.Block;
import org.jetbrains.annotations.NotNull;
public class TerraMinestomChunk implements Chunk {
private net.minestom.server.instance.Chunk delegate;
private final ServerWorld world;
public TerraMinestomChunk(net.minestom.server.instance.Chunk delegate, ServerWorld world) {
this.delegate = delegate;
this.world = world;
}
@Override
public void setBlock(int x, int y, int z, BlockState data, boolean physics) {
delegate.setBlock(x, y, z, (Block) data.getHandle());
}
@Override
public @NotNull BlockState getBlock(int x, int y, int z) {
return new MinestomBlockState(delegate.getBlock(x, y, z));
}
@Override
public int getX() {
return delegate.getChunkX();
}
@Override
public int getZ() {
return delegate.getChunkZ();
}
@Override
public ServerWorld getWorld() {
return world;
}
@Override
public Object getHandle() {
return delegate;
}
}
@@ -0,0 +1,14 @@
package com.dfsek.terra.minestom.entity;
import com.dfsek.terra.minestom.api.EntityFactory;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.EntityType;
public class DefaultEntityFactory implements EntityFactory {
@Override
public Entity createEntity(EntityType type) {
return new Entity(type);
}
}
@@ -0,0 +1,68 @@
package com.dfsek.terra.minestom.entity;
import com.dfsek.terra.api.entity.Entity;
import com.dfsek.terra.api.entity.EntityType;
import com.dfsek.seismic.type.vector.Vector3;
import com.dfsek.terra.api.world.ServerWorld;
import com.dfsek.terra.minestom.world.TerraMinestomWorld;
import net.minestom.server.coordinate.Pos;
public class DeferredMinestomEntity implements Entity {
private final EntityType type;
private double x;
private double y;
private double z;
private TerraMinestomWorld world;
public DeferredMinestomEntity(double x, double y, double z, EntityType type, TerraMinestomWorld world) {
this.x = x;
this.y = y;
this.z = z;
this.type = type;
this.world = world;
}
@Override
public Vector3 position() {
return Vector3.of(x, y, z);
}
public Pos pos() {
return new Pos(x, y, z);
}
@Override
public void position(Vector3 position) {
x = position.getX();
y = position.getY();
z = position.getZ();
}
@Override
public void world(ServerWorld world) {
this.world = (TerraMinestomWorld) world;
}
@Override
public ServerWorld world() {
return world;
}
@Override
public Object getHandle() {
return this;
}
public void spawn() {
int chunkX = (int) x >> 4;
int chunkZ = (int) z >> 4;
if(!world.getHandle().isChunkLoaded(chunkX, chunkZ)) {
return;
}
MinestomEntity.spawn(x, y, z, type, world);
}
}
@@ -0,0 +1,55 @@
package com.dfsek.terra.minestom.entity;
import com.dfsek.terra.api.entity.EntityType;
import com.dfsek.seismic.type.vector.Vector3;
import com.dfsek.terra.api.world.ServerWorld;
import com.dfsek.terra.minestom.MinestomAdapter;
import com.dfsek.terra.minestom.world.TerraMinestomWorld;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.Entity;
import net.minestom.server.instance.Instance;
public class MinestomEntity implements com.dfsek.terra.api.entity.Entity {
private final Entity delegate;
private final TerraMinestomWorld world;
public MinestomEntity(Entity delegate, TerraMinestomWorld world) {
this.delegate = delegate;
this.world = world;
}
@Override
public Vector3 position() {
return MinestomAdapter.adapt(delegate.getPosition());
}
@Override
public void position(Vector3 position) {
delegate.teleport(MinestomAdapter.adapt(position));
}
@Override
public void world(ServerWorld world) {
delegate.setInstance(((TerraMinestomWorld) world).getHandle());
}
@Override
public ServerWorld world() {
return world;
}
@Override
public Object getHandle() {
return delegate;
}
public static MinestomEntity spawn(double x, double y, double z, EntityType type, TerraMinestomWorld world) {
Instance instance = world.getHandle();
Entity entity = world.getEntityFactory().createEntity(((MinestomEntityType) type).getHandle());
entity.setInstance(instance, new Pos(x, y, z));
return new MinestomEntity(entity, world);
}
}
@@ -0,0 +1,18 @@
package com.dfsek.terra.minestom.entity;
import net.minestom.server.entity.EntityType;
public class MinestomEntityType implements com.dfsek.terra.api.entity.EntityType {
private final EntityType delegate;
public MinestomEntityType(String id) {
delegate = EntityType.fromNamespaceId(id);
}
@Override
public EntityType getHandle() {
return delegate;
}
}
@@ -0,0 +1,51 @@
package com.dfsek.terra.minestom.item;
import com.dfsek.terra.api.inventory.ItemStack;
import com.dfsek.terra.api.inventory.item.Enchantment;
import net.minestom.server.MinecraftServer;
import net.minestom.server.item.Material;
import net.minestom.server.utils.NamespaceID;
import java.util.Objects;
public class MinestomEnchantment implements Enchantment {
private final net.minestom.server.item.enchant.Enchantment delegate;
private final String id;
public MinestomEnchantment(net.minestom.server.item.enchant.Enchantment delegate) {
this.delegate = delegate;
id = Objects.requireNonNull(delegate.registry()).raw();
}
public MinestomEnchantment(String id) {
this.delegate = MinecraftServer.getEnchantmentRegistry().get(NamespaceID.from(id));
this.id = id;
}
@Override
public boolean canEnchantItem(ItemStack itemStack) {
return delegate.supportedItems().contains((Material) itemStack.getType().getHandle());
}
@Override
public boolean conflictsWith(Enchantment other) {
return delegate.exclusiveSet().contains(NamespaceID.from(((MinestomEnchantment) other).id));
}
@Override
public String getID() {
return id;
}
@Override
public int getMaxLevel() {
return delegate.maxLevel();
}
@Override
public net.minestom.server.item.enchant.Enchantment getHandle() {
return delegate;
}
}
@@ -0,0 +1,28 @@
package com.dfsek.terra.minestom.item;
import com.dfsek.terra.api.handle.ItemHandle;
import com.dfsek.terra.api.inventory.Item;
import com.dfsek.terra.api.inventory.item.Enchantment;
import net.minestom.server.MinecraftServer;
import java.util.Set;
import java.util.stream.Collectors;
public class MinestomItemHandle implements ItemHandle {
@Override
public Item createItem(String data) {
return new MinestomMaterial(data);
}
@Override
public Enchantment getEnchantment(String id) {
return new MinestomEnchantment(id);
}
@Override
public Set<Enchantment> getEnchantments() {
return MinecraftServer.getEnchantmentRegistry().values().stream().map(MinestomEnchantment::new).collect(Collectors.toSet());
}
}
@@ -0,0 +1,31 @@
package com.dfsek.terra.minestom.item;
import com.dfsek.terra.api.inventory.item.Enchantment;
import com.dfsek.terra.api.inventory.item.ItemMeta;
import java.util.HashMap;
import java.util.Map;
public class MinestomItemMeta implements ItemMeta {
private final HashMap<Enchantment, Integer> enchantments;
public MinestomItemMeta(HashMap<Enchantment, Integer> enchantments) {
this.enchantments = enchantments;
}
@Override
public void addEnchantment(Enchantment enchantment, int level) {
enchantments.put(enchantment, level);
}
@Override
public Map<Enchantment, Integer> getEnchantments() {
return enchantments;
}
@Override
public Object getHandle() {
return enchantments;
}
}
@@ -0,0 +1,72 @@
package com.dfsek.terra.minestom.item;
import com.dfsek.terra.api.inventory.Item;
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.item.ItemStack;
import net.minestom.server.item.component.EnchantmentList;
import net.minestom.server.registry.DynamicRegistry;
import net.minestom.server.registry.DynamicRegistry.Key;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Objects;
public class MinestomItemStack implements com.dfsek.terra.api.inventory.ItemStack {
private ItemStack base;
public MinestomItemStack(net.minestom.server.item.@NotNull ItemStack base) {
this.base = base;
}
@Override
public Object getHandle() {
return base;
}
@Override
public int getAmount() {
return base.amount();
}
@Override
public void setAmount(int i) {
base = base.withAmount(i);
}
@Override
public Item getType() {
return new MinestomMaterial(base.material());
}
@Override
public ItemMeta getItemMeta() {
HashMap<Enchantment, Integer> enchantments = new HashMap<>();
EnchantmentList enchantmentList = base.get(ItemComponent.ENCHANTMENTS);
if(enchantmentList != null) {
enchantmentList.enchantments().forEach((enchantmentKey, integer) -> {
enchantments.put(
new MinestomEnchantment(Objects.requireNonNull(MinecraftServer.getEnchantmentRegistry().get(enchantmentKey))), integer);
});
}
return new MinestomItemMeta(enchantments);
}
@Override
public void setItemMeta(ItemMeta meta) {
HashMap<Key<net.minestom.server.item.enchant.Enchantment>, Integer> enchantments = new HashMap<>();
DynamicRegistry<net.minestom.server.item.enchant.Enchantment> registry = MinecraftServer.getEnchantmentRegistry();
meta.getEnchantments().forEach((key, value) -> {
MinestomEnchantment enchantment = (MinestomEnchantment) key;
enchantments.put(registry.getKey(enchantment.getHandle()), value);
});
EnchantmentList list = new EnchantmentList(enchantments);
base = base.with(ItemComponent.ENCHANTMENTS, list);
}
}
@@ -0,0 +1,34 @@
package com.dfsek.terra.minestom.item;
import com.dfsek.terra.api.inventory.Item;
import com.dfsek.terra.api.inventory.ItemStack;
import net.minestom.server.item.Material;
public class MinestomMaterial implements Item {
private final Material delegate;
public MinestomMaterial(Material delegate) {
this.delegate = delegate;
}
public MinestomMaterial(String id) {
this.delegate = Material.fromNamespaceId(id);
}
@Override
public ItemStack newItemStack(int amount) {
return new MinestomItemStack(net.minestom.server.item.ItemStack.builder(delegate).amount(amount).build());
}
@Override
public double getMaxDurability() {
return 0;
}
@Override
public Material getHandle() {
return delegate;
}
}
@@ -0,0 +1,68 @@
package com.dfsek.terra.minestom.world;
import com.dfsek.terra.api.config.ConfigPack;
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.chunk.CachedChunk;
import com.dfsek.terra.minestom.chunk.GeneratedChunkCache;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.generator.GenerationUnit;
import net.minestom.server.instance.generator.Generator;
import org.jetbrains.annotations.NotNull;
public class MinestomChunkGeneratorWrapper implements Generator, GeneratorWrapper {
private final GeneratedChunkCache cache;
private ChunkGenerator generator;
private final TerraMinestomWorld world;
private ConfigPack pack;
public MinestomChunkGeneratorWrapper(ChunkGenerator generator, TerraMinestomWorld world, ConfigPack pack) {
this.generator = generator;
this.world = world;
this.pack = pack;
this.cache = new GeneratedChunkCache(world.getDimensionType(), generator, world);
}
public ChunkGenerator getGenerator() {
return generator;
}
@Override
public void generate(@NotNull GenerationUnit unit) {
Point start = unit.absoluteStart();
int x = start.chunkX();
int z = start.chunkZ();
CachedChunk chunk = cache.at(x, z);
chunk.writeRelative(unit.modifier());
unit.fork(setter -> {
MinestomProtoWorld protoWorld = new MinestomProtoWorld(cache, x, z, world, setter);
for(GenerationStage stage : world.getPack().getStages()) {
stage.populate(protoWorld);
}
});
}
public ConfigPack getPack() {
return pack;
}
public void setPack(ConfigPack pack) {
this.pack = pack;
this.generator = pack.getGeneratorProvider().newInstance(pack);
}
public void displayStats() {
cache.displayStats();
}
@Override
public ChunkGenerator getHandle() {
return generator;
}
}
@@ -0,0 +1,111 @@
package com.dfsek.terra.minestom.world;
import com.dfsek.terra.api.block.entity.BlockEntity;
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.world.ServerWorld;
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.ProtoWorld;
import com.dfsek.terra.minestom.chunk.CachedChunk;
import com.dfsek.terra.minestom.chunk.GeneratedChunkCache;
import com.dfsek.terra.minestom.entity.DeferredMinestomEntity;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.block.Block.Setter;
public class MinestomProtoWorld implements ProtoWorld {
private final GeneratedChunkCache cache;
private final int x;
private final int z;
private final TerraMinestomWorld world;
private final Setter modifier;
public MinestomProtoWorld(GeneratedChunkCache cache, int x, int z, TerraMinestomWorld world, Setter modifier) {
this.cache = cache;
this.x = x;
this.z = z;
this.world = world;
this.modifier = modifier;
}
@Override
public int centerChunkX() {
return x;
}
@Override
public int centerChunkZ() {
return z;
}
@Override
public ServerWorld getWorld() {
return world;
}
@Override
public void setBlockState(int x, int y, int z, BlockState data, boolean physics) {
modifier.setBlock(x, y, z, (Block) data.getHandle());
}
@Override
public Entity spawnEntity(double x, double y, double z, EntityType entityType) {
TerraMinestomWorld world = this.world;
DeferredMinestomEntity entity = new DeferredMinestomEntity(x, y, z, entityType, world);
world.enqueue(entity.pos(), (chunk) -> entity.spawn());
return entity;
}
@Override
public BlockState getBlockState(int x, int y, int z) {
int chunkX = x >> 4;
int chunkZ = z >> 4;
CachedChunk chunk = cache.at(chunkX, chunkZ);
return chunk.getBlock(x & 15, y, z & 15);
}
@Override
public BlockEntity getBlockEntity(int x, int y, int z) {
return world.getBlockEntity(x, y, z);
}
@Override
public ChunkGenerator getGenerator() {
return world.getGenerator();
}
@Override
public BiomeProvider getBiomeProvider() {
return world.getBiomeProvider();
}
@Override
public ConfigPack getPack() {
return world.getPack();
}
@Override
public long getSeed() {
return world.getSeed();
}
@Override
public int getMaxHeight() {
return world.getMaxHeight();
}
@Override
public int getMinHeight() {
return world.getMinHeight();
}
@Override
public Object getHandle() {
return world;
}
}
@@ -0,0 +1,31 @@
package com.dfsek.terra.minestom.world;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.entity.EntityType;
import com.dfsek.terra.api.handle.WorldHandle;
import com.dfsek.terra.minestom.block.MinestomBlockState;
import com.dfsek.terra.minestom.entity.MinestomEntityType;
import net.minestom.server.instance.block.Block;
import org.jetbrains.annotations.NotNull;
public class MinestomWorldHandle implements WorldHandle {
private static final MinestomBlockState AIR = new MinestomBlockState(Block.AIR);
@Override
public @NotNull BlockState createBlockState(@NotNull String data) {
return new MinestomBlockState(data);
}
@Override
public @NotNull BlockState air() {
return AIR;
}
@Override
public @NotNull EntityType getEntity(@NotNull String id) {
return new MinestomEntityType(id);
}
}
@@ -0,0 +1,130 @@
package com.dfsek.terra.minestom.world;
import com.dfsek.terra.api.block.entity.BlockEntity;
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.world.ServerWorld;
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
import com.dfsek.terra.api.world.chunk.Chunk;
import com.dfsek.terra.api.world.chunk.generation.ChunkGenerator;
import com.dfsek.terra.api.world.info.WorldProperties;
import com.dfsek.terra.minestom.api.BlockEntityFactory;
import com.dfsek.terra.minestom.api.EntityFactory;
import com.dfsek.terra.minestom.block.MinestomBlockState;
import com.dfsek.terra.minestom.entity.MinestomEntity;
import net.minestom.server.MinecraftServer;
import net.minestom.server.coordinate.BlockVec;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.Instance;
import net.minestom.server.instance.block.Block;
import net.minestom.server.world.DimensionType;
import java.util.function.Consumer;
public final class TerraMinestomWorld implements ServerWorld, WorldProperties {
private final Instance instance;
private final ConfigPack pack;
private final long seed;
private final DimensionType dimensionType;
private final MinestomChunkGeneratorWrapper wrapper;
private final EntityFactory entityFactory;
private final BlockEntityFactory blockEntityFactory;
public TerraMinestomWorld(Instance instance, ConfigPack pack, long seed, EntityFactory entityFactory,
BlockEntityFactory blockEntityFactory) {
this.instance = instance;
this.pack = pack;
this.seed = seed;
this.dimensionType = MinecraftServer.getDimensionTypeRegistry().get(instance.getDimensionType());
this.blockEntityFactory = blockEntityFactory;
this.wrapper = new MinestomChunkGeneratorWrapper(pack.getGeneratorProvider().newInstance(pack), this, pack);
this.entityFactory = entityFactory;
instance.setGenerator(this.wrapper);
}
@Override
public Chunk getChunkAt(int x, int z) {
return null;
}
@Override
public void setBlockState(int x, int y, int z, BlockState data, boolean physics) {
instance.setBlock(x, y, z, (Block) data.getHandle());
}
@Override
public Entity spawnEntity(double x, double y, double z, EntityType entityType) {
return MinestomEntity.spawn(x, y, z, entityType, this);
}
public void displayStats() {
wrapper.displayStats();
}
@Override
public BlockState getBlockState(int x, int y, int z) {
return new MinestomBlockState(instance.getBlock(x, y, z));
}
@Override
public BlockEntity getBlockEntity(int x, int y, int z) {
return blockEntityFactory.createBlockEntity(new BlockVec(x, y, z));
}
@Override
public ChunkGenerator getGenerator() {
return wrapper.getGenerator();
}
@Override
public BiomeProvider getBiomeProvider() {
return pack.getBiomeProvider();
}
@Override
public ConfigPack getPack() {
return pack;
}
@Override
public long getSeed() {
return seed;
}
@Override
public int getMaxHeight() {
return dimensionType.maxY();
}
@Override
public int getMinHeight() {
return dimensionType.minY();
}
@Override
public Instance getHandle() {
return instance;
}
public DimensionType getDimensionType() {
return dimensionType;
}
public EntityFactory getEntityFactory() {
return entityFactory;
}
public void enqueue(Point position, Consumer<net.minestom.server.instance.Chunk> action) {
instance.loadChunk(position.chunkX(), position.chunkZ()).thenAccept(action);
}
}

Some files were not shown because too many files have changed in this diff Show More