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:
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -43,5 +43,5 @@ public interface BlockManager extends Manageable, ModelGenerator {
|
||||
ImmutableBlockState getImmutableBlockState(int stateId);
|
||||
|
||||
@Nullable
|
||||
BlockStateWrapper createPackedBlockState(String blockState);
|
||||
BlockStateWrapper createBlockState(String blockState);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package net.momirealms.craftengine.core.block.entity;
|
||||
|
||||
public class BlockEntityTypes {
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() + ']';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -5,4 +5,8 @@ public class ResourcePackGenerationException extends RuntimeException {
|
||||
public ResourcePackGenerationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ResourcePackGenerationException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -35,7 +35,9 @@ public interface Plugin {
|
||||
|
||||
PluginLogger logger();
|
||||
|
||||
ClassPathAppender classPathAppender();
|
||||
ClassPathAppender sharedClassPathAppender();
|
||||
|
||||
ClassPathAppender privateClassPathAppender();
|
||||
|
||||
File dataFolderFile();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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> {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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> {
|
||||
|
||||
@@ -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<>());
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package net.momirealms.craftengine.core.plugin.network.codec;
|
||||
|
||||
public interface NetworkDecoder<I, T> {
|
||||
T decode(I in);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package net.momirealms.craftengine.core.plugin.network.codec;
|
||||
|
||||
public interface NetworkEncoder<O, T> {
|
||||
void encode(O out, T value);
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package net.momirealms.craftengine.core.plugin.network.codec;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface NetworkMemberEncoder<O, T> {
|
||||
void encode(T object, O object2);
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ public enum SoundSource {
|
||||
NEUTRAL("neutral"),
|
||||
PLAYER("player"),
|
||||
AMBIENT("ambient"),
|
||||
VOICE("voice");
|
||||
VOICE("voice"),
|
||||
UI("ui");
|
||||
|
||||
private final String id;
|
||||
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package net.momirealms.craftengine.core.world.chunk;
|
||||
|
||||
public class ChunkStatus {
|
||||
|
||||
public ChunkStatus() {
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user