9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2026-01-04 15:41:38 +00:00

Merge branch 'dev' into preparing-block-entity

This commit is contained in:
XiaoMoMi
2025-09-04 06:27:02 +08:00
committed by GitHub
208 changed files with 3653 additions and 1636 deletions

View File

@@ -65,6 +65,8 @@ dependencies {
compileOnly("com.mojang:brigadier:${rootProject.properties["mojang_brigadier_version"]}")
// authlib
compileOnly("com.mojang:authlib:${rootProject.properties["authlib_version"]}")
// concurrentutil
compileOnly("ca.spottedleaf:concurrentutil:${rootProject.properties["concurrent_util_version"]}")
}
java {
@@ -99,6 +101,10 @@ tasks {
relocate("com.google.common.jimfs", "net.momirealms.craftengine.libraries.jimfs")
relocate("org.apache.commons", "net.momirealms.craftengine.libraries.commons")
relocate("io.leangen.geantyref", "net.momirealms.craftengine.libraries.geantyref")
relocate("io.netty.handler.codec.http", "net.momirealms.craftengine.libraries.netty.handler.codec.http")
relocate("io.netty.handler.codec.rtsp", "net.momirealms.craftengine.libraries.netty.handler.codec.rtsp")
relocate("io.netty.handler.codec.spdy", "net.momirealms.craftengine.libraries.netty.handler.codec.spdy")
relocate("io.netty.handler.codec.http2", "net.momirealms.craftengine.libraries.netty.handler.codec.http2")
}
}

View File

