mirror of
https://github.com/PolyhedralDev/Terra.git
synced 2026-02-16 02:20:57 +00:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -27,6 +27,8 @@ public class TerraMinestomExample {
|
|||||||
private TerraMinestomWorld world;
|
private TerraMinestomWorld world;
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
System.setProperty("minestom.registry.unsafe-ops", "true");
|
||||||
|
|
||||||
TerraMinestomExample example = new TerraMinestomExample();
|
TerraMinestomExample example = new TerraMinestomExample();
|
||||||
example.createNewInstance();
|
example.createNewInstance();
|
||||||
example.attachTerra();
|
example.attachTerra();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.dfsek.terra.minestom.api;
|
package com.dfsek.terra.minestom.api;
|
||||||
|
|
||||||
|
|
||||||
|
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||||
import net.minestom.server.entity.Entity;
|
import net.minestom.server.entity.Entity;
|
||||||
import net.minestom.server.entity.EntityType;
|
import net.minestom.server.entity.EntityType;
|
||||||
|
|
||||||
@@ -9,5 +10,22 @@ import net.minestom.server.entity.EntityType;
|
|||||||
* Allows adding AI to generated entities using custom entity types
|
* Allows adding AI to generated entities using custom entity types
|
||||||
*/
|
*/
|
||||||
public interface EntityFactory {
|
public interface EntityFactory {
|
||||||
|
/**
|
||||||
|
* Creates a new entity of the specified type.
|
||||||
|
*
|
||||||
|
* @param type the type of the entity to be created
|
||||||
|
* @return the created entity instance
|
||||||
|
*/
|
||||||
Entity createEntity(EntityType type);
|
Entity createEntity(EntityType type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new entity of the specified type with additional data.
|
||||||
|
*
|
||||||
|
* @param type the type of the entity to be created
|
||||||
|
* @param data the additional data for the entity, represented as a CompoundBinaryTag
|
||||||
|
* @return the created entity instance
|
||||||
|
*/
|
||||||
|
default Entity createEntity(EntityType type, CompoundBinaryTag data) {
|
||||||
|
return createEntity(type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
package com.dfsek.terra.minestom.block;
|
package com.dfsek.terra.minestom.block;
|
||||||
|
|
||||||
|
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||||
|
import net.kyori.adventure.nbt.TagStringIO;
|
||||||
import net.minestom.server.instance.block.Block;
|
import net.minestom.server.instance.block.Block;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -10,38 +13,84 @@ import com.dfsek.terra.api.block.BlockType;
|
|||||||
import com.dfsek.terra.api.block.state.BlockState;
|
import com.dfsek.terra.api.block.state.BlockState;
|
||||||
import com.dfsek.terra.api.block.state.properties.Property;
|
import com.dfsek.terra.api.block.state.properties.Property;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class MinestomBlockState implements BlockState {
|
|
||||||
|
public record MinestomBlockState(Block block) implements BlockState {
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(MinestomBlockState.class);
|
||||||
public static final MinestomBlockState AIR = new MinestomBlockState(Block.AIR);
|
public static final MinestomBlockState AIR = new MinestomBlockState(Block.AIR);
|
||||||
private final Block block;
|
private static final TagStringIO tagStringIO = TagStringIO.tagStringIO();
|
||||||
|
|
||||||
public MinestomBlockState(Block block) {
|
public MinestomBlockState {
|
||||||
if(block == null) {
|
block = Objects.requireNonNullElse(block, Block.AIR);
|
||||||
this.block = Block.AIR;
|
|
||||||
} else {
|
|
||||||
this.block = block;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MinestomBlockState(String data) {
|
public static MinestomBlockState fromStateId(String data) {
|
||||||
if(!data.contains("[")) {
|
CompoundBinaryTag nbt = CompoundBinaryTag.empty();
|
||||||
block = Block.fromKey(data);
|
int splitIndex = data.indexOf('{');
|
||||||
return;
|
if(splitIndex != -1) {
|
||||||
|
String fullId = data;
|
||||||
|
data = data.substring(0, splitIndex);
|
||||||
|
String dataString = fullId.substring(splitIndex);
|
||||||
|
try {
|
||||||
|
nbt = tagStringIO.asCompound(dataString);
|
||||||
|
} catch(IOException exception) {
|
||||||
|
LOGGER.warn("Invalid entity data, will be ignored: {}", dataString);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String[] split = data.split("\\[");
|
int openBracketIndex = data.indexOf('[');
|
||||||
String namespaceId = split[0];
|
int closeBracketIndex = data.indexOf(']');
|
||||||
String properties = split[1].substring(0, split[1].length() - 1);
|
|
||||||
|
if(openBracketIndex == -1 || closeBracketIndex == -1 || closeBracketIndex < openBracketIndex) {
|
||||||
|
// no or invalid properties
|
||||||
|
Block block = Block.fromKey(data);
|
||||||
|
if(block != null && !nbt.isEmpty()) {
|
||||||
|
block = block.withNbt(nbt);
|
||||||
|
}
|
||||||
|
return new MinestomBlockState(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
String namespaceId = data.substring(0, openBracketIndex);
|
||||||
|
String propertiesContent = data.substring(openBracketIndex + 1, closeBracketIndex);
|
||||||
Block block = Block.fromKey(namespaceId);
|
Block block = Block.fromKey(namespaceId);
|
||||||
HashMap<String, String> propertiesMap = new HashMap<>();
|
if (block == null) {
|
||||||
|
LOGGER.error("Invalid block ID found during parsing: {}", namespaceId);
|
||||||
for(String property : properties.split(",")) {
|
return new MinestomBlockState(Block.AIR);
|
||||||
String[] kv = property.split("=");
|
|
||||||
propertiesMap.put(kv[0].strip(), kv[1].strip());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert block != null;
|
HashMap<String, String> propertiesMap = new HashMap<>();
|
||||||
this.block = block.withProperties(propertiesMap);
|
int current = 0;
|
||||||
|
while (current < propertiesContent.length()) {
|
||||||
|
int nextComma = propertiesContent.indexOf(',', current);
|
||||||
|
String property;
|
||||||
|
|
||||||
|
if (nextComma == -1) {
|
||||||
|
property = propertiesContent.substring(current);
|
||||||
|
current = propertiesContent.length();
|
||||||
|
} else {
|
||||||
|
property = propertiesContent.substring(current, nextComma);
|
||||||
|
current = nextComma + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int equalsIndex = property.indexOf('=');
|
||||||
|
|
||||||
|
if (equalsIndex == -1) {
|
||||||
|
LOGGER.warn("Invalid block property syntax (missing '=') in string: {}", property);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String key = property.substring(0, equalsIndex).strip();
|
||||||
|
String value = property.substring(equalsIndex + 1).strip();
|
||||||
|
propertiesMap.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!nbt.isEmpty()) {
|
||||||
|
block = block.withNbt(nbt);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MinestomBlockState(block.withProperties(propertiesMap));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -15,26 +15,27 @@ import com.dfsek.terra.minestom.block.MinestomBlockState;
|
|||||||
public class CachedChunk implements ProtoChunk {
|
public class CachedChunk implements ProtoChunk {
|
||||||
private final int minHeight;
|
private final int minHeight;
|
||||||
private final int maxHeight;
|
private final int maxHeight;
|
||||||
private final Block[] blocks;
|
private final MinestomBlockState[] blocks;
|
||||||
|
|
||||||
public CachedChunk(int minHeight, int maxHeight) {
|
public CachedChunk(int minHeight, int maxHeight) {
|
||||||
this.minHeight = minHeight;
|
this.minHeight = minHeight;
|
||||||
this.maxHeight = maxHeight;
|
this.maxHeight = maxHeight;
|
||||||
this.blocks = new Block[16 * (maxHeight - minHeight + 1) * 16];
|
this.blocks = new MinestomBlockState[16 * (maxHeight - minHeight + 1) * 16];
|
||||||
Arrays.fill(blocks, Block.AIR);
|
Arrays.fill(blocks, MinestomBlockState.AIR);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeRelative(UnitModifier modifier) {
|
public void writeRelative(UnitModifier modifier) {
|
||||||
modifier.setAllRelative((x, y, z) -> blocks[getIndex(x, y + minHeight, z)]);
|
modifier.setAllRelative((x, y, z) -> blocks[getIndex(x, y + minHeight, z)].block());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setBlock(int x, int y, int z, @NotNull BlockState blockState) {
|
public void setBlock(int x, int y, int z, @NotNull BlockState blockState) {
|
||||||
Block block = (Block) blockState.getHandle();
|
MinestomBlockState minestomBlockState = (MinestomBlockState) blockState;
|
||||||
|
Block block = minestomBlockState.block();
|
||||||
if(block == null) return;
|
if(block == null) return;
|
||||||
int index = getIndex(x, y, z);
|
int index = getIndex(x, y, z);
|
||||||
if(index > blocks.length || index < 0) return;
|
if(index > blocks.length || index < 0) return;
|
||||||
blocks[index] = block;
|
blocks[index] = minestomBlockState;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getIndex(int x, int y, int z) {
|
private int getIndex(int x, int y, int z) {
|
||||||
@@ -46,7 +47,7 @@ public class CachedChunk implements ProtoChunk {
|
|||||||
public @NotNull BlockState getBlock(int x, int y, int z) {
|
public @NotNull BlockState getBlock(int x, int y, int z) {
|
||||||
int index = getIndex(x, y, z);
|
int index = getIndex(x, y, z);
|
||||||
if(index > blocks.length || index < 0) return MinestomBlockState.AIR;
|
if(index > blocks.length || index < 0) return MinestomBlockState.AIR;
|
||||||
return new MinestomBlockState(blocks[index]);
|
return blocks[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.github.benmanes.caffeine.cache.Caffeine;
|
|||||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||||
import com.github.benmanes.caffeine.cache.stats.CacheStats;
|
import com.github.benmanes.caffeine.cache.stats.CacheStats;
|
||||||
import net.minestom.server.world.DimensionType;
|
import net.minestom.server.world.DimensionType;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -15,7 +16,7 @@ import com.dfsek.terra.api.world.chunk.generation.ChunkGenerator;
|
|||||||
|
|
||||||
public class GeneratedChunkCache {
|
public class GeneratedChunkCache {
|
||||||
private static final Logger log = LoggerFactory.getLogger(GeneratedChunkCache.class);
|
private static final Logger log = LoggerFactory.getLogger(GeneratedChunkCache.class);
|
||||||
private final LoadingCache<Pair<Integer, Integer>, CachedChunk> cache;
|
private final LoadingCache<@NotNull Long, CachedChunk> cache;
|
||||||
private final DimensionType dimensionType;
|
private final DimensionType dimensionType;
|
||||||
private final ChunkGenerator generator;
|
private final ChunkGenerator generator;
|
||||||
private final ServerWorld world;
|
private final ServerWorld world;
|
||||||
@@ -29,7 +30,7 @@ public class GeneratedChunkCache {
|
|||||||
this.cache = Caffeine.newBuilder()
|
this.cache = Caffeine.newBuilder()
|
||||||
.maximumSize(128)
|
.maximumSize(128)
|
||||||
.recordStats()
|
.recordStats()
|
||||||
.build((Pair<Integer, Integer> key) -> generateChunk(key.getLeft(), key.getRight()));
|
.build((Long key) -> generateChunk(unpackX(key), unpackZ(key)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private CachedChunk generateChunk(int x, int z) {
|
private CachedChunk generateChunk(int x, int z) {
|
||||||
@@ -50,6 +51,18 @@ public class GeneratedChunkCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public CachedChunk at(int x, int z) {
|
public CachedChunk at(int x, int z) {
|
||||||
return cache.get(Pair.of(x, z));
|
return cache.get(pack(x, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
private long pack(final int x, final int z) {
|
||||||
|
return ((long) x) << 32 | z & 0xFFFFFFFFL;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int unpackX(long key) {
|
||||||
|
return (int) (key >>> 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int unpackZ(long key) {
|
||||||
|
return (int) key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ public class MinestomEntity implements com.dfsek.terra.api.entity.Entity {
|
|||||||
|
|
||||||
public static MinestomEntity spawn(double x, double y, double z, EntityType type, TerraMinestomWorld world) {
|
public static MinestomEntity spawn(double x, double y, double z, EntityType type, TerraMinestomWorld world) {
|
||||||
Instance instance = world.getHandle();
|
Instance instance = world.getHandle();
|
||||||
Entity entity = world.getEntityFactory().createEntity(((MinestomEntityType) type).getHandle());
|
MinestomEntityType entityType = (MinestomEntityType) type;
|
||||||
|
Entity entity = world.getEntityFactory().createEntity(entityType.getHandle(), entityType.getData());
|
||||||
entity.setInstance(instance, new Pos(x, y, z));
|
entity.setInstance(instance, new Pos(x, y, z));
|
||||||
return new MinestomEntity(entity, world);
|
return new MinestomEntity(entity, world);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,38 @@
|
|||||||
package com.dfsek.terra.minestom.entity;
|
package com.dfsek.terra.minestom.entity;
|
||||||
|
|
||||||
|
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||||
|
import net.kyori.adventure.nbt.TagStringIO;
|
||||||
import net.minestom.server.entity.EntityType;
|
import net.minestom.server.entity.EntityType;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
|
||||||
public class MinestomEntityType implements com.dfsek.terra.api.entity.EntityType {
|
public class MinestomEntityType implements com.dfsek.terra.api.entity.EntityType {
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(MinestomEntityType.class);
|
||||||
|
private static final TagStringIO tagStringIO = TagStringIO.tagStringIO();
|
||||||
private final EntityType delegate;
|
private final EntityType delegate;
|
||||||
|
private final CompoundBinaryTag data;
|
||||||
|
|
||||||
public MinestomEntityType(String id) {
|
public MinestomEntityType(String id) {
|
||||||
|
int splitIndex = id.indexOf('{');
|
||||||
|
if(splitIndex != -1) {
|
||||||
|
String fullId = id;
|
||||||
|
id = id.substring(0, splitIndex);
|
||||||
|
String dataString = fullId.substring(splitIndex);
|
||||||
|
CompoundBinaryTag data;
|
||||||
|
try {
|
||||||
|
data = tagStringIO.asCompound(dataString);
|
||||||
|
} catch(IOException exception) {
|
||||||
|
LOGGER.warn("Invalid entity data, will be ignored: {}", dataString);
|
||||||
|
data = CompoundBinaryTag.empty();
|
||||||
|
}
|
||||||
|
this.data = data;
|
||||||
|
} else {
|
||||||
|
this.data = CompoundBinaryTag.empty();
|
||||||
|
}
|
||||||
|
|
||||||
delegate = EntityType.fromKey(id);
|
delegate = EntityType.fromKey(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14,4 +40,8 @@ public class MinestomEntityType implements com.dfsek.terra.api.entity.EntityType
|
|||||||
public EntityType getHandle() {
|
public EntityType getHandle() {
|
||||||
return delegate;
|
return delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CompoundBinaryTag getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,7 @@ public class MinestomWorldHandle implements WorldHandle {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull BlockState createBlockState(@NotNull String data) {
|
public @NotNull BlockState createBlockState(@NotNull String data) {
|
||||||
return new MinestomBlockState(data);
|
return MinestomBlockState.fromStateId(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user