@@ -6,11 +6,11 @@ import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext;
import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider;
import net.momirealms.craftengine.core.util.AdventureHelper;
import net.momirealms.craftengine.core.util.FriendlyByteBuf;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.sparrow.nbt.Tag;
import java.util.Map;
import java.util.Optional;
@@ -51,6 +51,16 @@ public class AdvancementDisplay {
public void applyClientboundData(Player player) {
this.icon = CraftEngine.instance().itemManager().s2c(this.icon, player);
if (Config.interceptAdvancement()) {
Map<String, ComponentProvider> tokens1 = CraftEngine.instance().fontManager().matchTags(AdventureHelper.componentToJson(this.title));
if (!tokens1.isEmpty()) {
this.title = AdventureHelper.replaceText(this.title, tokens1, NetworkTextReplaceContext.of(player));
}
Map<String, ComponentProvider> tokens2 = CraftEngine.instance().fontManager().matchTags(AdventureHelper.componentToJson(this.description));
if (!tokens2.isEmpty()) {
this.description = AdventureHelper.replaceText(this.description, tokens2, NetworkTextReplaceContext.of(player));
}
}
}
public void write(FriendlyByteBuf buf) {
@@ -75,8 +85,8 @@ public class AdvancementDisplay {
}
public static AdvancementDisplay read(FriendlyByteBuf buf) {
Component title = readComponent(buf);
Component description = readComponent(buf);
Component title = buf.readComponent();
Component description = buf.readComponent();
Item<Object> icon = CraftEngine.instance().itemManager().decode(buf);
AdvancementType type = AdvancementType.byId(buf.readVarInt());
int flags = buf.readInt();
@@ -88,28 +98,4 @@ public class AdvancementDisplay {
float y = buf.readFloat();
return new AdvancementDisplay(title, description, icon, background, type, showToast, hidden, x, y);
}
private static Component readComponent(FriendlyByteBuf buf) {
if (Config.interceptAdvancement()) {
if (VersionHelper.isOrAbove1_20_3()) {
Tag nbt = buf.readNbt(false);
Map<String, Component> tokens = CraftEngine.instance().fontManager().matchTags(nbt.getAsString());
Component component = AdventureHelper.nbtToComponent(nbt);
if (!tokens.isEmpty()) {
component = AdventureHelper.replaceText(component, tokens);
}
return component;
} else {
String json = buf.readUtf();
Component component = AdventureHelper.jsonToComponent(json);
Map<String, Component> tokens = CraftEngine.instance().fontManager().matchTags(json);
if (!tokens.isEmpty()) {
component = AdventureHelper.replaceText(component, tokens);
}
return component;
}
} else {
return buf.readComponent();
}
}
}

View File

@@ -324,6 +324,7 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem
// 结合variants
JsonElement combinedVariant = GsonHelper.combine(variants);
Map<String, JsonElement> overrideMap = AbstractBlockManager.this.blockStateOverrides.computeIfAbsent(blockId, k -> new HashMap<>());
AbstractBlockManager.this.tempVanillaBlockStateModels.put(registryId, combinedVariant);
JsonElement previous = overrideMap.get(propertyNBT);
if (previous != null && !previous.equals(combinedVariant)) {
throw new LocalizedResourceConfigException("warning.config.block.state.model.conflict", GsonHelper.get().toJson(combinedVariant), blockState, GsonHelper.get().toJson(previous));
@@ -397,8 +398,8 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem
}
} else {
// 其他情况则是完整的方块
BlockStateWrapper packedBlockState = createPackedBlockState(blockState);
if (packedBlockState == null || !packedBlockState.isVanillaBlock()) {
BlockStateWrapper packedBlockState = createBlockState(blockState);
if (packedBlockState == null) {
throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", blockState);
}
registryId = packedBlockState.registryId();

View File

@@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.block;
import com.google.common.collect.ImmutableMap;
import net.momirealms.craftengine.core.block.behavior.EmptyBlockBehavior;
import net.momirealms.craftengine.core.block.behavior.EntityBlockBehavior;
import net.momirealms.craftengine.core.block.parser.BlockNbtParser;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
@@ -58,6 +59,8 @@ public abstract class AbstractCustomBlock implements CustomBlock {
placements.add(Property.createStateForPlacement(propertyEntry.getKey(), propertyEntry.getValue()));
}
this.placementFunction = composite(placements);
EntityBlockBehavior entityBlockBehavior = this.behavior.getEntityBehavior();
boolean isEntityBlock = entityBlockBehavior != null;
for (Map.Entry<String, BlockStateVariant> entry : variantMapper.entrySet()) {
String nbtString = entry.getKey();
CompoundTag tag = BlockNbtParser.deserialize(this, nbtString);
@@ -77,15 +80,19 @@ public abstract class AbstractCustomBlock implements CustomBlock {
// Late init states
ImmutableBlockState state = possibleStates.getFirst();
state.setSettings(blockStateVariant.settings());
state.setVanillaBlockState((BlockStateWrapper.VanillaBlockState) BlockRegistryMirror.stateByRegistryId(vanillaStateRegistryId));
state.setCustomBlockState((BlockStateWrapper.CustomBlockState) BlockRegistryMirror.stateByRegistryId(blockStateVariant.internalRegistryId()));
state.setVanillaBlockState(BlockRegistryMirror.stateByRegistryId(vanillaStateRegistryId));
state.setCustomBlockState(BlockRegistryMirror.stateByRegistryId(blockStateVariant.internalRegistryId()));
}
// double check if there's any invalid state
for (ImmutableBlockState state : this.variantProvider().states()) {
state.setBehavior(this.behavior);
if (state.settings() == null) {
state.setSettings(settings);
}
if (isEntityBlock) {
state.setBlockEntityType(entityBlockBehavior.blockEntityType());
}
}
this.applyPlatformSettings();
}

View File

@@ -1,5 +1,6 @@
package net.momirealms.craftengine.core.block;
import net.momirealms.craftengine.core.block.behavior.EntityBlockBehavior;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.item.CustomItem;
import net.momirealms.craftengine.core.item.Item;
@@ -10,6 +11,7 @@ import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.Key;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
import java.util.concurrent.Callable;
@@ -24,6 +26,14 @@ public abstract class BlockBehavior {
return Optional.empty();
}
@Nullable
public EntityBlockBehavior getEntityBehavior() {
if (this instanceof EntityBlockBehavior behavior) {
return behavior;
}
return null;
}
// BlockState state, Rotation rotation
public Object rotate(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
return superMethod.call();

View File

@@ -8,6 +8,7 @@ public final class BlockKeys {
public static final Key NOTE_BLOCK = Key.of("minecraft:note_block");
public static final Key TRIPWIRE = Key.of("minecraft:tripwire");
public static final Key CRAFTING_TABLE = Key.of("minecraft:crafting_table");
public static final Key CARTOGRAPHY_TABLE = Key.of("minecraft:cartography_table");
public static final Key STONECUTTER = Key.of("minecraft:stonecutter");
public static final Key BELL = Key.of("minecraft:bell");
public static final Key SMITHING_TABLE = Key.of("minecraft:smithing_table");
@@ -176,8 +177,6 @@ public final class BlockKeys {
public static final Key WAXED_OXIDIZED_COPPER_TRAPDOOR = Key.of("minecraft:waxed_oxidized_copper_trapdoor");
public static final Key WAXED_WEATHERED_COPPER_TRAPDOOR = Key.of("minecraft:waxed_weathered_copper_trapdoor");
public static final Key OAK_FENCE_GATE = Key.of("minecraft:oak_fence_gate");
public static final Key SPRUCE_FENCE_GATE = Key.of("minecraft:spruce_fence_gate");
public static final Key BIRCH_FENCE_GATE = Key.of("minecraft:birch_fence_gate");

View File

@@ -43,5 +43,5 @@ public interface BlockManager extends Manageable, ModelGenerator {
ImmutableBlockState getImmutableBlockState(int stateId);
@Nullable
BlockStateWrapper createPackedBlockState(String blockState);
BlockStateWrapper createBlockState(String blockState);
}

View File

@@ -38,6 +38,9 @@ public class BlockSettings {
LazyReference<Set<Key>> correctTools = LazyReference.lazyReference(Set::of);
String name;
String supportShapeBlockState;
float friction = 0.6f;
float speedFactor = 1f;
float jumpFactor = 1f;
private BlockSettings() {}
@@ -101,6 +104,9 @@ public class BlockSettings {
newSettings.supportShapeBlockState = settings.supportShapeBlockState;
newSettings.propagatesSkylightDown = settings.propagatesSkylightDown;
newSettings.useShapeForLightOcclusion = settings.useShapeForLightOcclusion;
newSettings.speedFactor = settings.speedFactor;
newSettings.jumpFactor = settings.jumpFactor;
newSettings.friction = settings.friction;
return newSettings;
}
@@ -140,6 +146,18 @@ public class BlockSettings {
return hardness;
}
public float friction() {
return friction;
}
public float jumpFactor() {
return jumpFactor;
}
public float speedFactor() {
return speedFactor;
}
public Tristate canOcclude() {
return canOcclude;
}
@@ -236,6 +254,21 @@ public class BlockSettings {
return this;
}
public BlockSettings friction(float friction) {
this.friction = friction;
return this;
}
public BlockSettings speedFactor(float speedFactor) {
this.speedFactor = speedFactor;
return this;
}
public BlockSettings jumpFactor(float jumpFactor) {
this.jumpFactor = jumpFactor;
return this;
}
public BlockSettings tags(Set<Key> tags) {
this.tags = tags;
return this;
@@ -384,6 +417,18 @@ public class BlockSettings {
float floatValue = ResourceConfigUtils.getAsFloat(value, "hardness");
return settings -> settings.hardness(floatValue);
}));
registerFactory("friction", (value -> {
float floatValue = ResourceConfigUtils.getAsFloat(value, "friction");
return settings -> settings.friction(floatValue);
}));
registerFactory("speed-factor", (value -> {
float floatValue = ResourceConfigUtils.getAsFloat(value, "speed-factor");
return settings -> settings.speedFactor(floatValue);
}));
registerFactory("jump-factor", (value -> {
float floatValue = ResourceConfigUtils.getAsFloat(value, "jump-factor");
return settings -> settings.jumpFactor(floatValue);
}));
registerFactory("resistance", (value -> {
float floatValue = ResourceConfigUtils.getAsFloat(value, "resistance");
return settings -> settings.resistance(floatValue);

View File

@@ -2,71 +2,7 @@ package net.momirealms.craftengine.core.block;
public interface BlockStateWrapper {
Object handle();
Object literalObject();
int registryId();
boolean isVanillaBlock();
static BlockStateWrapper vanilla(Object handle, int registryId) {
return new VanillaBlockState(handle, registryId);
}
static BlockStateWrapper custom(Object handle, int registryId) {
return new CustomBlockState(handle, registryId);
}
static BlockStateWrapper create(Object handle, int registryId, boolean isVanillaBlock) {
if (isVanillaBlock) return new VanillaBlockState(handle, registryId);
else return new CustomBlockState(handle, registryId);
}
abstract class AbstractBlockState implements BlockStateWrapper {
protected final Object handle;
protected final int registryId;
public AbstractBlockState(Object handle, int registryId) {
this.handle = handle;
this.registryId = registryId;
}
@Override
public Object handle() {
return this.handle;
}
@Override
public int registryId() {
return this.registryId;
}
}
class VanillaBlockState extends AbstractBlockState {
public VanillaBlockState(Object handle, int registryId) {
super(handle, registryId);
}
@Override
public boolean isVanillaBlock() {
return true;
}
}
class CustomBlockState extends AbstractBlockState {
public CustomBlockState(Object handle, int registryId) {
super(handle, registryId);
}
@Override
public DelegatingBlockState handle() {
return (DelegatingBlockState) super.handle();
}
@Override
public boolean isVanillaBlock() {
return false;
}
}
}

View File

@@ -19,11 +19,13 @@ public interface CustomBlock {
Key id();
@Nullable LootTable<?> lootTable();
@Nullable
LootTable<?> lootTable();
void execute(PlayerOptionalContext context, EventTrigger trigger);
@NotNull BlockStateVariantProvider variantProvider();
@NotNull
BlockStateVariantProvider variantProvider();
List<ImmutableBlockState> getPossibleStates(CompoundTag nbt);

View File

@@ -1,6 +1,10 @@
package net.momirealms.craftengine.core.block;
import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap;
import net.momirealms.craftengine.core.block.behavior.EntityBlockBehavior;
import net.momirealms.craftengine.core.block.entity.BlockEntity;
import net.momirealms.craftengine.core.block.entity.BlockEntityType;
import net.momirealms.craftengine.core.block.entity.tick.BlockEntityTicker;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.Item;
@@ -8,6 +12,7 @@ import net.momirealms.craftengine.core.loot.LootTable;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.registry.Holder;
import net.momirealms.craftengine.core.world.CEWorld;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.sparrow.nbt.CompoundTag;
import net.momirealms.sparrow.nbt.NBT;
@@ -19,11 +24,12 @@ import java.util.List;
public final class ImmutableBlockState extends BlockStateHolder {
private CompoundTag tag;
private BlockStateWrapper.CustomBlockState customBlockState;
private BlockStateWrapper.VanillaBlockState vanillaBlockState;
private BlockStateWrapper customBlockState;
private BlockStateWrapper vanillaBlockState;
private BlockBehavior behavior;
private Integer hashCode;
private BlockSettings settings;
private BlockEntityType<? extends BlockEntity> blockEntityType;
ImmutableBlockState(
Holder<CustomBlock> owner,
@@ -48,6 +54,14 @@ public final class ImmutableBlockState extends BlockStateHolder {
this.settings = settings;
}
public BlockEntityType<? extends BlockEntity> blockEntityType() {
return blockEntityType;
}
public void setBlockEntityType(BlockEntityType<? extends BlockEntity> blockEntityType) {
this.blockEntityType = blockEntityType;
}
public boolean isEmpty() {
return this == EmptyBlock.STATE;
}
@@ -67,6 +81,10 @@ public final class ImmutableBlockState extends BlockStateHolder {
return this.hashCode;
}
public boolean hasBlockEntity() {
return this.blockEntityType != null;
}
public BlockStateWrapper customBlockState() {
return this.customBlockState;
}
@@ -75,11 +93,11 @@ public final class ImmutableBlockState extends BlockStateHolder {
return this.vanillaBlockState;
}
public void setCustomBlockState(@NotNull BlockStateWrapper.CustomBlockState customBlockState) {
public void setCustomBlockState(@NotNull BlockStateWrapper customBlockState) {
this.customBlockState = customBlockState;
}
public void setVanillaBlockState(@NotNull BlockStateWrapper.VanillaBlockState vanillaBlockState) {
public void setVanillaBlockState(@NotNull BlockStateWrapper vanillaBlockState) {
this.vanillaBlockState = vanillaBlockState;
}
@@ -132,4 +150,11 @@ public final class ImmutableBlockState extends BlockStateHolder {
if (lootTable == null) return List.of();
return lootTable.getRandomItems(builder.withParameter(DirectContextParameters.CUSTOM_BLOCK_STATE, this).build(), world, player);
}
@SuppressWarnings("unchecked")
public <T extends BlockEntity> BlockEntityTicker<T> createBlockEntityTicker(CEWorld world, BlockEntityType<? extends BlockEntity> type) {
EntityBlockBehavior blockBehavior = this.behavior.getEntityBehavior();
if (blockBehavior == null) return null;
return (BlockEntityTicker<T>) blockBehavior.createBlockEntityTicker(world, this, type);
}
}

View File

@@ -0,0 +1,19 @@
package net.momirealms.craftengine.core.block.behavior;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.entity.BlockEntity;
import net.momirealms.craftengine.core.block.entity.BlockEntityType;
import net.momirealms.craftengine.core.block.entity.tick.BlockEntityTicker;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.CEWorld;
public interface EntityBlockBehavior {
<T extends BlockEntity> BlockEntityType<T> blockEntityType();
BlockEntity createBlockEntity(BlockPos pos, ImmutableBlockState state);
default <T extends BlockEntity> BlockEntityTicker<T> createBlockEntityTicker(CEWorld level, ImmutableBlockState state, BlockEntityType<T> blockEntityType) {
return null;
}
}

View File

@@ -0,0 +1,107 @@
package net.momirealms.craftengine.core.block.entity;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.CEWorld;
import net.momirealms.craftengine.core.world.ChunkPos;
import net.momirealms.craftengine.core.world.SectionPos;
import net.momirealms.sparrow.nbt.CompoundTag;
public abstract class BlockEntity {
protected final BlockPos pos;
protected ImmutableBlockState blockState;
protected BlockEntityType<? extends BlockEntity> type;
protected CEWorld world;
protected boolean valid;
protected BlockEntity(BlockEntityType<? extends BlockEntity> type, BlockPos pos, ImmutableBlockState blockState) {
this.pos = pos;
this.blockState = blockState;
this.type = type;
}
public final CompoundTag saveAsTag() {
CompoundTag tag = new CompoundTag();
this.saveId(tag);
this.savePos(tag);
this.saveCustomData(tag);
return tag;
}
private void saveId(CompoundTag tag) {
tag.putString("id", this.type.id().asString());
}
public void setBlockState(ImmutableBlockState blockState) {
this.blockState = blockState;
}
public ImmutableBlockState blockState() {
return blockState;
}
public CEWorld world() {
return world;
}
public void setWorld(CEWorld world) {
this.world = world;
}
public boolean isValid() {
return valid;
}
public void setValid(boolean valid) {
this.valid = valid;
}
private void savePos(CompoundTag tag) {
tag.putInt("x", this.pos.x());
tag.putInt("y", this.pos.y());
tag.putInt("z", this.pos.z());
}
protected void saveCustomData(CompoundTag tag) {
}
public void loadCustomData(CompoundTag tag) {
}
public void preRemove() {
}
public static BlockPos readPos(CompoundTag tag) {
return new BlockPos(tag.getInt("x"), tag.getInt("y"), tag.getInt("z"));
}
public BlockEntityType<? extends BlockEntity> type() {
return type;
}
public static BlockPos readPosAndVerify(CompoundTag tag, ChunkPos chunkPos) {
int x = tag.getInt("x", 0);
int y = tag.getInt("y", 0);
int z = tag.getInt("z", 0);
int sectionX = SectionPos.blockToSectionCoord(x);
int sectionZ = SectionPos.blockToSectionCoord(z);
if (sectionX != chunkPos.x || sectionZ != chunkPos.z) {
x = chunkPos.x * 16 + SectionPos.sectionRelative(x);
z = chunkPos.z * 16 + SectionPos.sectionRelative(z);
}
return new BlockPos(x, y, z);
}
public BlockPos pos() {
return pos;
}
public boolean isValidBlockState(ImmutableBlockState blockState) {
return this.type == blockState.blockEntityType();
}
public interface Factory<T extends BlockEntity> {
T create(BlockPos pos, ImmutableBlockState state);
}
}

View File

@@ -0,0 +1,6 @@
package net.momirealms.craftengine.core.block.entity;
import net.momirealms.craftengine.core.util.Key;
public record BlockEntityType<T extends BlockEntity>(Key id, BlockEntity.Factory<T> factory) {
}

View File

@@ -0,0 +1,5 @@
package net.momirealms.craftengine.core.block.entity;
public class BlockEntityTypes {
}

View File

@@ -0,0 +1,11 @@
package net.momirealms.craftengine.core.block.entity.tick;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.entity.BlockEntity;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.CEWorld;
public interface BlockEntityTicker<T extends BlockEntity> {
void tick(CEWorld world, BlockPos pos, ImmutableBlockState state, T blockEntity);
}

View File

@@ -0,0 +1,21 @@
package net.momirealms.craftengine.core.block.entity.tick;
import net.momirealms.craftengine.core.world.BlockPos;
public class DummyTickingBlockEntity implements TickingBlockEntity {
public static final DummyTickingBlockEntity INSTANCE = new DummyTickingBlockEntity();
@Override
public boolean isValid() {
return false;
}
@Override
public void tick() {
}
@Override
public BlockPos pos() {
return BlockPos.ZERO;
}
}

View File

@@ -0,0 +1,34 @@
package net.momirealms.craftengine.core.block.entity.tick;
import net.momirealms.craftengine.core.world.BlockPos;
public class ReplaceableTickingBlockEntity implements TickingBlockEntity {
private TickingBlockEntity target;
public ReplaceableTickingBlockEntity(TickingBlockEntity target) {
this.target = target;
}
public TickingBlockEntity target() {
return target;
}
public void setTicker(TickingBlockEntity target) {
this.target = target;
}
@Override
public BlockPos pos() {
return this.target.pos();
}
@Override
public void tick() {
this.target.tick();
}
@Override
public boolean isValid() {
return this.target.isValid();
}
}

View File

@@ -0,0 +1,12 @@
package net.momirealms.craftengine.core.block.entity.tick;
import net.momirealms.craftengine.core.world.BlockPos;
public interface TickingBlockEntity {
void tick();
boolean isValid();
BlockPos pos();
}

View File

@@ -0,0 +1,51 @@
package net.momirealms.craftengine.core.block.entity.tick;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.entity.BlockEntity;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.logger.Debugger;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.chunk.CEChunk;
public class TickingBlockEntityImpl<T extends BlockEntity> implements TickingBlockEntity {
private final T blockEntity;
private final BlockEntityTicker<T> ticker;
private final CEChunk chunk;
public TickingBlockEntityImpl(CEChunk chunk, T blockEntity, BlockEntityTicker<T> ticker) {
this.blockEntity = blockEntity;
this.ticker = ticker;
this.chunk = chunk;
}
@Override
public BlockPos pos() {
return this.blockEntity.pos();
}
@Override
public void tick() {
// 已无效
if (!this.isValid()) return;
// 还没加载完全
if (this.blockEntity.world() == null) return;
BlockPos pos = pos();
ImmutableBlockState state = this.chunk.getBlockState(pos);
// 不是合法方块
if (!this.blockEntity.isValidBlockState(state)) {
this.chunk.removeBlockEntity(pos);
Debugger.BLOCK_ENTITY.warn(() -> "Invalid block entity(" + this.blockEntity.getClass().getSimpleName() + ") with state " + state + " found at world " + this.chunk.world().name() + " " + pos, null);
return;
}
try {
this.ticker.tick(this.chunk.world(), pos, state, this.blockEntity);
} catch (Throwable t) {
CraftEngine.instance().logger().warn("Failed to tick block entity(" + this.blockEntity.getClass().getSimpleName() + ") at world " + this.chunk.world().name() + " " + pos, t);
}
}
@Override
public boolean isValid() {
return this.blockEntity.isValid();
}
}

View File

@@ -0,0 +1,14 @@
package net.momirealms.craftengine.core.block.state;
import java.util.Collection;
public interface StatePropertyAccessor {
String getPropertyValueAsString(String property);
Collection<String> getPropertyNames();
boolean hasProperty(String property);
<T> T getPropertyValue(String property);
}

View File

@@ -10,8 +10,6 @@ import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.world.BlockPos;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public abstract class Player extends AbstractEntity implements NetWorkUser {
private static final Key TYPE = Key.of("minecraft:player");
@@ -26,10 +24,6 @@ public abstract class Player extends AbstractEntity implements NetWorkUser {
@Override
public abstract Object serverPlayer();
public abstract void sendPackets(List<Object> packet, boolean immediately);
public abstract void sendPackets(List<Object> packet, boolean immediately, Runnable sendListener);
public abstract float getDestroyProgress(Object blockState, BlockPos pos);
public abstract void setClientSideCanBreakBlock(boolean canBreak);
@@ -52,6 +46,14 @@ public abstract class Player extends AbstractEntity implements NetWorkUser {
public abstract boolean isSneaking();
public abstract boolean isSwimming();
public abstract boolean isClimbing();
public abstract boolean isGliding();
public abstract boolean isFlying();
public abstract GameMode gameMode();
public abstract void setGameMode(GameMode gameMode);
@@ -104,12 +106,12 @@ public abstract class Player extends AbstractEntity implements NetWorkUser {
public abstract void unloadCurrentResourcePack();
public abstract void performCommand(String command);
public abstract void performCommand(String command, boolean asOp);
public abstract void performCommandAsEvent(String command);
public abstract double luck();
public abstract boolean isFlying();
@Override
public Key type() {
return TYPE;

View File

@@ -10,6 +10,7 @@ import net.momirealms.craftengine.core.plugin.config.ConfigParser;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider;
import net.momirealms.craftengine.core.util.*;
import org.ahocorasick.trie.Token;
import org.ahocorasick.trie.Trie;
@@ -39,9 +40,9 @@ public abstract class AbstractFontManager implements FontManager {
private final EmojiParser emojiParser;
private OffsetFont offsetFont;
protected Trie imageTagTrie;
protected Trie networkTagTrie;
protected Trie emojiKeywordTrie;
protected Map<String, Component> tagMapper;
protected Map<String, ComponentProvider> networkTagMapper;
protected Map<String, Emoji> emojiMapper;
protected List<Emoji> emojiList;
protected List<String> allEmojiSuggestions;
@@ -57,6 +58,7 @@ public abstract class AbstractFontManager implements FontManager {
this.offsetFont = Optional.ofNullable(plugin.config().settings().getSection("image.offset-characters"))
.map(OffsetFont::new)
.orElse(null);
this.networkTagMapper = new HashMap<>(1024);
}
@Override
@@ -65,6 +67,14 @@ public abstract class AbstractFontManager implements FontManager {
this.images.clear();
this.illegalChars.clear();
this.emojis.clear();
this.networkTagTrie = null;
this.emojiKeywordTrie = null;
if (this.networkTagMapper != null) {
this.networkTagMapper.clear();
}
if (this.emojiMapper != null) {
this.emojiMapper.clear();
}
}
@Override
@@ -80,7 +90,10 @@ public abstract class AbstractFontManager implements FontManager {
@Override
public void delayedLoad() {
Optional.ofNullable(this.fonts.get(DEFAULT_FONT)).ifPresent(font -> this.illegalChars.addAll(font.codepointsInUse()));
this.buildImageTagTrie();
this.registerImageTags();
this.registerShiftTags();
this.registerGlobalTags();
this.buildNetworkTagTrie();
this.buildEmojiKeywordsTrie();
this.emojiList = new ArrayList<>(this.emojis.values());
this.allEmojiSuggestions = this.emojis.values().stream()
@@ -88,15 +101,48 @@ public abstract class AbstractFontManager implements FontManager {
.collect(Collectors.toList());
}
private void registerGlobalTags() {
for (Map.Entry<String, String> entry : this.plugin.globalVariableManager().globalVariables().entrySet()) {
String globalTag = globalTag(entry.getKey());
this.networkTagMapper.put(globalTag, ComponentProvider.miniMessageOrConstant(entry.getValue()));
this.networkTagMapper.put("\\" + globalTag, ComponentProvider.constant(Component.text(entry.getValue())));
}
}
private void registerShiftTags() {
for (int i = -256; i <= 256; i++) {
String shiftTag = "<shift:" + i + ">";
this.networkTagMapper.put(shiftTag, ComponentProvider.constant(this.offsetFont.createOffset(i)));
this.networkTagMapper.put("\\" + shiftTag, ComponentProvider.constant(Component.text(shiftTag)));
}
}
private void registerImageTags() {
for (BitmapImage image : this.images.values()) {
String id = image.id().toString();
String simpleImageTag = imageTag(id);
this.networkTagMapper.put(simpleImageTag, ComponentProvider.constant(image.componentAt(0, 0)));
this.networkTagMapper.put("\\" + simpleImageTag, ComponentProvider.constant(Component.text(simpleImageTag)));
for (int i = 0; i < image.rows(); i++) {
for (int j = 0; j < image.columns(); j++) {
String imageArgs = id + ":" + i + ":" + j;
String imageTag = imageTag(imageArgs);
this.networkTagMapper.put(imageTag, ComponentProvider.constant(image.componentAt(i, j)));
this.networkTagMapper.put("\\" + imageTag, ComponentProvider.constant(Component.text(imageTag)));
}
}
}
}
@Override
public Map<String, Component> matchTags(String json) {
if (this.imageTagTrie == null) {
public Map<String, ComponentProvider> matchTags(String json) {
if (this.networkTagTrie == null) {
return Collections.emptyMap();
}
Map<String, Component> tags = new HashMap<>();
for (Token token : this.imageTagTrie.tokenize(json)) {
Map<String, ComponentProvider> tags = new HashMap<>();
for (Token token : this.networkTagTrie.tokenize(json)) {
if (token.isMatch()) {
tags.put(token.getFragment(), this.tagMapper.get(token.getFragment()));
tags.put(token.getFragment(), this.networkTagMapper.get(token.getFragment()));
}
}
return tags;
@@ -218,9 +264,9 @@ public abstract class AbstractFontManager implements FontManager {
public IllegalCharacterProcessResult processIllegalCharacters(String raw, char replacement) {
boolean hasIllegal = false;
// replace illegal image usage
Map<String, Component> tokens = matchTags(raw);
Map<String, ComponentProvider> tokens = matchTags(raw);
if (!tokens.isEmpty()) {
for (Map.Entry<String, Component> entry : tokens.entrySet()) {
for (Map.Entry<String, ComponentProvider> entry : tokens.entrySet()) {
raw = raw.replace(entry.getKey(), String.valueOf(replacement));
hasIllegal = true;
}
@@ -264,30 +310,10 @@ public abstract class AbstractFontManager implements FontManager {
.build();
}
private void buildImageTagTrie() {
this.tagMapper = new HashMap<>(1024);
for (BitmapImage image : this.images.values()) {
String id = image.id().toString();
String simpleImageTag = imageTag(id);
this.tagMapper.put(simpleImageTag, image.componentAt(0, 0));
this.tagMapper.put("\\" + simpleImageTag, Component.text(simpleImageTag));
for (int i = 0; i < image.rows(); i++) {
for (int j = 0; j < image.columns(); j++) {
String imageArgs = id + ":" + i + ":" + j;
String imageTag = imageTag(imageArgs);
this.tagMapper.put(imageTag, image.componentAt(i, j));
this.tagMapper.put("\\" + imageTag, Component.text(imageTag));
}
}
}
for (int i = -256; i <= 256; i++) {
String shiftTag = "<shift:" + i + ">";
this.tagMapper.put(shiftTag, this.offsetFont.createOffset(i));
this.tagMapper.put("\\" + shiftTag, Component.text(shiftTag));
}
this.imageTagTrie = Trie.builder()
private void buildNetworkTagTrie() {
this.networkTagTrie = Trie.builder()
.ignoreOverlaps()
.addKeywords(this.tagMapper.keySet())
.addKeywords(this.networkTagMapper.keySet())
.build();
}
@@ -295,6 +321,10 @@ public abstract class AbstractFontManager implements FontManager {
return "<image:" + text + ">";
}
private static String globalTag(String text) {
return "<global:" + text + ">";
}
@Override
public boolean isDefaultFontInUse() {
return !this.illegalChars.isEmpty();

View File

@@ -5,6 +5,7 @@ import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.plugin.Manageable;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.config.ConfigParser;
import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider;
import net.momirealms.craftengine.core.util.AdventureHelper;
import net.momirealms.craftengine.core.util.CharacterUtils;
import net.momirealms.craftengine.core.util.FormatUtils;
@@ -103,7 +104,7 @@ public interface FontManager extends Manageable {
return createOffsets(offset, (raw, font) -> raw);
}
Map<String, Component> matchTags(String json);
Map<String, ComponentProvider> matchTags(String json);
void refreshEmojiSuggestions(UUID uuid);
}

View File

@@ -105,7 +105,11 @@ public class AttributeModifiersModifier<I> implements SimpleNetworkItemDataModif
@Override
public Item<I> apply(Item<I> item, ItemBuildContext context) {
return item.attributeModifiers(this.modifiers.stream().map(it -> it.toAttributeModifier(context)).toList());
List<AttributeModifier> results = new ArrayList<>(this.modifiers.size());
for (PreModifier modifier : this.modifiers) {
results.add(modifier.toAttributeModifier(item, context));
}
return item.attributeModifiers(results);
}
@Override
@@ -125,12 +129,12 @@ public class AttributeModifiersModifier<I> implements SimpleNetworkItemDataModif
public record PreModifier(String type,
AttributeModifier.Slot slot,
Key id,
Optional<Key> id,
NumberProvider amount,
AttributeModifier.Operation operation,
AttributeModifiersModifier.PreModifier.@Nullable PreDisplay display) {
public PreModifier(String type, AttributeModifier.Slot slot, Key id, NumberProvider amount, AttributeModifier.Operation operation, @Nullable PreDisplay display) {
public PreModifier(String type, AttributeModifier.Slot slot, Optional<Key> id, NumberProvider amount, AttributeModifier.Operation operation, @Nullable PreDisplay display) {
this.amount = amount;
this.type = type;
this.slot = slot;
@@ -139,8 +143,9 @@ public class AttributeModifiersModifier<I> implements SimpleNetworkItemDataModif
this.display = display;
}
public AttributeModifier toAttributeModifier(ItemBuildContext context) {
return new AttributeModifier(type, slot, id, amount.getDouble(context), operation, display == null ? null : display.toDisplay(context));
public <I> AttributeModifier toAttributeModifier(Item<I> item, ItemBuildContext context) {
return new AttributeModifier(this.type, this.slot, this.id.orElseGet(() -> Key.of("craftengine", UUID.randomUUID().toString())),
this.amount.getDouble(context), this.operation, this.display == null ? null : this.display.toDisplay(context));
}
public record PreDisplay(AttributeModifier.Display.Type type, String value) {
@@ -155,15 +160,11 @@ public class AttributeModifiersModifier<I> implements SimpleNetworkItemDataModif
@Override
public ItemDataModifier<I> create(Object arg) {
MutableInt mutableInt = new MutableInt(0);
List<PreModifier> attributeModifiers = ResourceConfigUtils.parseConfigAsList(arg, (map) -> {
String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("type"), "warning.config.item.data.attribute_modifiers.missing_type");
Key nativeType = AttributeModifiersModifier.getNativeAttributeName(Key.of(type));
AttributeModifier.Slot slot = AttributeModifier.Slot.valueOf(map.getOrDefault("slot", "any").toString().toUpperCase(Locale.ENGLISH));
Key id = Optional.ofNullable(map.get("id")).map(String::valueOf).map(Key::of).orElseGet(() -> {
mutableInt.add(1);
return Key.of("craftengine", "modifier_" + mutableInt.intValue());
});
Optional<Key> id = Optional.ofNullable(map.get("id")).map(String::valueOf).map(Key::of);
NumberProvider amount = NumberProviders.fromObject(ResourceConfigUtils.requireNonNullOrThrow(map.get("amount"), "warning.config.item.data.attribute_modifiers.missing_amount"));
AttributeModifier.Operation operation = AttributeModifier.Operation.valueOf(
ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("operation"), "warning.config.item.data.attribute_modifiers.missing_operation").toUpperCase(Locale.ENGLISH)

View File

@@ -45,6 +45,7 @@ public final class ItemDataModifiers {
public static final Key UNBREAKABLE = Key.of("craftengine:unbreakable");
public static final Key DYNAMIC_LORE = Key.of("craftengine:dynamic-lore");
public static final Key OVERWRITABLE_LORE = Key.of("craftengine:overwritable-lore");
public static final Key MAX_DAMAGE = Key.of("craftengine:max-damage");
public static <T> void register(Key key, ItemDataModifierFactory<T> factory) {
((WritableRegistry<ItemDataModifierFactory<?>>) BuiltInRegistries.ITEM_DATA_MODIFIER_FACTORY)
@@ -83,6 +84,7 @@ public final class ItemDataModifiers {
register(COMPONENTS, ComponentsModifier.FACTORY);
register(REMOVE_COMPONENTS, RemoveComponentModifier.FACTORY);
register(FOOD, FoodModifier.FACTORY);
register(MAX_DAMAGE, MaxDamageModifier.FACTORY);
} else {
register(CUSTOM_NAME, CustomNameModifier.FACTORY);
register(ITEM_NAME, CustomNameModifier.FACTORY);

View File

@@ -0,0 +1,44 @@
package net.momirealms.craftengine.core.item.modifier;
import net.momirealms.craftengine.core.item.ComponentKeys;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import net.momirealms.craftengine.core.item.ItemDataModifierFactory;
import net.momirealms.craftengine.core.plugin.context.number.NumberProvider;
import net.momirealms.craftengine.core.plugin.context.number.NumberProviders;
import net.momirealms.craftengine.core.util.Key;
import org.jetbrains.annotations.Nullable;
public class MaxDamageModifier<I> implements SimpleNetworkItemDataModifier<I> {
public static final Factory<?> FACTORY = new Factory<>();
private final NumberProvider argument;
public MaxDamageModifier(NumberProvider argument) {
this.argument = argument;
}
@Override
public Key type() {
return ItemDataModifiers.MAX_DAMAGE;
}
@Override
public Item<I> apply(Item<I> item, ItemBuildContext context) {
item.maxDamage(argument.getInt(context));
return item;
}
@Override
public @Nullable Key componentType(Item<I> item, ItemBuildContext context) {
return ComponentKeys.MAX_DAMAGE;
}
public static class Factory<I> implements ItemDataModifierFactory<I> {
@Override
public ItemDataModifier<I> create(Object arg) {
NumberProvider numberProvider = NumberProviders.fromObject(arg);
return new MaxDamageModifier<>(numberProvider);
}
}
}

View File

@@ -11,6 +11,7 @@ import net.momirealms.craftengine.core.item.recipe.result.PostProcessors;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.util.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
@@ -61,9 +62,10 @@ public abstract class AbstractRecipeSerializer<T, R extends Recipe<T>> implement
return recipeCategory;
}
@NotNull
@SuppressWarnings({"unchecked"})
protected CustomRecipeResult<T> parseResult(Map<String, Object> arguments) {
Map<String, Object> resultMap = MiscUtils.castToMap(arguments.get("result"), true);
Map<String, Object> resultMap = ResourceConfigUtils.getAsMapOrNull(arguments.get("result"), "result");
if (resultMap == null) {
throw new LocalizedResourceConfigException("warning.config.recipe.missing_result");
}
@@ -81,6 +83,27 @@ public abstract class AbstractRecipeSerializer<T, R extends Recipe<T>> implement
);
}
@Nullable
@SuppressWarnings({"unchecked"})
protected CustomRecipeResult<T> parseVisualResult(Map<String, Object> arguments) {
Map<String, Object> resultMap = ResourceConfigUtils.getAsMapOrNull(arguments.get("visual-result"), "visual-result");
if (resultMap == null) {
return null;
}
String id = ResourceConfigUtils.requireNonEmptyStringOrThrow(resultMap.get("id"), "warning.config.recipe.result.missing_id");
int count = ResourceConfigUtils.getAsInt(resultMap.getOrDefault("count", 1), "count");
BuildableItem<T> resultItem = (BuildableItem<T>) CraftEngine.instance().itemManager().getBuildableItem(Key.of(id)).orElseThrow(() -> new LocalizedResourceConfigException("warning.config.recipe.invalid_result", id));
if (resultItem.isEmpty()) {
throw new LocalizedResourceConfigException("warning.config.recipe.invalid_result", id);
}
List<PostProcessor<T>> processors = ResourceConfigUtils.parseConfigAsList(resultMap.get("post-processors"), PostProcessors::fromMap);
return new CustomRecipeResult<>(
resultItem,
count,
processors.isEmpty() ? null : processors.toArray(new PostProcessor[0])
);
}
@SuppressWarnings("unchecked")
protected CustomRecipeResult<T> parseResult(DatapackRecipeResult recipeResult) {
Item<T> result = (Item<T>) CraftEngine.instance().itemManager().build(recipeResult);

View File

@@ -1,14 +1,20 @@
package net.momirealms.craftengine.core.item.recipe;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import net.momirealms.craftengine.core.item.recipe.input.RecipeInput;
import net.momirealms.craftengine.core.item.recipe.result.CustomRecipeResult;
import net.momirealms.craftengine.core.util.Key;
import org.jetbrains.annotations.Nullable;
public abstract class CustomCraftingTableRecipe<T> extends AbstractGroupedRecipe<T> {
protected final CraftingRecipeCategory category;
private final CustomRecipeResult<T> visualResult;
protected CustomCraftingTableRecipe(Key id, boolean showNotification, CustomRecipeResult<T> result, String group, CraftingRecipeCategory category) {
protected CustomCraftingTableRecipe(Key id, boolean showNotification, CustomRecipeResult<T> result, @Nullable CustomRecipeResult<T> visualResult, String group, CraftingRecipeCategory category) {
super(id, showNotification, result, group);
this.category = category == null ? CraftingRecipeCategory.MISC : category;
this.visualResult = visualResult;
}
public CraftingRecipeCategory category() {
@@ -19,4 +25,28 @@ public abstract class CustomCraftingTableRecipe<T> extends AbstractGroupedRecipe
public RecipeType type() {
return RecipeType.CRAFTING;
}
public CustomRecipeResult<T> visualResult() {
return visualResult;
}
public boolean hasVisualResult() {
return visualResult != null;
}
public T assembleVisual(RecipeInput input, ItemBuildContext context) {
if (this.visualResult != null) {
return this.visualResult.buildItemStack(context);
} else {
throw new IllegalStateException("No visual result available");
}
}
public Item<T> buildVisualOrActualResult(ItemBuildContext context) {
if (this.visualResult != null) {
return this.visualResult.buildItem(context);
} else {
return super.result.buildItem(context);
}
}
}

View File

@@ -21,10 +21,11 @@ public class CustomShapedRecipe<T> extends CustomCraftingTableRecipe<T> {
public CustomShapedRecipe(Key id,
boolean showNotification,
CustomRecipeResult<T> result,
CustomRecipeResult<T> visualResult,
String group,
CraftingRecipeCategory category,
Pattern<T> pattern) {
super(id, showNotification, result, group, category);
super(id, showNotification, result, visualResult, group, category);
this.pattern = pattern;
this.parsedPattern = pattern.parse();
}
@@ -165,7 +166,9 @@ public class CustomShapedRecipe<T> extends CustomCraftingTableRecipe<T> {
}
return new CustomShapedRecipe(id,
showNotification(arguments),
parseResult(arguments), arguments.containsKey("group") ? arguments.get("group").toString() : null, craftingRecipeCategory(arguments),
parseResult(arguments),
parseVisualResult(arguments),
arguments.containsKey("group") ? arguments.get("group").toString() : null, craftingRecipeCategory(arguments),
new Pattern<>(pattern.toArray(new String[0]), ingredients)
);
}
@@ -175,7 +178,10 @@ public class CustomShapedRecipe<T> extends CustomCraftingTableRecipe<T> {
Map<Character, Ingredient<A>> ingredients = Maps.transformValues(VANILLA_RECIPE_HELPER.shapedIngredientMap(json.getAsJsonObject("key")), this::toIngredient);
return new CustomShapedRecipe<>(id,
true,
parseResult(VANILLA_RECIPE_HELPER.craftingResult(json.getAsJsonObject("result"))), VANILLA_RECIPE_HELPER.readGroup(json), VANILLA_RECIPE_HELPER.craftingCategory(json),
parseResult(VANILLA_RECIPE_HELPER.craftingResult(json.getAsJsonObject("result"))),
null,
VANILLA_RECIPE_HELPER.readGroup(json),
VANILLA_RECIPE_HELPER.craftingCategory(json),
new Pattern<>(VANILLA_RECIPE_HELPER.craftingShapedPattern(json), ingredients)
);
}

View File

@@ -20,10 +20,11 @@ public class CustomShapelessRecipe<T> extends CustomCraftingTableRecipe<T> {
public CustomShapelessRecipe(Key id,
boolean showNotification,
CustomRecipeResult<T> result,
CustomRecipeResult<T> visualResult,
String group,
CraftingRecipeCategory category,
List<Ingredient<T>> ingredients) {
super(id, showNotification, result, group, category);
super(id, showNotification, result, visualResult, group, category);
this.ingredients = ingredients;
this.placementInfo = PlacementInfo.create(ingredients);
}
@@ -84,7 +85,9 @@ public class CustomShapelessRecipe<T> extends CustomCraftingTableRecipe<T> {
}
return new CustomShapelessRecipe(id,
showNotification(arguments),
parseResult(arguments), arguments.containsKey("group") ? arguments.get("group").toString() : null, craftingRecipeCategory(arguments),
parseResult(arguments),
parseVisualResult(arguments),
arguments.containsKey("group") ? arguments.get("group").toString() : null, craftingRecipeCategory(arguments),
ingredients
);
}
@@ -93,7 +96,9 @@ public class CustomShapelessRecipe<T> extends CustomCraftingTableRecipe<T> {
public CustomShapelessRecipe<A> readJson(Key id, JsonObject json) {
return new CustomShapelessRecipe<>(id,
true,
parseResult(VANILLA_RECIPE_HELPER.craftingResult(json.getAsJsonObject("result"))), VANILLA_RECIPE_HELPER.readGroup(json), VANILLA_RECIPE_HELPER.craftingCategory(json),
parseResult(VANILLA_RECIPE_HELPER.craftingResult(json.getAsJsonObject("result"))),
null,
VANILLA_RECIPE_HELPER.readGroup(json), VANILLA_RECIPE_HELPER.craftingCategory(json),
VANILLA_RECIPE_HELPER.shapelessIngredients(json.getAsJsonArray("ingredients")).stream().map(this::toIngredient).toList()
);
}

View File

@@ -214,7 +214,7 @@ public class CustomSmithingTransformRecipe<T> extends AbstractedFixedResultRecip
for (Key component : this.components) {
Object componentObj = item1.getExactComponent(component);
if (componentObj != null) {
item3.setComponent(component, componentObj);
item3.setExactComponent(component, componentObj);
}
}
}

View File

@@ -19,7 +19,7 @@ public class UniqueIdItem<T> {
@NotNull
public UniqueKey id() {
return uniqueId;
return this.uniqueId;
}
@NotNull
@@ -37,6 +37,6 @@ public class UniqueIdItem<T> {
@Override
public String toString() {
return "UniqueIdItem[" + "uniqueId=" + uniqueId + ", item=" + rawItem.getItem() + ']';
return "UniqueIdItem[" + "uniqueId=" + this.uniqueId + ", item=" + this.rawItem.getItem() + ']';
}
}

View File

@@ -42,6 +42,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.scanner.ScannerException;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
@@ -125,6 +126,7 @@ public abstract class AbstractPackManager implements PackManager {
loadInternalList("textures", "", VANILLA_TEXTURES::add);
VANILLA_MODELS.add(Key.of("minecraft", "builtin/entity"));
VANILLA_MODELS.add(Key.of("minecraft", "item/player_head"));
for (int i = 0; i < 256; i++) {
VANILLA_TEXTURES.add(Key.of("minecraft", "font/unicode_page_" + String.format("%02x", i)));
}
@@ -542,6 +544,19 @@ public abstract class AbstractPackManager implements PackManager {
} catch (IOException e) {
AbstractPackManager.this.plugin.logger().severe("Error while reading config file: " + path, e);
return FileVisitResult.CONTINUE;
} catch (ScannerException e) {
if (e.getMessage() != null && e.getMessage().contains("TAB") && e.getMessage().contains("indentation")) {
try {
String content = Files.readString(path);
content = content.replace("\t", " ");
Files.writeString(path, content);
} catch (Exception ex) {
AbstractPackManager.this.plugin.logger().severe("Failed to fix tab indentation in config file: " + path, ex);
}
} else {
AbstractPackManager.this.plugin.logger().severe("Error found while reading config file: " + path, e);
}
return FileVisitResult.CONTINUE;
} catch (LocalizedException e) {
e.setArgument(0, path.toString());
TranslationManager.instance().log(e.node(), e.arguments());

View File

@@ -5,4 +5,8 @@ public class ResourcePackGenerationException extends RuntimeException {
public ResourcePackGenerationException(String message) {
super(message);
}
public ResourcePackGenerationException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -51,7 +51,8 @@ public abstract class CraftEngine implements Plugin {
protected PluginLogger logger;
protected Config config;
protected Platform platform;
protected ClassPathAppender classPathAppender;
protected ClassPathAppender sharedClassPathAppender;
protected ClassPathAppender privateClassPathAppender;
protected DependencyManager dependencyManager;
protected SchedulerAdapter<?> scheduler;
protected NetworkManager networkManager;
@@ -338,8 +339,13 @@ public abstract class CraftEngine implements Plugin {
}
@Override
public ClassPathAppender classPathAppender() {
return classPathAppender;
public ClassPathAppender sharedClassPathAppender() {
return sharedClassPathAppender;
}
@Override
public ClassPathAppender privateClassPathAppender() {
return privateClassPathAppender;
}
@Override

View File

@@ -35,7 +35,9 @@ public interface Plugin {
PluginLogger logger();
ClassPathAppender classPathAppender();
ClassPathAppender sharedClassPathAppender();
ClassPathAppender privateClassPathAppender();
File dataFolderFile();

View File

@@ -96,11 +96,8 @@ public class Config {
protected Path resource_pack$delivery$file_to_upload;
protected Component resource_pack$send$prompt;
protected int performance$max_note_block_chain_update_limit;
protected int performance$max_tripwire_chain_update_limit;
protected int performance$max_emojis_per_parse;
protected boolean light_system$force_update_light;
protected boolean light_system$async_update;
protected boolean light_system$enable;
protected int chunk_system$compression_method;
@@ -110,9 +107,11 @@ public class Config {
protected boolean chunk_system$cache_system;
protected boolean chunk_system$injection$use_fast_method;
protected boolean chunk_system$injection$target;
protected boolean chunk_system$process_invalid_furniture$enable;
protected Map<String, String> chunk_system$process_invalid_furniture$mapping;
protected boolean chunk_system$process_invalid_blocks$enable;
protected Map<String, String> chunk_system$process_invalid_blocks$mapping;
protected boolean furniture$handle_invalid_furniture_on_chunk_load$enable;
protected Map<String, String> furniture$handle_invalid_furniture_on_chunk_load$mapping;
protected boolean furniture$hide_base_entity;
protected ColliderType furniture$collision_entity_type;
@@ -122,6 +121,7 @@ public class Config {
protected boolean block$predict_breaking;
protected int block$predict_breaking_interval;
protected double block$extended_interaction_range;
protected boolean block$chunk_relighter;
protected boolean recipe$enable;
protected boolean recipe$disable_vanilla_recipes$all;
@@ -161,10 +161,11 @@ public class Config {
protected Key equipment$sacrificed_vanilla_armor$humanoid;
protected Key equipment$sacrificed_vanilla_armor$humanoid_leggings;
protected boolean emoji$chat;
protected boolean emoji$book;
protected boolean emoji$anvil;
protected boolean emoji$sign;
protected boolean emoji$contexts$chat;
protected boolean emoji$contexts$book;
protected boolean emoji$contexts$anvil;
protected boolean emoji$contexts$sign;
protected int emoji$max_emojis_per_parse;
public Config(CraftEngine plugin) {
this.plugin = plugin;
@@ -225,6 +226,8 @@ public class Config {
.builder()
.setVersioning(new BasicVersioning("config-version"))
.addIgnoredRoute(PluginProperties.getValue("config"), "resource-pack.delivery.hosting", '.')
.addIgnoredRoute(PluginProperties.getValue("config"), "chunk-system.process-invalid-blocks.convert", '.')
.addIgnoredRoute(PluginProperties.getValue("config"), "chunk-system.process-invalid-furniture.convert", '.')
.build());
}
try {
@@ -285,7 +288,7 @@ public class Config {
resource_pack$protection$obfuscation$resource_location$random_path$source = config.getString("resource-pack.protection.obfuscation.resource-location.random-path.source", "obf");
resource_pack$protection$obfuscation$resource_location$random_path$anti_unzip = config.getBoolean("resource-pack.protection.obfuscation.resource-location.random-path.anti-unzip", false);
resource_pack$protection$obfuscation$resource_location$random_atlas$images_per_canvas = config.getInt("resource-pack.protection.obfuscation.resource-location.random-atlas.images-per-canvas", 16);
resource_pack$protection$obfuscation$resource_location$random_atlas$use_double = config.getBoolean("resource-pack.protection.obfuscation.resource-location.random-atlas.use-double", true);
resource_pack$protection$obfuscation$resource_location$random_atlas$use_double = config.getBoolean("resource-pack.protection.obfuscation.resource-location.random-atlas.use-double", false);
resource_pack$protection$obfuscation$resource_location$bypass_textures = config.getStringList("resource-pack.protection.obfuscation.resource-location.bypass-textures");
resource_pack$protection$obfuscation$resource_location$bypass_models = config.getStringList("resource-pack.protection.obfuscation.resource-location.bypass-models");
resource_pack$protection$obfuscation$resource_location$bypass_sounds = config.getStringList("resource-pack.protection.obfuscation.resource-location.bypass-sounds");
@@ -310,13 +313,9 @@ public class Config {
resource_pack$duplicated_files_handler = List.of();
}
// performance
performance$max_note_block_chain_update_limit = config.getInt("performance.max-note-block-chain-update-limit", 64);
performance$max_tripwire_chain_update_limit = config.getInt("performance.max-tripwire-chain-update-limit", 128);
performance$max_emojis_per_parse = config.getInt("performance.max-emojis-per-parse", 32);
// light
light_system$force_update_light = config.getBoolean("light-system.force-update-light", false);
light_system$async_update = config.getBoolean("light-system.async-update", true);
light_system$enable = config.getBoolean("light-system.enable", true);
// chunk
@@ -330,22 +329,37 @@ public class Config {
chunk_system$injection$target = config.getEnum("chunk-system.injection.target", InjectionTarget.class, InjectionTarget.PALETTE) == InjectionTarget.PALETTE;
}
// furniture
furniture$handle_invalid_furniture_on_chunk_load$enable = config.getBoolean("furniture.handle-invalid-furniture-on-chunk-load.enable", false);
ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
for (String furniture : config.getStringList("furniture.handle-invalid-furniture-on-chunk-load.remove")) {
builder.put(furniture, "");
chunk_system$process_invalid_furniture$enable = config.getBoolean("chunk-system.process-invalid-furniture.enable", false);
ImmutableMap.Builder<String, String> furnitureBuilder = ImmutableMap.builder();
for (String furniture : config.getStringList("chunk-system.process-invalid-furniture.remove")) {
furnitureBuilder.put(furniture, "");
}
if (config.contains("furniture.handle-invalid-furniture-on-chunk-load.convert")) {
Section section = config.getSection("furniture.handle-invalid-furniture-on-chunk-load.convert");
if (config.contains("chunk-system.process-invalid-furniture.convert")) {
Section section = config.getSection("chunk-system.process-invalid-furniture.convert");
if (section != null) {
for (Map.Entry<String, Object> entry : section.getStringRouteMappedValues(false).entrySet()) {
builder.put(entry.getKey(), entry.getValue().toString());
furnitureBuilder.put(entry.getKey(), entry.getValue().toString());
}
}
}
chunk_system$process_invalid_furniture$mapping = furnitureBuilder.build();
furniture$handle_invalid_furniture_on_chunk_load$mapping = builder.build();
chunk_system$process_invalid_blocks$enable = config.getBoolean("chunk-system.process-invalid-blocks.enable", false);
ImmutableMap.Builder<String, String> blockBuilder = ImmutableMap.builder();
for (String furniture : config.getStringList("chunk-system.process-invalid-blocks.remove")) {
blockBuilder.put(furniture, "");
}
if (config.contains("chunk-system.process-invalid-blocks.convert")) {
Section section = config.getSection("chunk-system.process-invalid-blocks.convert");
if (section != null) {
for (Map.Entry<String, Object> entry : section.getStringRouteMappedValues(false).entrySet()) {
blockBuilder.put(entry.getKey(), entry.getValue().toString());
}
}
}
chunk_system$process_invalid_blocks$mapping = blockBuilder.build();
// furniture
furniture$hide_base_entity = config.getBoolean("furniture.hide-base-entity", true);
furniture$collision_entity_type = ColliderType.valueOf(config.getString("furniture.collision-entity-type", "interaction").toUpperCase(Locale.ENGLISH));
@@ -375,6 +389,7 @@ public class Config {
block$predict_breaking = config.getBoolean("block.predict-breaking.enable", true);
block$predict_breaking_interval = Math.max(config.getInt("block.predict-breaking.interval", 10), 1);
block$extended_interaction_range = Math.max(config.getDouble("block.predict-breaking.extended-interaction-range", 0.5), 0.0);
block$chunk_relighter = config.getBoolean("block.chunk-relighter", true);
// recipe
recipe$enable = config.getBoolean("recipe.enable", true);
@@ -405,10 +420,11 @@ public class Config {
image$intercept_packets$advancement = config.getBoolean("image.intercept-packets.advancement", true);
// emoji
emoji$chat = config.getBoolean("emoji.chat", true);
emoji$anvil = config.getBoolean("emoji.anvil", true);
emoji$book = config.getBoolean("emoji.book", true);
emoji$sign = config.getBoolean("emoji.sign", true);
emoji$contexts$chat = config.getBoolean("emoji.contexts.chat", true);
emoji$contexts$anvil = config.getBoolean("emoji.contexts.anvil", true);
emoji$contexts$book = config.getBoolean("emoji.contexts.book", true);
emoji$contexts$sign = config.getBoolean("emoji.contexts.sign", true);
emoji$max_emojis_per_parse = config.getInt("emoji.max-emojis-per-parse", 32);
firstTime = false;
}
@@ -439,6 +455,10 @@ public class Config {
return instance.debug$item;
}
public static boolean debugBlockEntity() {
return false;
}
public static boolean debugFurniture() {
return instance.debug$furniture;
}
@@ -464,24 +484,28 @@ public class Config {
}
public static int maxNoteBlockChainUpdate() {
return instance.performance$max_note_block_chain_update_limit;
return 64;
}
public static int maxEmojisPerParse() {
return instance.performance$max_emojis_per_parse;
return instance.emoji$max_emojis_per_parse;
}
public static boolean handleInvalidFurniture() {
return instance.furniture$handle_invalid_furniture_on_chunk_load$enable;
return instance.chunk_system$process_invalid_furniture$enable;
}
public static boolean handleInvalidBlock() {
return instance.chunk_system$process_invalid_blocks$enable;
}
public static Map<String, String> furnitureMappings() {
return instance.furniture$handle_invalid_furniture_on_chunk_load$mapping;
return instance.chunk_system$process_invalid_furniture$mapping;
}
// public static boolean forceUpdateLight() {
// return instance.light_system$force_update_light;
// }
public static Map<String, String> blockMappings() {
return instance.chunk_system$process_invalid_blocks$mapping;
}
public static boolean enableLightSystem() {
return instance.light_system$enable;
@@ -772,19 +796,19 @@ public class Config {
}
public static boolean allowEmojiSign() {
return instance.emoji$sign;
return instance.emoji$contexts$sign;
}
public static boolean allowEmojiChat() {
return instance.emoji$chat;
return instance.emoji$contexts$chat;
}
public static boolean allowEmojiAnvil() {
return instance.emoji$anvil;
return instance.emoji$contexts$anvil;
}
public static boolean allowEmojiBook() {
return instance.emoji$book;
return instance.emoji$contexts$book;
}
public static ColliderType colliderType() {
@@ -859,6 +883,14 @@ public class Config {
return instance.item$update_triggers$drop;
}
public static boolean enableChunkRelighter() {
return instance.block$chunk_relighter;
}
public static boolean asyncLightUpdate() {
return instance.light_system$async_update;
}
public void setObf(boolean enable) {
this.resource_pack$protection$obfuscation$enable = enable;
}

View File

@@ -8,6 +8,7 @@ import net.momirealms.craftengine.core.plugin.locale.LocalizedException;
import org.jetbrains.annotations.Nullable;
import java.nio.file.Path;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -25,6 +26,10 @@ public class GlobalVariableManager implements Manageable {
this.globalVariables.clear();
}
public Map<String, String> globalVariables() {
return Collections.unmodifiableMap(this.globalVariables);
}
public ConfigParser parser() {
return this.parser;
}

View File

@@ -0,0 +1,34 @@
package net.momirealms.craftengine.core.plugin.context;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.plugin.text.minimessage.*;
import java.util.Map;
public final class NetworkTextReplaceContext extends AbstractChainParameterContext {
private final Player player;
public NetworkTextReplaceContext(Player player) {
super(new ContextHolder(Map.of(DirectContextParameters.PLAYER, () -> player)));
this.player = player;
}
public static NetworkTextReplaceContext of(Player player) {
return new NetworkTextReplaceContext(player);
}
public Player player() {
return player;
}
@Override
public TagResolver[] tagResolvers() {
if (this.tagResolvers == null) {
this.tagResolvers = new TagResolver[]{ShiftTag.INSTANCE, ImageTag.INSTANCE, new PlaceholderTag(this), new I18NTag(this),
new NamedArgumentTag(this), new ExpressionTag(this), new GlobalVariableTag(this)};
}
return this.tagResolvers;
}
}

View File

@@ -0,0 +1,23 @@
package net.momirealms.craftengine.core.plugin.context.condition;
import net.momirealms.craftengine.core.plugin.context.Condition;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.util.Key;
import java.util.Map;
public class AlwaysFalseCondition<CTX extends Context> implements Condition<CTX> {
@Override
public Key type() {
return CommonConditions.ALWAYS_FALSE;
}
public static class FactoryImpl<CTX extends Context> implements ConditionFactory<CTX> {
@Override
public Condition<CTX> create(Map<String, Object> arguments) {
return new AlwaysFalseCondition<>();
}
}
}

View File

@@ -6,11 +6,11 @@ import net.momirealms.craftengine.core.util.Key;
import java.util.Map;
public class EmptyCondition<CTX extends Context> implements Condition<CTX> {
public class AlwaysTrueCondition<CTX extends Context> implements Condition<CTX> {
@Override
public Key type() {
return CommonConditions.EMPTY;
return CommonConditions.ALWAYS_TRUE;
}
@Override
@@ -22,7 +22,7 @@ public class EmptyCondition<CTX extends Context> implements Condition<CTX> {
@Override
public Condition<CTX> create(Map<String, Object> arguments) {
return new EmptyCondition<>();
return new AlwaysTrueCondition<>();
}
}
}

View File

@@ -5,11 +5,14 @@ import net.momirealms.craftengine.core.util.Key;
public final class CommonConditions {
private CommonConditions() {}
public static final Key EMPTY = Key.of("craftengine:empty");
public static final Key ALWAYS_TRUE = Key.of("craftengine:always_true");
public static final Key ALWAYS_FALSE = Key.of("craftengine:always_false");
public static final Key ALL_OF = Key.of("craftengine:all_of");
public static final Key ANY_OF = Key.of("craftengine:any_of");
public static final Key INVERTED = Key.of("craftengine:inverted");
public static final Key MATCH_ITEM = Key.of("craftengine:match_item");
public static final Key MATCH_ENTITY = Key.of("craftengine:match_entity");
public static final Key MATCH_BLOCK = Key.of("craftengine:match_block");
public static final Key MATCH_BLOCK_PROPERTY = Key.from("craftengine:match_block_property");
public static final Key TABLE_BONUS = Key.from("craftengine:table_bonus");
public static final Key SURVIVES_EXPLOSION = Key.from("craftengine:survives_explosion");

View File

@@ -27,7 +27,7 @@ public class ExpressionCondition<CTX extends Context> implements Condition<CTX>
@Override
public boolean test(CTX ctx) {
String exp = expression.get(ctx);
String exp = this.expression.get(ctx);
Expression expr = new Expression(exp);
try {
return expr.evaluate().getBooleanValue();

View File

@@ -0,0 +1,46 @@
package net.momirealms.craftengine.core.plugin.context.condition;
import net.momirealms.craftengine.core.plugin.context.Condition;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.ExistingBlock;
import java.util.*;
public class MatchBlockCondition<CTX extends Context> implements Condition<CTX> {
private final Set<String> ids;
private final boolean regexMatch;
public MatchBlockCondition(Collection<String> ids, boolean regexMatch) {
this.ids = new HashSet<>(ids);
this.regexMatch = regexMatch;
}
@Override
public Key type() {
return CommonConditions.MATCH_BLOCK;
}
@Override
public boolean test(CTX ctx) {
Optional<ExistingBlock> block = ctx.getOptionalParameter(DirectContextParameters.BLOCK);
return block.filter(blockInWorld -> MiscUtils.matchRegex(blockInWorld.id().asString(), this.ids, this.regexMatch)).isPresent();
}
public static class FactoryImpl<CTX extends Context> implements ConditionFactory<CTX> {
@Override
public Condition<CTX> create(Map<String, Object> arguments) {
List<String> ids = MiscUtils.getAsStringList(arguments.get("id"));
if (ids.isEmpty()) {
throw new LocalizedResourceConfigException("warning.config.condition.match_block.missing_id");
}
boolean regex = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("regex", false), "regex");
return new MatchBlockCondition<>(ids, regex);
}
}
}

View File

@@ -1,7 +1,9 @@
package net.momirealms.craftengine.core.plugin.context.condition;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.block.state.StatePropertyAccessor;
import net.momirealms.craftengine.core.plugin.context.Condition;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
@@ -9,11 +11,9 @@ import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.Pair;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.ExistingBlock;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.*;
public class MatchBlockPropertyCondition<CTX extends Context> implements Condition<CTX> {
private final List<Pair<String, String>> properties;
@@ -29,19 +29,49 @@ public class MatchBlockPropertyCondition<CTX extends Context> implements Conditi
@Override
public boolean test(CTX ctx) {
return ctx.getOptionalParameter(DirectContextParameters.CUSTOM_BLOCK_STATE).map(state -> {
CustomBlock block = state.owner().value();
ImmutableBlockState customBlockState = null;
StatePropertyAccessor vanillaStatePropertyAccessor = null;
// 优先使用自定义状态,其主要应用于自定义方块掉落物
Optional<ImmutableBlockState> optionalCustomState = ctx.getOptionalParameter(DirectContextParameters.CUSTOM_BLOCK_STATE);
if (optionalCustomState.isPresent()) {
customBlockState = optionalCustomState.get();
} else {
// 其次再判断block这个过程会更慢因为每次获取都是全新的方块状态适用于物品等事件
Optional<ExistingBlock> optionalExistingBlock = ctx.getOptionalParameter(DirectContextParameters.BLOCK);
if (optionalExistingBlock.isPresent()) {
ExistingBlock existingBlock = optionalExistingBlock.get();
customBlockState = existingBlock.customBlockState();
if (customBlockState == null) {
vanillaStatePropertyAccessor = existingBlock.createStatePropertyAccessor();
}
} else {
// 都没有则条件不过
return false;
}
}
if (customBlockState != null) {
CustomBlock block = customBlockState.owner().value();
for (Pair<String, String> property : this.properties) {
Property<?> propertyIns = block.getProperty(property.left());
if (propertyIns == null) {
return false;
}
if (!state.get(propertyIns).toString().toLowerCase(Locale.ENGLISH).equals(property.right())) {
if (!customBlockState.get(propertyIns).toString().toLowerCase(Locale.ENGLISH).equals(property.right())) {
return false;
}
}
return true;
}).orElse(false);
} else {
for (Pair<String, String> property : this.properties) {
String value = vanillaStatePropertyAccessor.getPropertyValueAsString(property.left());
if (value == null) {
return false;
}
if (!value.equals(property.right())) {
return false;
}
}
}
return true;
}
public static class FactoryImpl<CTX extends Context> implements ConditionFactory<CTX> {

View File

@@ -0,0 +1,46 @@
package net.momirealms.craftengine.core.plugin.context.condition;
import net.momirealms.craftengine.core.entity.Entity;
import net.momirealms.craftengine.core.plugin.context.Condition;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import java.util.*;
public class MatchEntityCondition<CTX extends Context> implements Condition<CTX> {
private final Set<String> ids;
private final boolean regexMatch;
public MatchEntityCondition(Collection<String> ids, boolean regexMatch) {
this.ids = new HashSet<>(ids);
this.regexMatch = regexMatch;
}
@Override
public Key type() {
return CommonConditions.MATCH_ENTITY;
}
@Override
public boolean test(CTX ctx) {
Optional<Entity> entity = ctx.getOptionalParameter(DirectContextParameters.ENTITY);
return entity.filter(value -> MiscUtils.matchRegex(value.type().asString(), this.ids, this.regexMatch)).isPresent();
}
public static class FactoryImpl<CTX extends Context> implements ConditionFactory<CTX> {
@Override
public Condition<CTX> create(Map<String, Object> arguments) {
List<String> ids = MiscUtils.getAsStringList(arguments.get("id"));
if (ids.isEmpty()) {
throw new LocalizedResourceConfigException("warning.config.condition.match_entity.missing_id");
}
boolean regex = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("regex", false), "regex");
return new MatchEntityCondition<>(ids, regex);
}
}
}

View File

@@ -28,19 +28,7 @@ public class MatchItemCondition<CTX extends Context> implements Condition<CTX> {
@Override
public boolean test(CTX ctx) {
Optional<Item<?>> item = ctx.getOptionalParameter(DirectContextParameters.ITEM_IN_HAND);
if (item.isEmpty()) return false;
Key key = item.get().id();
String itemId = key.toString();
if (this.regexMatch) {
for (String regex : ids) {
if (itemId.matches(regex)) {
return true;
}
}
} else {
return this.ids.contains(itemId);
}
return false;
return item.filter(value -> MiscUtils.matchRegex(value.id().asString(), this.ids, this.regexMatch)).isPresent();
}
public static class FactoryImpl<CTX extends Context> implements ConditionFactory<CTX> {

View File

@@ -17,6 +17,8 @@ public class EventConditions {
static {
register(CommonConditions.MATCH_ITEM, new MatchItemCondition.FactoryImpl<>());
register(CommonConditions.MATCH_ENTITY, new MatchEntityCondition.FactoryImpl<>());
register(CommonConditions.MATCH_BLOCK, new MatchBlockCondition.FactoryImpl<>());
register(CommonConditions.MATCH_BLOCK_PROPERTY, new MatchBlockPropertyCondition.FactoryImpl<>());
register(CommonConditions.TABLE_BONUS, new TableBonusCondition.FactoryImpl<>());
register(CommonConditions.SURVIVES_EXPLOSION, new SurvivesExplosionCondition.FactoryImpl<>());

View File

@@ -16,34 +16,37 @@ import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
public class CommandFunction<CTX extends Context> extends AbstractConditionalFunction<CTX> {
private final List<TextProvider> command;
private final boolean asPlayer;
private final PlayerSelector<CTX> selector;
private final boolean asPlayer;
private final boolean asEvent;
private final boolean asOp;
public CommandFunction(List<Condition<CTX>> predicates, @Nullable PlayerSelector<CTX> selector, List<TextProvider> command, boolean asPlayer) {
public CommandFunction(List<Condition<CTX>> predicates, @Nullable PlayerSelector<CTX> selector, List<TextProvider> command,
boolean asPlayer, boolean asEvent, boolean asOp) {
super(predicates);
this.asPlayer = asPlayer;
this.command = command;
this.selector = selector;
this.asPlayer = asPlayer;
this.asEvent = asEvent;
this.asOp = asOp;
}
@Override
public void runInternal(CTX ctx) {
if (this.asPlayer) {
if (this.asPlayer || this.asOp) {
if (this.selector == null) {
ctx.getOptionalParameter(DirectContextParameters.PLAYER).ifPresent(it -> {
for (TextProvider c : this.command) {
it.performCommand(c.get(ctx));
}
});
ctx.getOptionalParameter(DirectContextParameters.PLAYER)
.ifPresent(player -> executeCommands(
ctx, this.asEvent ? player::performCommandAsEvent : command1 -> player.performCommand(command1, this.asOp)
));
} else {
for (Player viewer : this.selector.get(ctx)) {
RelationalContext relationalContext = ViewerContext.of(ctx, PlayerOptionalContext.of(viewer, ContextHolder.EMPTY));
for (TextProvider c : this.command) {
viewer.performCommand(c.get(relationalContext));
}
executeCommands(relationalContext, this.asEvent ? viewer::performCommandAsEvent : command1 -> viewer.performCommand(command1, this.asOp));
}
}
} else {
@@ -54,6 +57,12 @@ public class CommandFunction<CTX extends Context> extends AbstractConditionalFun
}
}
private void executeCommands(Context ctx, Consumer<String> executor) {
for (TextProvider c : this.command) {
executor.accept(c.get(ctx));
}
}
@Override
public Key type() {
return CommonFunctions.COMMAND;
@@ -70,7 +79,9 @@ public class CommandFunction<CTX extends Context> extends AbstractConditionalFun
Object command = ResourceConfigUtils.requireNonNullOrThrow(ResourceConfigUtils.get(arguments, "command", "commands"), "warning.config.function.command.missing_command");
List<TextProvider> commands = MiscUtils.getAsStringList(command).stream().map(TextProviders::fromString).toList();
boolean asPlayer = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("as-player", false), "as-player");
return new CommandFunction<>(getPredicates(arguments), PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), commands, asPlayer);
boolean asEvent = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("as-event", false), "as-event");
boolean asOp = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("as-op", false), "as-op");
return new CommandFunction<>(getPredicates(arguments), PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), commands, asPlayer, asEvent, asOp);
}
}
}

View File

@@ -33,7 +33,7 @@ public class ParticleFunction<CTX extends Context> extends AbstractConditionalFu
final String blockState = ResourceConfigUtils.requireNonEmptyStringOrThrow(map.get("block-state"), "warning.config.function.particle.missing_block_state");
@Override
public BlockStateWrapper get() {
return CraftEngine.instance().blockManager().createPackedBlockState(this.blockState);
return CraftEngine.instance().blockManager().createBlockState(this.blockState);
}
})),
ParticleTypes.BLOCK, ParticleTypes.FALLING_DUST, ParticleTypes.DUST_PILLAR, ParticleTypes.BLOCK_CRUMBLE, ParticleTypes.BLOCK_MARKER);

View File

@@ -62,7 +62,7 @@ public class PlaceBlockFunction<CTX extends Context> extends AbstractConditional
NumberProvider y = NumberProviders.fromObject(arguments.getOrDefault("y", "<arg:position.y>"));
NumberProvider z = NumberProviders.fromObject(arguments.getOrDefault("z", "<arg:position.z>"));
NumberProvider flags = Optional.ofNullable(arguments.get("update-flags")).map(NumberProviders::fromObject).orElse(NumberProviders.direct(UpdateOption.UPDATE_ALL.flags()));
return new PlaceBlockFunction<>(LazyReference.lazyReference(() -> CraftEngine.instance().blockManager().createPackedBlockState(state)), x, y, z, flags, getPredicates(arguments));
return new PlaceBlockFunction<>(LazyReference.lazyReference(() -> CraftEngine.instance().blockManager().createBlockState(state)), x, y, z, flags, getPredicates(arguments));
}
}
}

View File

@@ -0,0 +1,104 @@
package net.momirealms.craftengine.core.plugin.context.number;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MCUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
public class GaussianNumberProvider implements NumberProvider {
public static final FactoryImpl FACTORY = new FactoryImpl();
private final double min;
private final double max;
private final double mean;
private final double stdDev;
private final int maxAttempts;
public GaussianNumberProvider(double min, double max, double mean, double stdDev, int maxAttempts) {
this.min = min;
this.max = max;
this.mean = mean;
this.stdDev = stdDev;
this.maxAttempts = maxAttempts;
validateParameters();
}
private void validateParameters() {
if (this.min >= this.max) {
throw new IllegalArgumentException("min must be less than max");
}
if (this.stdDev <= 0) {
throw new IllegalArgumentException("std-dev must be greater than 0");
}
if (this.maxAttempts <= 0) {
throw new IllegalArgumentException("max-attempts must be greater than 0");
}
}
@Override
public float getFloat(Context context) {
return (float) getDouble(context);
}
@Override
public double getDouble(Context context) {
Random random = ThreadLocalRandom.current();
int attempts = 0;
while (attempts < maxAttempts) {
double value = random.nextGaussian() * stdDev + mean;
if (value >= min && value <= max) {
return value;
}
attempts++;
}
return MCUtils.clamp(this.mean, this.min, this.max);
}
@Override
public Key type() {
return NumberProviders.GAUSSIAN;
}
public double min() {
return min;
}
public double max() {
return max;
}
public int maxAttempts() {
return maxAttempts;
}
public double mean() {
return mean;
}
public double stdDev() {
return stdDev;
}
public static class FactoryImpl implements NumberProviderFactory {
@Override
public NumberProvider create(Map<String, Object> arguments) {
double min = ResourceConfigUtils.getAsDouble(ResourceConfigUtils.requireNonNullOrThrow(arguments.get("min"), "warning.config.number.gaussian.missing_min"), "min");
double max = ResourceConfigUtils.getAsDouble(ResourceConfigUtils.requireNonNullOrThrow(arguments.get("max"), "warning.config.number.gaussian.missing_max"), "max");
double mean = ResourceConfigUtils.getAsDouble(arguments.getOrDefault("mean", (min + max) / 2.0), "mean");
double stdDev = ResourceConfigUtils.getAsDouble(arguments.getOrDefault("std-dev", (max - min) / 6.0), "std-dev");
int maxAttempts = ResourceConfigUtils.getAsInt(arguments.getOrDefault("max-attempts", 128), "max-attempts");
return new GaussianNumberProvider(min, max, mean, stdDev, maxAttempts);
}
}
@Override
public String toString() {
return String.format("GaussianNumberProvider{min=%.2f, max=%.2f, mean=%.2f, stdDev=%.2f, maxAttempts=%d}",
min, max, mean, stdDev, maxAttempts);
}
}

View File

@@ -17,11 +17,13 @@ public class NumberProviders {
public static final Key CONSTANT = Key.of("craftengine:constant");
public static final Key UNIFORM = Key.of("craftengine:uniform");
public static final Key EXPRESSION = Key.of("craftengine:expression");
public static final Key GAUSSIAN = Key.of("craftengine:gaussian");
static {
register(FIXED, FixedNumberProvider.FACTORY);
register(CONSTANT, FixedNumberProvider.FACTORY);
register(UNIFORM, UniformNumberProvider.FACTORY);
register(GAUSSIAN, GaussianNumberProvider.FACTORY);
register(EXPRESSION, ExpressionNumberProvider.FACTORY);
}

View File

@@ -2,31 +2,31 @@ package net.momirealms.craftengine.core.plugin.context.parameter;
import net.momirealms.craftengine.core.plugin.context.ChainParameterProvider;
import net.momirealms.craftengine.core.plugin.context.ContextKey;
import net.momirealms.craftengine.core.world.BlockInWorld;
import net.momirealms.craftengine.core.world.ExistingBlock;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
public class BlockParameterProvider implements ChainParameterProvider<BlockInWorld> {
private static final Map<ContextKey<?>, Function<BlockInWorld, Object>> CONTEXT_FUNCTIONS = new HashMap<>();
public class BlockParameterProvider implements ChainParameterProvider<ExistingBlock> {
private static final Map<ContextKey<?>, Function<ExistingBlock, Object>> CONTEXT_FUNCTIONS = new HashMap<>();
static {
CONTEXT_FUNCTIONS.put(DirectContextParameters.X, BlockInWorld::x);
CONTEXT_FUNCTIONS.put(DirectContextParameters.Y, BlockInWorld::y);
CONTEXT_FUNCTIONS.put(DirectContextParameters.Z, BlockInWorld::z);
CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_X, BlockInWorld::x);
CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, BlockInWorld::y);
CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, BlockInWorld::z);
CONTEXT_FUNCTIONS.put(DirectContextParameters.CUSTOM_BLOCK, BlockInWorld::customBlock);
CONTEXT_FUNCTIONS.put(DirectContextParameters.CUSTOM_BLOCK_STATE, BlockInWorld::customBlockState);
CONTEXT_FUNCTIONS.put(DirectContextParameters.WORLD, BlockInWorld::world);
CONTEXT_FUNCTIONS.put(DirectContextParameters.POSITION, BlockInWorld::position);
CONTEXT_FUNCTIONS.put(DirectContextParameters.X, ExistingBlock::x);
CONTEXT_FUNCTIONS.put(DirectContextParameters.Y, ExistingBlock::y);
CONTEXT_FUNCTIONS.put(DirectContextParameters.Z, ExistingBlock::z);
CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_X, ExistingBlock::x);
CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Y, ExistingBlock::y);
CONTEXT_FUNCTIONS.put(DirectContextParameters.BLOCK_Z, ExistingBlock::z);
CONTEXT_FUNCTIONS.put(DirectContextParameters.CUSTOM_BLOCK, ExistingBlock::customBlock);
CONTEXT_FUNCTIONS.put(DirectContextParameters.CUSTOM_BLOCK_STATE, ExistingBlock::customBlockState);
CONTEXT_FUNCTIONS.put(DirectContextParameters.WORLD, ExistingBlock::world);
CONTEXT_FUNCTIONS.put(DirectContextParameters.POSITION, ExistingBlock::position);
}
@SuppressWarnings("unchecked")
@Override
public <T> Optional<T> getOptionalParameter(ContextKey<T> parameter, BlockInWorld block) {
public <T> Optional<T> getOptionalParameter(ContextKey<T> parameter, ExistingBlock block) {
return (Optional<T>) Optional.ofNullable(CONTEXT_FUNCTIONS.get(parameter)).map(f -> f.apply(block));
}
}

View File

@@ -12,7 +12,7 @@ import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.plugin.context.ContextKey;
import net.momirealms.craftengine.core.util.Cancellable;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.world.BlockInWorld;
import net.momirealms.craftengine.core.world.ExistingBlock;
import net.momirealms.craftengine.core.world.Position;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.WorldPosition;
@@ -49,7 +49,7 @@ public final class DirectContextParameters {
public static final ContextKey<Item<?>> MAIN_HAND_ITEM = ContextKey.direct("main_hand_item");
public static final ContextKey<Item<?>> OFF_HAND_ITEM = ContextKey.direct("off_hand_item");
public static final ContextKey<CustomBlock> CUSTOM_BLOCK = ContextKey.direct("custom_block");
public static final ContextKey<BlockInWorld> BLOCK = ContextKey.direct("block");
public static final ContextKey<ExistingBlock> BLOCK = ContextKey.direct("block");
public static final ContextKey<Long> TIME = ContextKey.direct("time");
public static final ContextKey<Key> ID = ContextKey.direct("id");
public static final ContextKey<Integer> CUSTOM_MODEL_DATA = ContextKey.direct("custom_model_data");
@@ -57,8 +57,11 @@ public final class DirectContextParameters {
public static final ContextKey<AnchorType> ANCHOR_TYPE = ContextKey.direct("anchor_type");
public static final ContextKey<InteractionHand> HAND = ContextKey.direct("hand");
public static final ContextKey<Cancellable> EVENT = ContextKey.direct("event");
public static final ContextKey<Boolean> IS_FLYING = ContextKey.direct("is_flying");
public static final ContextKey<Boolean> IS_SNEAKING = ContextKey.direct("is_sneaking");
public static final ContextKey<Boolean> IS_SWIMMING = ContextKey.direct("is_swimming");
public static final ContextKey<Boolean> IS_CLIMBING = ContextKey.direct("is_climbing");
public static final ContextKey<Boolean> IS_GLIDING = ContextKey.direct("is_gliding");
public static final ContextKey<Boolean> IS_FLYING = ContextKey.direct("is_flying");
public static final ContextKey<Boolean> IS_CUSTOM = ContextKey.direct("is_custom");
public static final ContextKey<Boolean> IS_BLOCK_ITEM = ContextKey.direct("is_block_item");
public static final ContextKey<GameMode> GAMEMODE = ContextKey.direct("gamemode");

View File

@@ -29,8 +29,11 @@ public class PlayerParameterProvider implements ChainParameterProvider<Player> {
CONTEXT_FUNCTIONS.put(DirectContextParameters.NAME, Player::name);
CONTEXT_FUNCTIONS.put(DirectContextParameters.UUID, Player::uuid);
CONTEXT_FUNCTIONS.put(DirectContextParameters.WORLD, Entity::world);
CONTEXT_FUNCTIONS.put(DirectContextParameters.IS_FLYING, Player::isFlying);
CONTEXT_FUNCTIONS.put(DirectContextParameters.IS_SNEAKING, Player::isSneaking);
CONTEXT_FUNCTIONS.put(DirectContextParameters.IS_SWIMMING, Player::isSwimming);
CONTEXT_FUNCTIONS.put(DirectContextParameters.IS_CLIMBING, Player::isClimbing);
CONTEXT_FUNCTIONS.put(DirectContextParameters.IS_GLIDING, Player::isGliding);
CONTEXT_FUNCTIONS.put(DirectContextParameters.IS_FLYING, Player::isFlying);
CONTEXT_FUNCTIONS.put(DirectContextParameters.GAMEMODE, Player::gameMode);
CONTEXT_FUNCTIONS.put(DirectContextParameters.MAIN_HAND_ITEM, p -> p.getItemInHand(InteractionHand.MAIN_HAND));
CONTEXT_FUNCTIONS.put(DirectContextParameters.OFF_HAND_ITEM, p -> p.getItemInHand(InteractionHand.OFF_HAND));

View File

@@ -143,25 +143,6 @@ public class Dependencies {
Collections.emptyList()
);
public static final Dependency SLF4J_API = new Dependency(
"slf4j-api",
"org.slf4j",
"slf4j-api",
Collections.emptyList()
);
public static final Dependency SLF4J_SIMPLE = new Dependency(
"slf4j-simple",
"org.slf4j",
"slf4j-simple",
Collections.emptyList()
) {
@Override
public String getVersion() {
return Dependencies.SLF4J_API.getVersion();
}
};
public static final Dependency COMMONS_LANG3 = new Dependency(
"commons-lang3",
"org{}apache{}commons",
@@ -224,7 +205,8 @@ public class Dependencies {
"option",
List.of(Relocation.of("option", "net{}kyori{}option"),
Relocation.of("examination", "net{}kyori{}examination"),
Relocation.of("adventure", "net{}kyori{}adventure"))
Relocation.of("adventure", "net{}kyori{}adventure")),
true
);
public static final Dependency ADVENTURE_API = new Dependency(
@@ -233,7 +215,8 @@ public class Dependencies {
"adventure-api",
List.of(Relocation.of("option", "net{}kyori{}option"),
Relocation.of("examination", "net{}kyori{}examination"),
Relocation.of("adventure", "net{}kyori{}adventure"))
Relocation.of("adventure", "net{}kyori{}adventure")),
true
);
public static final Dependency ADVENTURE_NBT = new Dependency(
@@ -256,7 +239,8 @@ public class Dependencies {
"adventure-key",
List.of(Relocation.of("option", "net{}kyori{}option"),
Relocation.of("examination", "net{}kyori{}examination"),
Relocation.of("adventure", "net{}kyori{}adventure"))
Relocation.of("adventure", "net{}kyori{}adventure")),
true
) {
@Override
public String getVersion() {
@@ -270,7 +254,8 @@ public class Dependencies {
"examination-api",
List.of(Relocation.of("option", "net{}kyori{}option"),
Relocation.of("examination", "net{}kyori{}examination"),
Relocation.of("adventure", "net{}kyori{}adventure"))
Relocation.of("adventure", "net{}kyori{}adventure")),
true
);
public static final Dependency EXAMINATION_STRING = new Dependency(
@@ -279,7 +264,8 @@ public class Dependencies {
"examination-string",
List.of(Relocation.of("option", "net{}kyori{}option"),
Relocation.of("examination", "net{}kyori{}examination"),
Relocation.of("adventure", "net{}kyori{}adventure"))
Relocation.of("adventure", "net{}kyori{}adventure")),
true
) {
@Override
public String getVersion() {
@@ -293,7 +279,8 @@ public class Dependencies {
"adventure-text-minimessage",
List.of(Relocation.of("option", "net{}kyori{}option"),
Relocation.of("examination", "net{}kyori{}examination"),
Relocation.of("adventure", "net{}kyori{}adventure"))
Relocation.of("adventure", "net{}kyori{}adventure")),
true
) {
@Override
public String getVersion() {
@@ -307,7 +294,8 @@ public class Dependencies {
"adventure-text-serializer-commons",
List.of(Relocation.of("option", "net{}kyori{}option"),
Relocation.of("examination", "net{}kyori{}examination"),
Relocation.of("adventure", "net{}kyori{}adventure"))
Relocation.of("adventure", "net{}kyori{}adventure")),
true
) {
@Override
public String getVersion() {
@@ -321,7 +309,8 @@ public class Dependencies {
"adventure-text-serializer-gson",
List.of(Relocation.of("option", "net{}kyori{}option"),
Relocation.of("examination", "net{}kyori{}examination"),
Relocation.of("adventure", "net{}kyori{}adventure"))
Relocation.of("adventure", "net{}kyori{}adventure")),
true
) {
@Override
public String getVersion() {
@@ -335,7 +324,8 @@ public class Dependencies {
"adventure-text-serializer-json-legacy-impl",
List.of(Relocation.of("option", "net{}kyori{}option"),
Relocation.of("examination", "net{}kyori{}examination"),
Relocation.of("adventure", "net{}kyori{}adventure"))
Relocation.of("adventure", "net{}kyori{}adventure")),
true
) {
@Override
public String getVersion() {
@@ -349,7 +339,8 @@ public class Dependencies {
"adventure-text-serializer-legacy",
List.of(Relocation.of("option", "net{}kyori{}option"),
Relocation.of("examination", "net{}kyori{}examination"),
Relocation.of("adventure", "net{}kyori{}adventure"))
Relocation.of("adventure", "net{}kyori{}adventure")),
true
) {
@Override
public String getVersion() {
@@ -363,7 +354,8 @@ public class Dependencies {
"adventure-text-serializer-json",
List.of(Relocation.of("option", "net{}kyori{}option"),
Relocation.of("examination", "net{}kyori{}examination"),
Relocation.of("adventure", "net{}kyori{}adventure"))
Relocation.of("adventure", "net{}kyori{}adventure")),
true
) {
@Override
public String getVersion() {
@@ -396,14 +388,16 @@ public class Dependencies {
"netty-codec-http",
"io{}netty",
"netty-codec-http",
Collections.emptyList()
List.of(Relocation.of("netty{}handler{}codec{}http", "io{}netty{}handler{}codec{}http"),
Relocation.of("netty{}handler{}codec{}rtsp", "io{}netty{}handler{}codec{}rtsp"),
Relocation.of("netty{}handler{}codec{}spdy", "io{}netty{}handler{}codec{}spdy"))
);
public static final Dependency NETTY_HTTP2 = new Dependency(
"netty-codec-http2",
"io{}netty",
"netty-codec-http2",
Collections.emptyList()
List.of(Relocation.of("netty{}handler{}codec{}http2", "io{}netty{}handler{}codec{}http2"))
);
public static final Dependency REACTIVE_STREAMS = new Dependency(
@@ -420,7 +414,6 @@ public class Dependencies {
List.of(Relocation.of("jimfs", "com{}google{}common{}jimfs"))
);
public static final Dependency AMAZON_AWSSDK_S3 = new Dependency(
"amazon-sdk-s3",
"software{}amazon{}awssdk",

View File

@@ -15,21 +15,36 @@ public class Dependency {
private final String rawArtifactId;
private final List<Relocation> relocations;
private final Predicate<Path> verifier;
private final boolean shared;
public Dependency(String id, String groupId, String artifactId, List<Relocation> relocations) {
this(id, groupId, artifactId, relocations, false);
}
public Dependency(String id, String groupId, String artifactId, List<Relocation> relocations, boolean shared) {
this.id = id;
this.groupId = groupId;
this.rawArtifactId = artifactId;
this.relocations = relocations;
this.verifier = (p) -> true;
this.shared = shared;
}
public Dependency(String id, String groupId, String artifactId, List<Relocation> relocations, Predicate<Path> verifier) {
this(id, groupId, artifactId, relocations, verifier, false);
}
public Dependency(String id, String groupId, String artifactId, List<Relocation> relocations, Predicate<Path> verifier, boolean shared) {
this.id = id;
this.groupId = groupId;
this.rawArtifactId = artifactId;
this.relocations = relocations;
this.verifier = verifier;
this.shared = shared;
}
public boolean shared() {
return shared;
}
public boolean verify(Path remapped) {

View File

@@ -22,7 +22,8 @@ import java.util.stream.Stream;
public class DependencyManagerImpl implements DependencyManager {
private final DependencyRegistry registry;
private final Path cacheDirectory;
private final ClassPathAppender classPathAppender;
private final ClassPathAppender sharedClassPathAppender;
private final ClassPathAppender privateClassPathAppender;
private final Map<Dependency, Path> loaded = Collections.synchronizedMap(new HashMap<>());
private final Map<Set<Dependency>, IsolatedClassLoader> loaders = new HashMap<>();
private final RelocationHandler relocationHandler;
@@ -33,7 +34,8 @@ public class DependencyManagerImpl implements DependencyManager {
this.plugin = plugin;
this.registry = new DependencyRegistry();
this.cacheDirectory = setupCacheDirectory(plugin);
this.classPathAppender = plugin.classPathAppender();
this.sharedClassPathAppender = plugin.sharedClassPathAppender();
this.privateClassPathAppender = plugin.privateClassPathAppender();
this.loadingExecutor = plugin.scheduler().async();
this.relocationHandler = new RelocationHandler(this);
}
@@ -108,8 +110,14 @@ public class DependencyManagerImpl implements DependencyManager {
this.loaded.put(dependency, file);
if (this.classPathAppender != null && this.registry.shouldAutoLoad(dependency)) {
this.classPathAppender.addJarToClasspath(file);
if (dependency.shared()) {
if (this.sharedClassPathAppender != null && this.registry.shouldAutoLoad(dependency)) {
this.sharedClassPathAppender.addJarToClasspath(file);
}
} else {
if (this.privateClassPathAppender != null && this.registry.shouldAutoLoad(dependency)) {
this.privateClassPathAppender.addJarToClasspath(file);
}
}
}

View File

@@ -6,7 +6,7 @@ public final class Relocation {
private static final String RELOCATION_PREFIX = "net.momirealms.craftengine.libraries.";
public static Relocation of(String id, String pattern) {
return new Relocation(pattern.replace("{}", "."), RELOCATION_PREFIX + id);
return new Relocation(pattern.replace("{}", "."), RELOCATION_PREFIX + id.replace("{}", "."));
}
private final String pattern;

View File

@@ -92,7 +92,7 @@ public class I18NData {
}
private static String stateToRealBlockId(ImmutableBlockState state) {
String id = state.customBlockState().handle().toString();
String id = state.customBlockState().literalObject().toString();
int first = -1, last = -1;
for (int i = 0; i < id.length(); i++) {
char c = id.charAt(i);

View File

@@ -9,8 +9,9 @@ public enum Debugger {
COMMON(Config::debugCommon),
PACKET(Config::debugPacket),
FURNITURE(Config::debugFurniture),
RESOURCE_PACK(Config::debugFurniture),
ITEM(Config::debugItem);
RESOURCE_PACK(Config::debugResourcePack),
ITEM(Config::debugItem),
BLOCK_ENTITY(Config::debugBlockEntity);
private final Supplier<Boolean> condition;
@@ -26,7 +27,11 @@ public enum Debugger {
public void warn(Supplier<String> message, Throwable e) {
if (this.condition.get()) {
CraftEngine.instance().logger().warn("[DEBUG] " + message.get(), e);
if (e != null) {
CraftEngine.instance().logger().warn("[DEBUG] " + message.get(), e);
} else {
CraftEngine.instance().logger().warn("[DEBUG] " + message.get());
}
}
}
}

View File

@@ -1,6 +1,7 @@
package net.momirealms.craftengine.core.plugin.network;
import it.unimi.dsi.fastutil.ints.IntList;
import net.momirealms.craftengine.core.entity.player.Player;
public interface EntityPacketHandler {
@@ -8,7 +9,7 @@ public interface EntityPacketHandler {
return false;
}
default void handleSetEntityData(NetWorkUser user, ByteBufPacketEvent event) {
default void handleSetEntityData(Player user, ByteBufPacketEvent event) {
}
default void handleSyncEntityPosition(NetWorkUser user, NMSPacketEvent event, Object packet) {

View File

@@ -0,0 +1,21 @@
package net.momirealms.craftengine.core.plugin.network;
import io.netty.buffer.ByteBuf;
import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec;
import net.momirealms.craftengine.core.plugin.network.codec.NetworkDecoder;
import net.momirealms.craftengine.core.plugin.network.codec.NetworkMemberEncoder;
import net.momirealms.craftengine.core.util.FriendlyByteBuf;
import net.momirealms.craftengine.core.util.ResourceKey;
public interface ModPacket {
ResourceKey<NetworkCodec<FriendlyByteBuf, ? extends ModPacket>> type();
default void handle(NetWorkUser user) {
}
static <B extends ByteBuf, T extends ModPacket> NetworkCodec<B, T> codec(NetworkMemberEncoder<B, T> networkMemberEncoder, NetworkDecoder<B, T> networkDecoder) {
return NetworkCodec.ofMember(networkMemberEncoder, networkDecoder);
}
}

View File

@@ -4,10 +4,13 @@ import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.core.plugin.Plugin;
import net.momirealms.craftengine.core.util.IntIdentityList;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.world.chunk.ChunkStatus;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -38,6 +41,10 @@ public interface NetWorkUser {
void sendPacket(Object packet, boolean immediately, Runnable sendListener);
void sendPackets(List<Object> packet, boolean immediately);
void sendPackets(List<Object> packet, boolean immediately, Runnable sendListener);
void sendCustomPayload(Key channel, byte[] data);
void kick(Component message);
@@ -71,4 +78,18 @@ public interface NetWorkUser {
void setShouldProcessFinishConfiguration(boolean shouldProcess);
boolean shouldProcessFinishConfiguration();
boolean isChunkTracked(long chunkPos);
ChunkStatus getTrackedChunk(long chunkPos);
void addTrackedChunk(long chunkPos, ChunkStatus chunkStatus);
void clearTrackedChunks();
void removeTrackedChunk(long chunkPos);
IntIdentityList clientBlockList();
void setClientBlockList(IntIdentityList integers);
}

View File

@@ -0,0 +1,32 @@
package net.momirealms.craftengine.core.plugin.network.codec;
import java.util.function.Function;
public interface NetworkCodec<B, T> extends NetworkEncoder<B, T>, NetworkDecoder<B, T> {
default <V> NetworkCodec<B, V> map(Function<? super T, ? extends V> factory, Function<? super V, ? extends T> getter) {
return new NetworkCodec<>() {
@Override
public V decode(B in) {
return factory.apply(NetworkCodec.this.decode(in));
}
@Override
public void encode(B out, V value) {
NetworkCodec.this.encode(out, getter.apply(value));
}
};
}
static <B, V> NetworkCodec<B, V> ofMember(final NetworkMemberEncoder<B, V> networkMemberEncoder, final NetworkDecoder<B, V> networkDecoder) {
return new NetworkCodec<>() {
public V decode(B in) {
return networkDecoder.decode(in);
}
public void encode(B out, V value) {
networkMemberEncoder.encode(value, out);
}
};
}
}

View File

@@ -0,0 +1,394 @@
package net.momirealms.craftengine.core.plugin.network.codec;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.ByteBufUtil;
import io.netty.handler.codec.DecoderException;
import io.netty.handler.codec.EncoderException;
import net.momirealms.craftengine.core.util.MCUtils;
import net.momirealms.sparrow.nbt.CompoundTag;
import net.momirealms.sparrow.nbt.NBT;
import net.momirealms.sparrow.nbt.Tag;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import java.util.OptionalInt;
/**
* 随便写了点方便后面重构和客户端通讯
*/
public interface NetworkCodecs {
NetworkCodec<ByteBuf, Boolean> BOOLEAN = new NetworkCodec<>() {
@Override
public Boolean decode(ByteBuf in) {
return in.readBoolean();
}
@Override
public void encode(ByteBuf out, Boolean value) {
out.writeBoolean(value);
}
};
NetworkCodec<ByteBuf, Byte> BYTE = new NetworkCodec<>() {
@Override
public Byte decode(ByteBuf in) {
return in.readByte();
}
@Override
public void encode(ByteBuf out, Byte value) {
out.writeByte(value);
}
};
NetworkCodec<ByteBuf, Float> ROTATION_BYTE = BYTE.map(MCUtils::unpackDegrees, MCUtils::packDegrees);
NetworkCodec<ByteBuf, Short> SHORT = new NetworkCodec<>() {
@Override
public Short decode(ByteBuf in) {
return in.readShort();
}
@Override
public void encode(ByteBuf out, Short value) {
out.writeShort(value);
}
};
NetworkCodec<ByteBuf, Integer> UNSIGNED_SHORT = new NetworkCodec<>() {
@Override
public Integer decode(ByteBuf in) {
return in.readUnsignedShort();
}
@Override
public void encode(ByteBuf out, Integer value) {
out.writeShort(value);
}
};
NetworkCodec<ByteBuf, Integer> INTEGER = new NetworkCodec<>() {
@Override
public Integer decode(ByteBuf in) {
return in.readInt();
}
@Override
public void encode(ByteBuf out, Integer value) {
out.writeInt(value);
}
};
NetworkCodec<ByteBuf, Integer> VAR_INTEGER = new NetworkCodec<>() {
@Override
public Integer decode(ByteBuf in) {
int result = 0;
int bytesRead = 0;
byte currentByte;
do {
currentByte = in.readByte();
result |= (currentByte & 127) << bytesRead++ * 7;
if (bytesRead > 5) {
throw new RuntimeException("VarInt too big");
}
} while ((currentByte & 128) == 128);
return result;
}
@Override
public void encode(ByteBuf out, Integer value) {
while ((value & -128) != 0) {
out.writeByte(value & 127 | 128);
value >>>= 7;
}
out.writeByte(value);
}
};
NetworkCodec<ByteBuf, OptionalInt> OPTIONAL_VAR_INTEGER = VAR_INTEGER.map(
integer -> integer == 0 ? OptionalInt.empty() : OptionalInt.of(integer - 1),
optionalInt -> optionalInt.isPresent() ? optionalInt.getAsInt() + 1 : 0
);
NetworkCodec<ByteBuf, Long> LONG = new NetworkCodec<>() {
@Override
public Long decode(ByteBuf in) {
return in.readLong();
}
@Override
public void encode(ByteBuf out, Long value) {
out.writeLong(value);
}
};
NetworkCodec<ByteBuf, Long> VAR_LONG = new NetworkCodec<>() {
@Override
public Long decode(ByteBuf in) {
long result = 0L;
int bytesRead = 0;
byte currentByte;
do {
currentByte = in.readByte();
result |= (long)(currentByte & 127) << bytesRead++ * 7;
if (bytesRead > 10) {
throw new RuntimeException("VarLong too big");
}
} while ((currentByte & 128) == 128);
return result;
}
@Override
public void encode(ByteBuf out, Long value) {
while ((value & -128L) != 0L) {
out.writeByte((int)(value & 127L) | 128);
value >>>= 7;
}
out.writeByte(value.intValue());
}
};
NetworkCodec<ByteBuf, Float> FLOAT = new NetworkCodec<>() {
@Override
public Float decode(ByteBuf in) {
return in.readFloat();
}
@Override
public void encode(ByteBuf out, Float value) {
out.writeFloat(value);
}
};
NetworkCodec<ByteBuf, Double> DOUBLE = new NetworkCodec<>() {
@Override
public Double decode(ByteBuf in) {
return in.readDouble();
}
@Override
public void encode(ByteBuf out, Double value) {
out.writeDouble(value);
}
};
NetworkCodec<ByteBuf, byte[]> BYTE_ARRAY = new NetworkCodec<>() {
@Override
public byte[] decode(ByteBuf in) {
int maxSize = in.readableBytes();
int size = VAR_INTEGER.decode(in);
if (size > maxSize) {
throw new DecoderException("ByteArray with size " + size + " is bigger than allowed " + maxSize);
} else {
byte[] bytes = new byte[size];
in.readBytes(bytes);
return bytes;
}
}
@Override
public void encode(ByteBuf out, byte[] value) {
VAR_INTEGER.encode(out, value.length);
out.writeBytes(value);
}
};
NetworkCodec<ByteBuf, long[]> LONG_ARRAY = new NetworkCodec<>() {
@Override
public long[] decode(ByteBuf in) {
int arrayLength = VAR_INTEGER.decode(in);
int maxPossibleElements = in.readableBytes() / 8;
if (arrayLength > maxPossibleElements) {
throw new DecoderException("LongArray with size " + arrayLength + " is bigger than allowed " + maxPossibleElements);
} else {
long[] longArray = new long[arrayLength];
for (int i = 0; i < longArray.length; i++) {
longArray[i] = in.readLong();
}
return longArray;
}
}
@Override
public void encode(ByteBuf out, long[] value) {
VAR_INTEGER.encode(out, value.length);
for (long element : value) {
out.writeLong(element);
}
}
};
NetworkCodec<ByteBuf, String> STRING_UTF8 = new NetworkCodec<>() {
private static final int MAX_STRING_LENGTH = 32767;
@Override
public String decode(ByteBuf in) {
int maxEncodedBytes = ByteBufUtil.utf8MaxBytes(MAX_STRING_LENGTH);
int encodedLength = VAR_INTEGER.decode(in);
if (encodedLength > maxEncodedBytes) {
throw new DecoderException("The received encoded string buffer length is longer than maximum allowed (" + encodedLength + " > " + maxEncodedBytes + ")");
} else if (encodedLength < 0) {
throw new DecoderException("The received encoded string buffer length is less than zero! Weird string!");
} else {
int availableBytes = in.readableBytes();
if (encodedLength > availableBytes) {
throw new DecoderException("Not enough bytes in buffer, expected " + encodedLength + ", but got " + availableBytes);
} else {
String decodedString = in.toString(in.readerIndex(), encodedLength, StandardCharsets.UTF_8);
in.readerIndex(in.readerIndex() + encodedLength);
if (decodedString.length() > MAX_STRING_LENGTH) {
throw new DecoderException("The received string length is longer than maximum allowed (" + decodedString.length() + " > " + MAX_STRING_LENGTH + ")");
} else {
return decodedString;
}
}
}
}
@Override
public void encode(ByteBuf out, String value) {
if (value.length() > MAX_STRING_LENGTH) {
throw new EncoderException("String too big (was " + value.length() + " characters, max " + MAX_STRING_LENGTH + ")");
} else {
int maxPossibleBytes = ByteBufUtil.utf8MaxBytes(value);
ByteBuf tempBuffer = out.alloc().buffer(maxPossibleBytes);
try {
int actualEncodedBytes = ByteBufUtil.writeUtf8(tempBuffer, value);
int maxAllowedBytes = ByteBufUtil.utf8MaxBytes(MAX_STRING_LENGTH);
if (actualEncodedBytes > maxAllowedBytes) {
throw new EncoderException("String too big (was " + actualEncodedBytes + " bytes encoded, max " + maxAllowedBytes + ")");
}
VAR_INTEGER.encode(out, actualEncodedBytes);
out.writeBytes(tempBuffer);
} finally {
tempBuffer.release();
}
}
}
};
NetworkCodec<ByteBuf, Tag> TAG = new NetworkCodec<>() {
@Override
public Tag decode(ByteBuf in) {
int initialIndex = in.readerIndex();
byte marker = in.readByte();
if (marker == 0) {
return null;
} else {
in.readerIndex(initialIndex);
try {
return NBT.readUnnamedTag(new ByteBufInputStream(in), false);
} catch (IOException e) {
throw new EncoderException("Failed to read NBT compound: " + e.getMessage(), e);
}
}
}
@Override
public void encode(ByteBuf out, Tag value) {
if (value == null) {
out.writeByte(0);
} else {
try {
NBT.writeUnnamedTag(value, new ByteBufOutputStream(out), false);
} catch (IOException e) {
throw new EncoderException("Failed to write NBT compound: " + e.getMessage(), e);
}
}
}
};
NetworkCodec<ByteBuf, CompoundTag> COMPOUND_TAG = TAG.map(tag -> {
if (tag instanceof CompoundTag compoundTag) {
return compoundTag;
} else {
throw new DecoderException("Not a compound tag: " + tag);
}
}, tag -> tag);
NetworkCodec<ByteBuf, Optional<CompoundTag>> OPTIONAL_COMPOUND_TAG = new NetworkCodec<>() {
@Override
public Optional<CompoundTag> decode(ByteBuf in) {
int initialIndex = in.readerIndex();
byte marker = in.readByte();
if (marker == 0) {
return Optional.empty();
} else {
in.readerIndex(initialIndex);
try {
if (NBT.readUnnamedTag(new ByteBufInputStream(in), false) instanceof CompoundTag compoundTag) {
return Optional.of(compoundTag);
}
} catch (IOException e) {
throw new EncoderException("Failed to read NBT compound: " + e.getMessage(), e);
}
}
return Optional.empty();
}
@Override
public void encode(ByteBuf out, Optional<CompoundTag> value) {
CompoundTag compound = value.orElse(null);
if (compound == null) {
out.writeByte(0);
} else {
try {
NBT.writeUnnamedTag(compound, new ByteBufOutputStream(out), false);
} catch (IOException e) {
throw new EncoderException("Failed to write NBT compound: " + e.getMessage(), e);
}
}
}
};
NetworkCodec<ByteBuf, Vector3f> VECTOR3F = new NetworkCodec<>() {
@Override
public Vector3f decode(ByteBuf in) {
return new Vector3f(in.readFloat(), in.readFloat(), in.readFloat());
}
@Override
public void encode(ByteBuf out, Vector3f value) {
out.writeFloat(value.x());
out.writeFloat(value.y());
out.writeFloat(value.z());
}
};
NetworkCodec<ByteBuf, Quaternionf> QUATERNIONF = new NetworkCodec<>() {
@Override
public Quaternionf decode(ByteBuf in) {
return new Quaternionf(in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat());
}
@Override
public void encode(ByteBuf out, Quaternionf value) {
out.writeFloat(value.x());
out.writeFloat(value.y());
out.writeFloat(value.z());
out.writeFloat(value.w());
}
};
NetworkCodec<ByteBuf, Integer> CONTAINER_ID = VAR_INTEGER;
NetworkCodec<ByteBuf, Integer> RGB_COLOR = new NetworkCodec<>() {
@Override
public Integer decode(ByteBuf in) {
return 255 << 24 | in.readByte() & 0xFF << 16 | in.readByte() & 0xFF << 8 | in.readByte() & 0xFF;
}
@Override
public void encode(ByteBuf out, Integer value) {
out.writeByte(value >> 16 & 0xFF);
out.writeByte(value >> 8 & 0xFF);
out.writeByte(value & 0xFF);
}
};
}

View File

@@ -0,0 +1,5 @@
package net.momirealms.craftengine.core.plugin.network.codec;
public interface NetworkDecoder<I, T> {
T decode(I in);
}

View File

@@ -0,0 +1,5 @@
package net.momirealms.craftengine.core.plugin.network.codec;
public interface NetworkEncoder<O, T> {
void encode(O out, T value);
}

View File

@@ -0,0 +1,6 @@
package net.momirealms.craftengine.core.plugin.network.codec;
@FunctionalInterface
public interface NetworkMemberEncoder<O, T> {
void encode(T object, O object2);
}

View File

@@ -0,0 +1,51 @@
package net.momirealms.craftengine.core.plugin.text.component;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.util.AdventureHelper;
import java.util.function.Function;
import static net.momirealms.craftengine.core.plugin.text.minimessage.FormattedLine.CUSTOM_RESOLVERS;
public sealed interface ComponentProvider extends Function<Context, Component>
permits ComponentProvider.Constant, ComponentProvider.MiniMessage {
static ComponentProvider constant(Component component) {
return new Constant(component);
}
static ComponentProvider miniMessageOrConstant(String line) {
if (line.equals(AdventureHelper.customMiniMessage().stripTags(line, CUSTOM_RESOLVERS))) {
return constant(AdventureHelper.miniMessage().deserialize(line));
} else {
return new MiniMessage(line);
}
}
non-sealed class Constant implements ComponentProvider {
private final Component value;
public Constant(final Component value) {
this.value = value;
}
@Override
public Component apply(Context context) {
return this.value;
}
}
non-sealed class MiniMessage implements ComponentProvider {
private final String value;
public MiniMessage(final String value) {
this.value = value;
}
@Override
public Component apply(Context context) {
return AdventureHelper.miniMessage().deserialize(this.value, context.tagResolvers());
}
}
}

View File

@@ -9,12 +9,15 @@ import java.util.*;
public abstract class AbstractMappedRegistry<T> implements WritableRegistry<T> {
protected final ResourceKey<? extends Registry<T>> key;
protected final Map<Key, Holder.Reference<T>> byResourceLocation = new HashMap<>(512);
protected final Map<ResourceKey<T>, Holder.Reference<T>> byResourceKey = new HashMap<>(512);
protected final List<Holder.Reference<T>> byId = new ArrayList<>(512);
protected final Map<Key, Holder.Reference<T>> byResourceLocation;
protected final Map<ResourceKey<T>, Holder.Reference<T>> byResourceKey;
protected final List<Holder.Reference<T>> byId;
protected AbstractMappedRegistry(ResourceKey<? extends Registry<T>> key) {
protected AbstractMappedRegistry(ResourceKey<? extends Registry<T>> key, int expectedSize) {
this.key = key;
this.byResourceLocation = new HashMap<>(expectedSize);
this.byResourceKey = new HashMap<>(expectedSize);
this.byId = new ArrayList<>(expectedSize);
}
@Override

View File

@@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.registry;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.entity.BlockEntityType;
import net.momirealms.craftengine.core.block.properties.PropertyFactory;
import net.momirealms.craftengine.core.entity.furniture.HitBoxFactory;
import net.momirealms.craftengine.core.item.ItemDataModifierFactory;
@@ -40,53 +41,58 @@ import net.momirealms.craftengine.core.plugin.context.condition.ConditionFactory
import net.momirealms.craftengine.core.plugin.context.function.FunctionFactory;
import net.momirealms.craftengine.core.plugin.context.number.NumberProviderFactory;
import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectorFactory;
import net.momirealms.craftengine.core.plugin.network.ModPacket;
import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec;
import net.momirealms.craftengine.core.util.FriendlyByteBuf;
import net.momirealms.craftengine.core.util.ResourceKey;
public class BuiltInRegistries {
public static final Registry<CustomBlock> BLOCK = createDynamicBoundRegistry(Registries.BLOCK);
public static final Registry<BlockBehaviorFactory> BLOCK_BEHAVIOR_FACTORY = createConstantBoundRegistry(Registries.BLOCK_BEHAVIOR_FACTORY);
public static final Registry<ItemDataModifierFactory<?>> ITEM_DATA_MODIFIER_FACTORY = createConstantBoundRegistry(Registries.ITEM_DATA_MODIFIER_FACTORY);
public static final Registry<ItemBehaviorFactory> ITEM_BEHAVIOR_FACTORY = createConstantBoundRegistry(Registries.ITEM_BEHAVIOR_FACTORY);
public static final Registry<PropertyFactory> PROPERTY_FACTORY = createConstantBoundRegistry(Registries.PROPERTY_FACTORY);
public static final Registry<LootFunctionFactory<?>> LOOT_FUNCTION_FACTORY = createConstantBoundRegistry(Registries.LOOT_FUNCTION_FACTORY);
public static final Registry<ConditionFactory<LootContext>> LOOT_CONDITION_FACTORY = createConstantBoundRegistry(Registries.LOOT_CONDITION_FACTORY);
public static final Registry<LootEntryContainerFactory<?>> LOOT_ENTRY_CONTAINER_FACTORY = createConstantBoundRegistry(Registries.LOOT_ENTRY_CONTAINER_FACTORY);
public static final Registry<NumberProviderFactory> NUMBER_PROVIDER_FACTORY = createConstantBoundRegistry(Registries.NUMBER_PROVIDER_FACTORY);
public static final Registry<TemplateArgumentFactory> TEMPLATE_ARGUMENT_FACTORY = createConstantBoundRegistry(Registries.TEMPLATE_ARGUMENT_FACTORY);
public static final Registry<ItemModelFactory> ITEM_MODEL_FACTORY = createConstantBoundRegistry(Registries.ITEM_MODEL_FACTORY);
public static final Registry<ItemModelReader> ITEM_MODEL_READER = createConstantBoundRegistry(Registries.ITEM_MODEL_READER);
public static final Registry<TintFactory> TINT_FACTORY = createConstantBoundRegistry(Registries.TINT_FACTORY);
public static final Registry<TintReader> TINT_READER = createConstantBoundRegistry(Registries.TINT_READER);
public static final Registry<SpecialModelFactory> SPECIAL_MODEL_FACTORY = createConstantBoundRegistry(Registries.SPECIAL_MODEL_FACTORY);
public static final Registry<SpecialModelReader> SPECIAL_MODEL_READER = createConstantBoundRegistry(Registries.SPECIAL_MODEL_READER);
public static final Registry<RangeDispatchPropertyFactory> RANGE_DISPATCH_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.RANGE_DISPATCH_PROPERTY_FACTORY);
public static final Registry<RangeDispatchPropertyReader> RANGE_DISPATCH_PROPERTY_READER = createConstantBoundRegistry(Registries.RANGE_DISPATCH_PROPERTY_READER);
public static final Registry<ConditionPropertyFactory> CONDITION_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.CONDITION_PROPERTY_FACTORY);
public static final Registry<ConditionPropertyReader> CONDITION_PROPERTY_READER = createConstantBoundRegistry(Registries.CONDITION_PROPERTY_READER);
public static final Registry<SelectPropertyFactory> SELECT_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.SELECT_PROPERTY_FACTORY);
public static final Registry<SelectPropertyReader> SELECT_PROPERTY_READER = createConstantBoundRegistry(Registries.SELECT_PROPERTY_READER);
public static final Registry<RecipeSerializer<?, ? extends Recipe<?>>> RECIPE_SERIALIZER = createConstantBoundRegistry(Registries.RECIPE_FACTORY);
public static final Registry<ApplyBonusCountFunction.FormulaFactory> FORMULA_FACTORY = createConstantBoundRegistry(Registries.FORMULA_FACTORY);
public static final Registry<ConditionFactory<PathContext>> PATH_MATCHER_FACTORY = createConstantBoundRegistry(Registries.PATH_MATCHER_FACTORY);
public static final Registry<ResolutionFactory> RESOLUTION_FACTORY = createConstantBoundRegistry(Registries.RESOLUTION_FACTORY);
public static final Registry<CustomSmithingTransformRecipe.ItemDataProcessor.ProcessorFactory> SMITHING_RESULT_PROCESSOR_FACTORY = createConstantBoundRegistry(Registries.SMITHING_RESULT_PROCESSOR_FACTORY);
public static final Registry<HitBoxFactory> HITBOX_FACTORY = createConstantBoundRegistry(Registries.HITBOX_FACTORY);
public static final Registry<ResourcePackHostFactory> RESOURCE_PACK_HOST_FACTORY = createConstantBoundRegistry(Registries.RESOURCE_PACK_HOST_FACTORY);
public static final Registry<FunctionFactory<PlayerOptionalContext>> EVENT_FUNCTION_FACTORY = createConstantBoundRegistry(Registries.EVENT_FUNCTION_FACTORY);
public static final Registry<ConditionFactory<PlayerOptionalContext>> EVENT_CONDITION_FACTORY = createConstantBoundRegistry(Registries.EVENT_CONDITION_FACTORY);
public static final Registry<PlayerSelectorFactory<?>> PLAYER_SELECTOR_FACTORY = createConstantBoundRegistry(Registries.PLAYER_SELECTOR_FACTORY);
public static final Registry<EquipmentFactory> EQUIPMENT_FACTORY = createConstantBoundRegistry(Registries.EQUIPMENT_FACTORY);
public static final Registry<SlotDisplay.Type> SLOT_DISPLAY_TYPE = createConstantBoundRegistry(Registries.SLOT_DISPLAY_TYPE);
public static final Registry<RecipeDisplay.Type> RECIPE_DISPLAY_TYPE = createConstantBoundRegistry(Registries.RECIPE_DISPLAY_TYPE);
public static final Registry<LegacyRecipe.Type> LEGACY_RECIPE_TYPE = createConstantBoundRegistry(Registries.LEGACY_RECIPE_TYPE);
public static final Registry<PostProcessor.Type<?>> RECIPE_POST_PROCESSOR_TYPE = createConstantBoundRegistry(Registries.RECIPE_POST_PROCESSOR_TYPE);
public static final Registry<ItemUpdaterType<?>> ITEM_UPDATER_TYPE = createConstantBoundRegistry(Registries.ITEM_UPDATER_TYPE);
public static final Registry<CustomBlock> BLOCK = createDynamicBoundRegistry(Registries.BLOCK, 512);
public static final Registry<BlockBehaviorFactory> BLOCK_BEHAVIOR_FACTORY = createConstantBoundRegistry(Registries.BLOCK_BEHAVIOR_FACTORY, 64);
public static final Registry<ItemDataModifierFactory<?>> ITEM_DATA_MODIFIER_FACTORY = createConstantBoundRegistry(Registries.ITEM_DATA_MODIFIER_FACTORY, 64);
public static final Registry<ItemBehaviorFactory> ITEM_BEHAVIOR_FACTORY = createConstantBoundRegistry(Registries.ITEM_BEHAVIOR_FACTORY, 64);
public static final Registry<PropertyFactory> PROPERTY_FACTORY = createConstantBoundRegistry(Registries.PROPERTY_FACTORY, 16);
public static final Registry<LootFunctionFactory<?>> LOOT_FUNCTION_FACTORY = createConstantBoundRegistry(Registries.LOOT_FUNCTION_FACTORY, 32);
public static final Registry<ConditionFactory<LootContext>> LOOT_CONDITION_FACTORY = createConstantBoundRegistry(Registries.LOOT_CONDITION_FACTORY, 32);
public static final Registry<LootEntryContainerFactory<?>> LOOT_ENTRY_CONTAINER_FACTORY = createConstantBoundRegistry(Registries.LOOT_ENTRY_CONTAINER_FACTORY, 16);
public static final Registry<NumberProviderFactory> NUMBER_PROVIDER_FACTORY = createConstantBoundRegistry(Registries.NUMBER_PROVIDER_FACTORY, 16);
public static final Registry<TemplateArgumentFactory> TEMPLATE_ARGUMENT_FACTORY = createConstantBoundRegistry(Registries.TEMPLATE_ARGUMENT_FACTORY, 16);
public static final Registry<ItemModelFactory> ITEM_MODEL_FACTORY = createConstantBoundRegistry(Registries.ITEM_MODEL_FACTORY, 16);
public static final Registry<ItemModelReader> ITEM_MODEL_READER = createConstantBoundRegistry(Registries.ITEM_MODEL_READER, 16);
public static final Registry<TintFactory> TINT_FACTORY = createConstantBoundRegistry(Registries.TINT_FACTORY, 16);
public static final Registry<TintReader> TINT_READER = createConstantBoundRegistry(Registries.TINT_READER, 16);
public static final Registry<SpecialModelFactory> SPECIAL_MODEL_FACTORY = createConstantBoundRegistry(Registries.SPECIAL_MODEL_FACTORY, 16);
public static final Registry<SpecialModelReader> SPECIAL_MODEL_READER = createConstantBoundRegistry(Registries.SPECIAL_MODEL_READER, 16);
public static final Registry<RangeDispatchPropertyFactory> RANGE_DISPATCH_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.RANGE_DISPATCH_PROPERTY_FACTORY, 16);
public static final Registry<RangeDispatchPropertyReader> RANGE_DISPATCH_PROPERTY_READER = createConstantBoundRegistry(Registries.RANGE_DISPATCH_PROPERTY_READER, 16);
public static final Registry<ConditionPropertyFactory> CONDITION_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.CONDITION_PROPERTY_FACTORY, 16);
public static final Registry<ConditionPropertyReader> CONDITION_PROPERTY_READER = createConstantBoundRegistry(Registries.CONDITION_PROPERTY_READER, 16);
public static final Registry<SelectPropertyFactory> SELECT_PROPERTY_FACTORY = createConstantBoundRegistry(Registries.SELECT_PROPERTY_FACTORY, 16);
public static final Registry<SelectPropertyReader> SELECT_PROPERTY_READER = createConstantBoundRegistry(Registries.SELECT_PROPERTY_READER, 16);
public static final Registry<RecipeSerializer<?, ? extends Recipe<?>>> RECIPE_SERIALIZER = createConstantBoundRegistry(Registries.RECIPE_FACTORY, 16);
public static final Registry<ApplyBonusCountFunction.FormulaFactory> FORMULA_FACTORY = createConstantBoundRegistry(Registries.FORMULA_FACTORY, 16);
public static final Registry<ConditionFactory<PathContext>> PATH_MATCHER_FACTORY = createConstantBoundRegistry(Registries.PATH_MATCHER_FACTORY, 16);
public static final Registry<ResolutionFactory> RESOLUTION_FACTORY = createConstantBoundRegistry(Registries.RESOLUTION_FACTORY, 16);
public static final Registry<CustomSmithingTransformRecipe.ItemDataProcessor.ProcessorFactory> SMITHING_RESULT_PROCESSOR_FACTORY = createConstantBoundRegistry(Registries.SMITHING_RESULT_PROCESSOR_FACTORY, 16);
public static final Registry<HitBoxFactory> HITBOX_FACTORY = createConstantBoundRegistry(Registries.HITBOX_FACTORY, 16);
public static final Registry<ResourcePackHostFactory> RESOURCE_PACK_HOST_FACTORY = createConstantBoundRegistry(Registries.RESOURCE_PACK_HOST_FACTORY, 16);
public static final Registry<FunctionFactory<PlayerOptionalContext>> EVENT_FUNCTION_FACTORY = createConstantBoundRegistry(Registries.EVENT_FUNCTION_FACTORY, 128);
public static final Registry<ConditionFactory<PlayerOptionalContext>> EVENT_CONDITION_FACTORY = createConstantBoundRegistry(Registries.EVENT_CONDITION_FACTORY, 128);
public static final Registry<PlayerSelectorFactory<?>> PLAYER_SELECTOR_FACTORY = createConstantBoundRegistry(Registries.PLAYER_SELECTOR_FACTORY, 16);
public static final Registry<EquipmentFactory> EQUIPMENT_FACTORY = createConstantBoundRegistry(Registries.EQUIPMENT_FACTORY, 8);
public static final Registry<SlotDisplay.Type> SLOT_DISPLAY_TYPE = createConstantBoundRegistry(Registries.SLOT_DISPLAY_TYPE, 16);
public static final Registry<RecipeDisplay.Type> RECIPE_DISPLAY_TYPE = createConstantBoundRegistry(Registries.RECIPE_DISPLAY_TYPE, 16);
public static final Registry<LegacyRecipe.Type> LEGACY_RECIPE_TYPE = createConstantBoundRegistry(Registries.LEGACY_RECIPE_TYPE, 16);
public static final Registry<PostProcessor.Type<?>> RECIPE_POST_PROCESSOR_TYPE = createConstantBoundRegistry(Registries.RECIPE_POST_PROCESSOR_TYPE, 16);
public static final Registry<ItemUpdaterType<?>> ITEM_UPDATER_TYPE = createConstantBoundRegistry(Registries.ITEM_UPDATER_TYPE, 16);
public static final Registry<NetworkCodec<FriendlyByteBuf, ? extends ModPacket>> MOD_PACKET = createConstantBoundRegistry(Registries.MOD_PACKET, 16);
public static final Registry<BlockEntityType<?>> BLOCK_ENTITY_TYPE = createConstantBoundRegistry(Registries.BLOCK_ENTITY_TYPE, 128);
private static <T> Registry<T> createConstantBoundRegistry(ResourceKey<? extends Registry<T>> key) {
return new ConstantBoundRegistry<>(key);
private static <T> Registry<T> createConstantBoundRegistry(ResourceKey<? extends Registry<T>> key, int expectedSize) {
return new ConstantBoundRegistry<>(key, expectedSize);
}
private static <T> Registry<T> createDynamicBoundRegistry(ResourceKey<? extends Registry<T>> key) {
return new DynamicBoundRegistry<>(key);
private static <T> Registry<T> createDynamicBoundRegistry(ResourceKey<? extends Registry<T>> key, int expectedSize) {
return new DynamicBoundRegistry<>(key, expectedSize);
}
}

View File

@@ -12,10 +12,11 @@ import java.util.Objects;
public class ConstantBoundRegistry<T> extends AbstractMappedRegistry<T> {
protected final Reference2IntMap<T> toId = MCUtils.make(new Reference2IntOpenHashMap<>(), map -> map.defaultReturnValue(-1));
protected final Map<T, Holder.Reference<T>> byValue = new IdentityHashMap<>(512);
protected final Map<T, Holder.Reference<T>> byValue;
public ConstantBoundRegistry(ResourceKey<? extends Registry<T>> key) {
super(key);
public ConstantBoundRegistry(ResourceKey<? extends Registry<T>> key, int expectedSize) {
super(key, expectedSize);
this.byValue = new IdentityHashMap<>(expectedSize);
}
@Override

View File

@@ -8,8 +8,8 @@ import java.util.Objects;
public class DynamicBoundRegistry<T> extends AbstractMappedRegistry<T> {
public DynamicBoundRegistry(ResourceKey<? extends Registry<T>> key) {
super(key);
public DynamicBoundRegistry(ResourceKey<? extends Registry<T>> key, int expectedSize) {
super(key, expectedSize);
}
@Override

View File

@@ -2,6 +2,7 @@ package net.momirealms.craftengine.core.registry;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.entity.BlockEntityType;
import net.momirealms.craftengine.core.block.properties.PropertyFactory;
import net.momirealms.craftengine.core.entity.furniture.HitBoxFactory;
import net.momirealms.craftengine.core.item.ItemDataModifierFactory;
@@ -40,6 +41,9 @@ import net.momirealms.craftengine.core.plugin.context.condition.ConditionFactory
import net.momirealms.craftengine.core.plugin.context.function.FunctionFactory;
import net.momirealms.craftengine.core.plugin.context.number.NumberProviderFactory;
import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectorFactory;
import net.momirealms.craftengine.core.plugin.network.ModPacket;
import net.momirealms.craftengine.core.plugin.network.codec.NetworkCodec;
import net.momirealms.craftengine.core.util.FriendlyByteBuf;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.ResourceKey;
@@ -83,4 +87,6 @@ public class Registries {
public static final ResourceKey<Registry<LegacyRecipe.Type>> LEGACY_RECIPE_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("legacy_recipe_type"));
public static final ResourceKey<Registry<PostProcessor.Type<?>>> RECIPE_POST_PROCESSOR_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("recipe_post_processor_type"));
public static final ResourceKey<Registry<ItemUpdaterType<?>>> ITEM_UPDATER_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("item_updater_type"));
public static final ResourceKey<Registry<NetworkCodec<FriendlyByteBuf, ? extends ModPacket>>> MOD_PACKET = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("mod_packet_type"));
public static final ResourceKey<Registry<BlockEntityType<?>>> BLOCK_ENTITY_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("block_entity_type"));
}

View File

@@ -12,7 +12,8 @@ public enum SoundSource {
NEUTRAL("neutral"),
PLAYER("player"),
AMBIENT("ambient"),
VOICE("voice");
VOICE("voice"),
UI("ui");
private final String id;

View File

@@ -13,6 +13,8 @@ import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.text.serializer.json.JSONOptions;
import net.kyori.adventure.text.serializer.json.legacyimpl.NBTLegacyHoverEventSerializer;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider;
import net.momirealms.sparrow.nbt.Tag;
import net.momirealms.sparrow.nbt.adventure.NBTComponentSerializer;
import net.momirealms.sparrow.nbt.adventure.NBTSerializerOptions;
@@ -356,12 +358,15 @@ public class AdventureHelper {
return AdventureHelper.plainTextContent(resultComponent);
}
public static Component replaceText(Component text, Map<String, Component> replacements) {
public static Component replaceText(Component text, Map<String, ComponentProvider> replacements, Context context) {
String patternString = replacements.keySet().stream()
.map(Pattern::quote)
.collect(Collectors.joining("|"));
return text.replaceText(builder ->
builder.match(Pattern.compile(patternString))
.replacement((result, b) -> replacements.get(result.group())));
.replacement((result, b) ->
Optional.ofNullable(replacements.get(result.group())).orElseThrow(() -> new IllegalStateException("Could not find tag '" + result.group() + "'")).apply(context)
)
);
}
}

View File

@@ -4,10 +4,7 @@ import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigExce
import org.joml.Quaternionf;
import org.joml.Vector3f;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.*;
public class MiscUtils {
@@ -153,4 +150,17 @@ public class MiscUtils {
return o;
}
}
public static boolean matchRegex(String id, Set<String> ids, boolean regexMatch) {
if (regexMatch) {
for (String regex : ids) {
if (id.matches(regex)) {
return true;
}
}
} else {
return ids.contains(id);
}
return false;
}
}

View File

@@ -0,0 +1,17 @@
package net.momirealms.craftengine.core.util;
public class MutableBoolean {
private boolean value;
public MutableBoolean(boolean value) {
this.value = value;
}
public boolean booleanValue() {
return value;
}
public void set(boolean value) {
this.value = value;
}
}

View File

@@ -29,7 +29,7 @@ public class SectionPosUtils {
return nearby;
}
public static Map<Long, BitSet> toMap(Set<SectionPos> sections, int minLightSection, int maxLightSection) {
public static Map<Long, BitSet> toMap(Collection<SectionPos> sections, int minLightSection, int maxLightSection) {
int nBits = maxLightSection - minLightSection;
Map<Long, BitSet> nearby = new Long2ObjectOpenHashMap<>(Math.max(8, sections.size() / 2), 0.5f);
for (SectionPos section : sections) {

View File

@@ -4,6 +4,7 @@ import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.MCUtils;
public class BlockPos extends Vec3i {
public static final BlockPos ZERO = new BlockPos(0, 0, 0);
public BlockPos(int x, int y, int z) {
super(x, y, z);

View File

@@ -1,52 +1,78 @@
package net.momirealms.craftengine.core.world;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import ca.spottedleaf.concurrentutil.map.ConcurrentLong2ReferenceChainedHashTable;
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.entity.BlockEntity;
import net.momirealms.craftengine.core.block.entity.tick.TickingBlockEntity;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.scheduler.SchedulerTask;
import net.momirealms.craftengine.core.world.chunk.CEChunk;
import net.momirealms.craftengine.core.world.chunk.storage.StorageAdaptor;
import net.momirealms.craftengine.core.world.chunk.storage.WorldDataStorage;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public abstract class CEWorld {
public static final String REGION_DIRECTORY = "craftengine";
protected final World world;
protected final Map<Long, CEChunk> loadedChunkMap;
protected final ConcurrentLong2ReferenceChainedHashTable<CEChunk> loadedChunkMap;
protected final WorldDataStorage worldDataStorage;
protected final ReentrantReadWriteLock loadedChunkMapLock = new ReentrantReadWriteLock();
protected final WorldHeight worldHeightAccessor;
protected final Set<SectionPos> updatedSectionSet = ConcurrentHashMap.newKeySet(128);
private CEChunk lastChunk;
private long lastChunkPos;
protected final List<TickingBlockEntity> tickingBlockEntities = new ArrayList<>();
protected final List<TickingBlockEntity> pendingTickingBlockEntities = new ArrayList<>();
protected boolean isTickingBlockEntities = false;
protected SchedulerTask syncTickTask;
protected SchedulerTask asyncTickTask;
public CEWorld(World world, StorageAdaptor adaptor) {
this.world = world;
this.loadedChunkMap = new Long2ObjectOpenHashMap<>(1024, 0.5f);
this.loadedChunkMap = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(1024, 0.5f);
this.worldDataStorage = adaptor.adapt(world);
this.worldHeightAccessor = world.worldHeight();
this.lastChunkPos = ChunkPos.INVALID_CHUNK_POS;
}
public CEWorld(World world, WorldDataStorage dataStorage) {
this.world = world;
this.loadedChunkMap = new Long2ObjectOpenHashMap<>(1024, 0.5f);
this.loadedChunkMap = ConcurrentLong2ReferenceChainedHashTable.createWithCapacity(1024, 0.5f);
this.worldDataStorage = dataStorage;
this.worldHeightAccessor = world.worldHeight();
this.lastChunkPos = ChunkPos.INVALID_CHUNK_POS;
}
public void setTicking(boolean ticking) {
if (ticking) {
if (this.syncTickTask == null || this.syncTickTask.cancelled()) {
this.syncTickTask = CraftEngine.instance().scheduler().sync().runRepeating(this::syncTick, 1, 1);
}
if (this.asyncTickTask == null || this.asyncTickTask.cancelled()) {
this.asyncTickTask = CraftEngine.instance().scheduler().sync().runAsyncRepeating(this::asyncTick, 1, 1);
}
} else {
if (this.syncTickTask != null && !this.syncTickTask.cancelled()) {
this.syncTickTask.cancel();
}
if (this.asyncTickTask != null && !this.asyncTickTask.cancelled()) {
this.asyncTickTask.cancel();
}
}
}
public String name() {
return this.world.name();
}
public UUID uuid() {
return this.world.uuid();
}
public void save() {
this.loadedChunkMapLock.readLock().lock();
try {
for (Map.Entry<Long, CEChunk> entry : this.loadedChunkMap.entrySet()) {
for (ConcurrentLong2ReferenceChainedHashTable.TableEntry<CEChunk> entry : this.loadedChunkMap.entrySet()) {
CEChunk chunk = entry.getValue();
if (chunk.dirty()) {
worldDataStorage.writeChunkAt(new ChunkPos(entry.getKey()), chunk);
@@ -55,8 +81,6 @@ public abstract class CEWorld {
}
} catch (IOException e) {
CraftEngine.instance().logger().warn("Failed to save world chunks", e);
} finally {
this.loadedChunkMapLock.readLock().unlock();
}
}
@@ -65,44 +89,20 @@ public abstract class CEWorld {
}
public boolean isChunkLoaded(final long chunkPos) {
this.loadedChunkMapLock.readLock().lock();
try {
return loadedChunkMap.containsKey(chunkPos);
} finally {
this.loadedChunkMapLock.readLock().unlock();
}
return loadedChunkMap.containsKey(chunkPos);
}
public void addLoadedChunk(CEChunk chunk) {
this.loadedChunkMapLock.writeLock().lock();
try {
this.loadedChunkMap.put(chunk.chunkPos().longKey(), chunk);
} finally {
this.loadedChunkMapLock.writeLock().unlock();
}
this.loadedChunkMap.put(chunk.chunkPos().longKey(), chunk);
}
public void removeLoadedChunk(CEChunk chunk) {
this.loadedChunkMapLock.writeLock().lock();
try {
this.loadedChunkMap.remove(chunk.chunkPos().longKey());
if (this.lastChunk == chunk) {
this.lastChunk = null;
this.lastChunkPos = ChunkPos.INVALID_CHUNK_POS;
}
} finally {
this.loadedChunkMapLock.writeLock().unlock();
}
this.loadedChunkMap.remove(chunk.chunkPos().longKey());
}
@Nullable
public CEChunk getChunkAtIfLoaded(long chunkPos) {
this.loadedChunkMapLock.readLock().lock();
try {
return getChunkAtIfLoadedMainThread(chunkPos);
} finally {
this.loadedChunkMapLock.readLock().unlock();
}
return this.loadedChunkMap.get(chunkPos);
}
@Nullable
@@ -111,27 +111,6 @@ public abstract class CEWorld {
}
@Nullable
public CEChunk getChunkAtIfLoadedMainThread(long chunkPos) {
if (chunkPos == this.lastChunkPos) {
return this.lastChunk;
}
CEChunk chunk = this.loadedChunkMap.get(chunkPos);
if (chunk != null) {
this.lastChunk = chunk;
this.lastChunkPos = chunkPos;
}
return chunk;
}
@Nullable
public CEChunk getChunkAtIfLoadedMainThread(int x, int z) {
return getChunkAtIfLoadedMainThread(ChunkPos.asLong(x, z));
}
public WorldHeight worldHeight() {
return worldHeightAccessor;
}
public ImmutableBlockState getBlockStateAtIfLoaded(int x, int y, int z) {
CEChunk chunk = getChunkAtIfLoaded(x >> 4, z >> 4);
if (chunk == null) {
@@ -140,6 +119,7 @@ public abstract class CEWorld {
return chunk.getBlockState(x, y, z);
}
@Nullable
public ImmutableBlockState getBlockStateAtIfLoaded(BlockPos blockPos) {
CEChunk chunk = getChunkAtIfLoaded(blockPos.x() >> 4, blockPos.z() >> 4);
if (chunk == null) {
@@ -149,7 +129,7 @@ public abstract class CEWorld {
}
public boolean setBlockStateAtIfLoaded(BlockPos blockPos, ImmutableBlockState blockState) {
if (worldHeightAccessor.isOutsideBuildHeight(blockPos)) {
if (this.worldHeightAccessor.isOutsideBuildHeight(blockPos)) {
return false;
}
CEChunk chunk = getChunkAtIfLoaded(blockPos.x() >> 4, blockPos.z() >> 4);
@@ -160,6 +140,18 @@ public abstract class CEWorld {
return true;
}
@Nullable
public BlockEntity getBlockEntityAtIfLoaded(BlockPos blockPos) {
if (this.worldHeightAccessor.isOutsideBuildHeight(blockPos)) {
return null;
}
CEChunk chunk = getChunkAtIfLoaded(blockPos.x() >> 4, blockPos.z() >> 4);
if (chunk == null) {
return null;
}
return chunk.getBlockEntity(blockPos, true);
}
public WorldDataStorage worldDataStorage() {
return worldDataStorage;
}
@@ -172,5 +164,48 @@ public abstract class CEWorld {
this.updatedSectionSet.addAll(pos);
}
public abstract void tick();
public WorldHeight worldHeight() {
return this.worldHeightAccessor;
}
public void syncTick() {
this.tickBlockEntities();
if (!Config.asyncLightUpdate()) {
this.updateLight();
}
}
public void asyncTick() {
if (Config.asyncLightUpdate()) {
this.updateLight();
}
}
public abstract void updateLight();
public void addBlockEntityTicker(TickingBlockEntity ticker) {
if (this.isTickingBlockEntities) {
this.pendingTickingBlockEntities.add(ticker);
} else {
this.tickingBlockEntities.add(ticker);
}
}
protected void tickBlockEntities() {
this.isTickingBlockEntities = true;
if (!this.pendingTickingBlockEntities.isEmpty()) {
this.tickingBlockEntities.addAll(this.pendingTickingBlockEntities);
this.pendingTickingBlockEntities.clear();
}
ReferenceOpenHashSet<TickingBlockEntity> toRemove = new ReferenceOpenHashSet<>();
for (TickingBlockEntity blockEntity : this.tickingBlockEntities) {
if (blockEntity.isValid()) {
blockEntity.tick();
} else {
toRemove.add(blockEntity);
}
}
this.tickingBlockEntities.removeAll(toRemove);
this.isTickingBlockEntities = false;
}
}

View File

@@ -65,10 +65,23 @@ public class ChunkPos {
return longKey;
}
public ChunkPos[] adjacentChunkPos() {
return adjacentChunkPos(this);
}
public static long asLong(int chunkX, int chunkZ) {
return (long) chunkX & 4294967295L | ((long) chunkZ & 4294967295L) << 32;
}
public static ChunkPos[] adjacentChunkPos(ChunkPos chunkPos) {
return new ChunkPos[] {
new ChunkPos(chunkPos.x, chunkPos.z - 1),
new ChunkPos(chunkPos.x, chunkPos.z + 1),
new ChunkPos(chunkPos.x + 1, chunkPos.z),
new ChunkPos(chunkPos.x - 1, chunkPos.z)
};
}
@Override
public final boolean equals(Object o) {
if (o == null) return false;

View File

@@ -1,11 +1,15 @@
package net.momirealms.craftengine.core.world;
import net.momirealms.craftengine.core.block.BlockStateWrapper;
import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.state.StatePropertyAccessor;
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
import net.momirealms.craftengine.core.util.Key;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public interface BlockInWorld {
public interface ExistingBlock {
default boolean canBeReplaced(BlockPlaceContext blockPlaceContext) {
return false;
@@ -18,15 +22,25 @@ public interface BlockInWorld {
@Nullable
CustomBlock customBlock();
boolean isCustom();
@Nullable
ImmutableBlockState customBlockState();
@NotNull
BlockStateWrapper blockState();
@NotNull
StatePropertyAccessor createStatePropertyAccessor();
default WorldPosition position() {
return new WorldPosition(world(), x(), y(), z());
}
World world();
Key id();
int x();
int y();

View File

@@ -13,4 +13,8 @@ public class SectionPos extends Vec3i {
public static SectionPos of(BlockPos pos) {
return new SectionPos(pos.x() >> 4, pos.y() >> 4, pos.z() >> 4);
}
public static int sectionRelative(int rel) {
return rel & 15;
}
}

View File

@@ -47,12 +47,12 @@ public class Vec3i implements Comparable<Vec3i> {
@Override
public boolean equals(Object object) {
return this == object || object instanceof Vec3i vec3i && this.x() == vec3i.x() && this.y() == vec3i.y() && this.z() == vec3i.z();
return this == object || object instanceof Vec3i vec3i && this.x == vec3i.x && this.y == vec3i.y && this.z == vec3i.z;
}
@Override
public int hashCode() {
return (this.y() + this.z() * 31) * 31 + this.x();
return (this.y + this.z * 31) * 31 + this.x;
}
@Override

View File

@@ -21,9 +21,9 @@ public interface World {
WorldHeight worldHeight();
BlockInWorld getBlockAt(int x, int y, int z);
ExistingBlock getBlockAt(int x, int y, int z);
default BlockInWorld getBlockAt(final BlockPos pos) {
default ExistingBlock getBlockAt(final BlockPos pos) {
return getBlockAt(pos.x(), pos.y(), pos.z());
}

View File

@@ -1,37 +1,46 @@
package net.momirealms.craftengine.core.world.chunk;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.momirealms.craftengine.core.block.BlockEntityState;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.momirealms.craftengine.core.block.EmptyBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.entity.BlockEntity;
import net.momirealms.craftengine.core.block.entity.tick.*;
import net.momirealms.craftengine.core.plugin.logger.Debugger;
import net.momirealms.craftengine.core.world.*;
import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntitySerializer;
import net.momirealms.sparrow.nbt.ListTag;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
public class CEChunk {
private boolean loaded;
private final CEWorld world;
private final ChunkPos chunkPos;
private final CESection[] sections;
private final WorldHeight worldHeightAccessor;
private final Map<Integer, BlockEntityState> blockEntities;
private boolean dirty;
public final CEWorld world;
public final ChunkPos chunkPos;
public final CESection[] sections;
public final WorldHeight worldHeightAccessor;
public final Map<BlockPos, BlockEntity> blockEntities;
private volatile boolean dirty;
private volatile boolean loaded;
protected final Map<BlockPos, ReplaceableTickingBlockEntity> tickingBlockEntitiesByPos = new ConcurrentHashMap<>();
public CEChunk(CEWorld world, ChunkPos chunkPos) {
this.world = world;
this.chunkPos = chunkPos;
this.worldHeightAccessor = world.worldHeight();
this.sections = new CESection[this.worldHeightAccessor.getSectionsCount()];
this.blockEntities = new Int2ObjectOpenHashMap<>(16, 0.5f);
this.blockEntities = new Object2ObjectOpenHashMap<>(16, 0.5f);
this.fillEmptySection();
}
public CEChunk(CEWorld world, ChunkPos chunkPos, CESection[] sections, Map<Integer, BlockEntityState> blockEntities) {
public CEChunk(CEWorld world, ChunkPos chunkPos, CESection[] sections, ListTag blockEntitiesTag) {
this.world = world;
this.chunkPos = chunkPos;
this.blockEntities = blockEntities;
this.blockEntities = new Object2ObjectOpenHashMap<>(Math.max(blockEntitiesTag.size(), 16), 0.5f);
this.worldHeightAccessor = world.worldHeight();
int sectionCount = this.worldHeightAccessor.getSectionsCount();
this.sections = new CESection[sectionCount];
@@ -44,10 +53,97 @@ public class CEChunk {
}
}
this.fillEmptySection();
List<BlockEntity> blockEntities = DefaultBlockEntitySerializer.deserialize(this, blockEntitiesTag);
for (BlockEntity blockEntity : blockEntities) {
this.setBlockEntity(blockEntity);
}
}
public Map<Integer, BlockEntityState> blockEntities() {
return this.blockEntities;
public void addBlockEntity(BlockEntity blockEntity) {
this.setBlockEntity(blockEntity);
this.replaceOrCreateTickingBlockEntity(blockEntity);
}
public void removeBlockEntity(BlockPos blockPos) {
BlockEntity removedBlockEntity = this.blockEntities.remove(blockPos);
if (removedBlockEntity != null) {
removedBlockEntity.setValid(false);
}
}
public <T extends BlockEntity> void replaceOrCreateTickingBlockEntity(T blockEntity) {
ImmutableBlockState blockState = blockEntity.blockState();
BlockEntityTicker<T> ticker = blockState.createBlockEntityTicker(this.world, blockEntity.type());
if (ticker != null) {
this.tickingBlockEntitiesByPos.compute(blockEntity.pos(), ((pos, previousTicker) -> {
TickingBlockEntity newTicker = new TickingBlockEntityImpl<>(this, blockEntity, ticker);
if (previousTicker != null) {
previousTicker.setTicker(newTicker);
return previousTicker;
} else {
ReplaceableTickingBlockEntity replaceableTicker = new ReplaceableTickingBlockEntity(newTicker);
this.world.addBlockEntityTicker(replaceableTicker);
return replaceableTicker;
}
}));
} else {
this.removeBlockEntityTicker(blockEntity.pos());
}
}
private void removeBlockEntityTicker(BlockPos pos) {
ReplaceableTickingBlockEntity blockEntity = this.tickingBlockEntitiesByPos.remove(pos);
if (blockEntity != null) {
blockEntity.setTicker(DummyTickingBlockEntity.INSTANCE);
}
}
public void setBlockEntity(BlockEntity blockEntity) {
BlockPos pos = blockEntity.pos();
ImmutableBlockState blockState = this.getBlockState(pos);
if (!blockState.hasBlockEntity()) {
Debugger.BLOCK_ENTITY.debug(() -> "Failed to add invalid block entity " + blockEntity.saveAsTag() + " at " + pos);
return;
}
// 设置方块实体所在世界
blockEntity.setWorld(this.world);
blockEntity.setValid(true);
BlockEntity previous = this.blockEntities.put(pos, blockEntity);
// 标记旧的方块实体无效
if (previous != null && previous != blockEntity) {
previous.setValid(false);
}
}
@Nullable
public BlockEntity getBlockEntity(BlockPos pos, boolean create) {
BlockEntity blockEntity = this.blockEntities.get(pos);
if (blockEntity == null) {
if (create) {
blockEntity = createBlockEntity(pos);
if (blockEntity != null) {
this.addBlockEntity(blockEntity);
}
}
} else {
if (!blockEntity.isValid()) {
this.blockEntities.remove(pos);
return null;
}
}
return blockEntity;
}
private BlockEntity createBlockEntity(BlockPos pos) {
ImmutableBlockState blockState = this.getBlockState(pos);
if (!blockState.hasBlockEntity()) {
return null;
}
return Objects.requireNonNull(blockState.behavior().getEntityBehavior()).createBlockEntity(pos, blockState);
}
public Map<BlockPos, BlockEntity> blockEntities() {
return Collections.unmodifiableMap(this.blockEntities);
}
public boolean dirty() {
@@ -69,9 +165,9 @@ public class CEChunk {
}
private void fillEmptySection() {
for (int i = 0; i < sections.length; ++i) {
if (sections[i] == null) {
sections[i] = new CESection(world.worldHeight().getSectionYFromSectionIndex(i),
for (int i = 0; i < this.sections.length; ++i) {
if (this.sections[i] == null) {
this.sections[i] = new CESection(this.world.worldHeight().getSectionYFromSectionIndex(i),
new PalettedContainer<>(null, EmptyBlock.STATE, PalettedContainer.PaletteProvider.CUSTOM_BLOCK_STATE));
}
}
@@ -93,17 +189,17 @@ public class CEChunk {
}
}
@Nullable
@NotNull
public ImmutableBlockState getBlockState(BlockPos pos) {
return getBlockState(pos.x(), pos.y(), pos.z());
}
@Nullable
@NotNull
public ImmutableBlockState getBlockState(int x, int y, int z) {
int index = sectionIndex(SectionPos.blockToSectionCoord(y));
CESection section = this.sections[index];
if (section == null) {
return null;
return EmptyBlock.STATE;
}
return section.getBlockState((y & 15) << 8 | (z & 15) << 4 | x & 15);
}
@@ -128,21 +224,21 @@ public class CEChunk {
@NotNull
public CEWorld world() {
return world;
return this.world;
}
@NotNull
public ChunkPos chunkPos() {
return chunkPos;
return this.chunkPos;
}
@NotNull
public CESection[] sections() {
return sections;
return this.sections;
}
public boolean isLoaded() {
return loaded;
return this.loaded;
}
public void load() {

View File

@@ -9,8 +9,8 @@ public class CESection {
public static final int SECTION_HEIGHT = 16;
public static final int SECTION_SIZE = SECTION_WIDTH * SECTION_WIDTH * SECTION_HEIGHT;
private final int sectionY;
private final PalettedContainer<ImmutableBlockState> statesContainer;
public final int sectionY;
public final PalettedContainer<ImmutableBlockState> statesContainer;
public CESection(int sectionY, PalettedContainer<ImmutableBlockState> statesContainer) {
this.sectionY = sectionY;
@@ -53,6 +53,6 @@ public class CESection {
}
public int sectionY() {
return sectionY;
return this.sectionY;
}
}

View File

@@ -0,0 +1,7 @@
package net.momirealms.craftengine.core.world.chunk;
public class ChunkStatus {
public ChunkStatus() {
}
}

View File

@@ -1,48 +1,49 @@
package net.momirealms.craftengine.core.world.chunk.serialization;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.momirealms.craftengine.core.block.BlockEntityState;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.entity.BlockEntity;
import net.momirealms.craftengine.core.block.entity.BlockEntityType;
import net.momirealms.craftengine.core.plugin.logger.Debugger;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.chunk.CEChunk;
import net.momirealms.sparrow.nbt.CompoundTag;
import net.momirealms.sparrow.nbt.ListTag;
import org.jetbrains.annotations.ApiStatus;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public final class DefaultBlockEntitySerializer {
@ApiStatus.Experimental
public static ListTag serialize(Map<Integer, BlockEntityState> tiles) {
public static ListTag serialize(Map<BlockPos, BlockEntity> tiles) {
ListTag result = new ListTag();
Map<CompoundTag, int[]> nbtToPosMap = new Object2ObjectOpenHashMap<>(Math.max(tiles.size(), 10), 0.75f);
for (Map.Entry<Integer, BlockEntityState> entry : tiles.entrySet()) {
int pos = entry.getKey();
CompoundTag tag = entry.getValue().nbt();
int[] previous = nbtToPosMap.computeIfAbsent(tag, k -> new int[] {pos});
int[] newPoses = new int[previous.length + 1];
System.arraycopy(previous, 0, newPoses, 0, previous.length);
newPoses[previous.length] = pos;
nbtToPosMap.put(tag, newPoses);
}
for (Map.Entry<CompoundTag, int[]> entry : nbtToPosMap.entrySet()) {
CompoundTag blockEntityTag = new CompoundTag();
blockEntityTag.put("data", entry.getKey());
blockEntityTag.putIntArray("pos", entry.getValue());
result.add(blockEntityTag);
}
return result;
}
@ApiStatus.Experimental
public static Map<Integer, BlockEntityState> deserialize(ListTag tag) {
Map<Integer, BlockEntityState> result = new Object2ObjectOpenHashMap<>(Math.max(tag.size(), 16), 0.5f);
for (int i = 0; i < tag.size(); i++) {
CompoundTag blockEntityTag = tag.getCompound(i);
CompoundTag data = blockEntityTag.getCompound("data");
int[] pos = blockEntityTag.getIntArray("pos");
for (int j = 0; j < pos.length; j++) {
result.put(j, new BlockEntityState(data));
for (Map.Entry<BlockPos, BlockEntity> entry : tiles.entrySet()) {
BlockEntity entity = entry.getValue();
if (entity.isValid()) {
result.add(entity.saveAsTag());
}
}
return result;
}
public static List<BlockEntity> deserialize(CEChunk chunk, ListTag tag) {
List<BlockEntity> blockEntities = new ArrayList<>(tag.size());
for (int i = 0; i < tag.size(); i++) {
CompoundTag data = tag.getCompound(i);
Key id = Key.of(data.getString("id"));
BlockEntityType<?> type = BuiltInRegistries.BLOCK_ENTITY_TYPE.getValue(id);
if (type == null) {
Debugger.BLOCK_ENTITY.debug(() -> "Unknown block entity type: " + id);
} else {
BlockPos pos = BlockEntity.readPosAndVerify(data, chunk.chunkPos());
ImmutableBlockState blockState = chunk.getBlockState(pos);
BlockEntity blockEntity = type.factory().create(pos, blockState);
blockEntity.loadCustomData(data);
blockEntities.add(blockEntity);
}
}
return blockEntities;
}
}

View File

@@ -47,6 +47,6 @@ public final class DefaultChunkSerializer {
}
}
ListTag blockEntities = Optional.ofNullable(chunkNbt.getList("block_entities")).orElse(new ListTag());
return new CEChunk(world, pos, sectionArray, DefaultBlockEntitySerializer.deserialize(blockEntities));
return new CEChunk(world, pos, sectionArray, blockEntities);
}
}

View File

@@ -4,6 +4,7 @@ import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.EmptyBlock;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.InactiveCustomBlock;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.registry.Holder;
import net.momirealms.craftengine.core.registry.WritableRegistry;
@@ -58,7 +59,20 @@ public final class DefaultSectionSerializer {
CompoundTag palette = (CompoundTag) tag;
String id = palette.getString("id");
CompoundTag data = palette.getCompound("properties");
Key key = Key.of(id);
Key key;
if (Config.handleInvalidBlock()) {
String converted = Config.blockMappings().get(id);
if (converted == null) {
key = Key.of(id);
} else if (converted.isEmpty()) {
paletteEntries.add(EmptyBlock.STATE);
continue;
} else {
key = Key.of(converted);
}
} else {
key = Key.of(id);
}
Holder<CustomBlock> owner = BuiltInRegistries.BLOCK.get(key).orElseGet(() -> {
Holder.Reference<CustomBlock> holder = ((WritableRegistry<CustomBlock>) BuiltInRegistries.BLOCK).registerForHolder(
ResourceKey.create(BuiltInRegistries.BLOCK.key().location(), key));