diff --git a/README.md b/README.md index 1780802cf..597ac2271 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ The code you contribute will be open-sourced under the GPLv3 license. If you pre Help sustain CraftEngine's development by going Premium! - **Polymart**: [Support via Polymart](https://polymart.org/product/7624/craftengine) -- **BuiltByBit**: [None] +- **BuiltByBit**: [Support via BuiltByBit](https://builtbybit.com/resources/craftengine.82674/) - **Afdian**: [Support via Afdian](https://afdian.com/@xiaomomi/) ## CraftEngine API diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java index aae33ecc1..a6c163446 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/BukkitBlockBehaviors.java @@ -49,6 +49,7 @@ public class BukkitBlockBehaviors extends BlockBehaviors { public static final Key HANGABLE_BLOCK = Key.from("craftengine:hangable_block"); public static final Key DROP_EXPERIENCE_BLOCK = Key.from("craftengine:drop_experience_block"); public static final Key DROP_EXP_BLOCK = Key.from("craftengine:drop_exp_block"); + public static final Key MULTI_HIGH_BLOCK = Key.from("craftengine:multi_high_block"); public static void init() { register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE); @@ -96,5 +97,6 @@ public class BukkitBlockBehaviors extends BlockBehaviors { register(HANGABLE_BLOCK, HangableBlockBehavior.FACTORY); register(DROP_EXPERIENCE_BLOCK, DropExperienceBlockBehavior.FACTORY); register(DROP_EXP_BLOCK, DropExperienceBlockBehavior.FACTORY); + register(MULTI_HIGH_BLOCK, MultiHighBlockBehavior.FACTORY); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoorBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoorBlockBehavior.java index a93cdf0e3..b0dcadff4 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoorBlockBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/DoorBlockBehavior.java @@ -114,11 +114,7 @@ public class DoorBlockBehavior extends AbstractCanSurviveBlockBehavior implement } else { if (half == DoubleBlockHalf.LOWER && direction == CoreReflections.instance$Direction$DOWN && !canSurvive(thisBlock, blockState, level, blockPos)) { - BlockPos pos = LocationUtils.fromBlockPos(blockPos); - net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level)); - WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(pos)); - world.playBlockSound(position, customState.settings().sounds().breakSound()); - FastNMS.INSTANCE.method$LevelAccessor$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, customState.customBlockState().registryId()); + MultiHighBlockBehavior.playBreakEffect(customState, blockPos, level); return MBlocks.AIR$defaultState; } return blockState; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/MultiHighBlockBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/MultiHighBlockBehavior.java new file mode 100644 index 000000000..15bb0068e --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/block/behavior/MultiHighBlockBehavior.java @@ -0,0 +1,251 @@ +package net.momirealms.craftengine.bukkit.block.behavior; + +import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids; +import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; +import net.momirealms.craftengine.bukkit.util.BlockStateUtils; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.bukkit.world.BukkitWorld; +import net.momirealms.craftengine.core.block.BlockBehavior; +import net.momirealms.craftengine.core.block.CustomBlock; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.UpdateOption; +import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory; +import net.momirealms.craftengine.core.block.properties.IntegerProperty; +import net.momirealms.craftengine.core.entity.player.InteractionHand; +import net.momirealms.craftengine.core.item.Item; +import net.momirealms.craftengine.core.item.context.BlockPlaceContext; +import net.momirealms.craftengine.core.util.Direction; +import net.momirealms.craftengine.core.util.ResourceConfigUtils; +import net.momirealms.craftengine.core.world.*; +import org.bukkit.inventory.ItemStack; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.Callable; + +public class MultiHighBlockBehavior extends BukkitBlockBehavior { + public static final Factory FACTORY = new Factory(); + public final IntegerProperty highProperty; + + public MultiHighBlockBehavior(CustomBlock customBlock, IntegerProperty highProperty) { + super(customBlock); + this.highProperty = highProperty; + } + + @SuppressWarnings("DuplicatedCode") + @Override + public Object updateShape(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object blockState = args[0]; + ImmutableBlockState customState = BlockStateUtils.getOptionalCustomBlockState(blockState).orElse(null); + if (customState == null || customState.isEmpty()) { + return MBlocks.AIR$defaultState; + } + MultiHighBlockBehavior behavior = customState.behavior().getAs(MultiHighBlockBehavior.class).orElse(null); + if (behavior == null) { + return MBlocks.AIR$defaultState; + } + IntegerProperty property = behavior.highProperty; + int high = customState.get(property); + Object direction = args[updateShape$direction]; + Object level = args[updateShape$level]; + Object blockPos = args[updateShape$blockPos]; + if (direction == CoreReflections.instance$Direction$UP && high != property.max) { + Object abovePos = LocationUtils.above(blockPos); + Object aboveState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, abovePos); + ImmutableBlockState state = BlockStateUtils.getOptionalCustomBlockState(aboveState).orElse(null); + if (state == null) { + playBreakEffect(customState, blockPos, level); + return MBlocks.AIR$defaultState; + } + MultiHighBlockBehavior aboveBehavior = state.behavior().getAs(MultiHighBlockBehavior.class).orElse(null); + if (aboveBehavior == null || aboveBehavior.highProperty != property) { + playBreakEffect(customState, blockPos, level); + return MBlocks.AIR$defaultState; + } + Integer aboveHigh = state.get(property); + if (high + 1 != aboveHigh) { + playBreakEffect(customState, blockPos, level); + return MBlocks.AIR$defaultState; + } + } else if (direction == CoreReflections.instance$Direction$DOWN && high != property.min) { + Object belowPos = LocationUtils.below(blockPos); + Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, belowPos); + ImmutableBlockState state = BlockStateUtils.getOptionalCustomBlockState(belowState).orElse(null); + if (state == null) { + playBreakEffect(customState, blockPos, level); + return MBlocks.AIR$defaultState; + } + MultiHighBlockBehavior belowBehavior = state.behavior().getAs(MultiHighBlockBehavior.class).orElse(null); + if (belowBehavior == null || belowBehavior.highProperty != property) { + playBreakEffect(customState, blockPos, level); + return MBlocks.AIR$defaultState; + } + Integer belowHigh = state.get(property); + if (high - 1 != belowHigh) { + playBreakEffect(customState, blockPos, level); + return MBlocks.AIR$defaultState; + } + } + return blockState; + } + + public static void playBreakEffect(ImmutableBlockState customState, Object blockPos, Object level) { + BlockPos pos = LocationUtils.fromBlockPos(blockPos); + net.momirealms.craftengine.core.world.World world = new BukkitWorld(FastNMS.INSTANCE.method$Level$getCraftWorld(level)); + WorldPosition position = new WorldPosition(world, Vec3d.atCenterOf(pos)); + world.playBlockSound(position, customState.settings().sounds().breakSound()); + FastNMS.INSTANCE.method$LevelAccessor$levelEvent(level, WorldEvents.BLOCK_BREAK_EFFECT, blockPos, customState.customBlockState().registryId()); + } + + @Override + public Object playerWillDestroy(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object player = args[3]; + ImmutableBlockState blockState = BlockStateUtils.getOptionalCustomBlockState(args[2]).orElse(null); + if (blockState == null || blockState.isEmpty()) { + return superMethod.call(); + } + BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(FastNMS.INSTANCE.method$ServerPlayer$getBukkitEntity(player)); + if (serverPlayer == null) { + return superMethod.call(); + } + Item item = serverPlayer.getItemInHand(InteractionHand.MAIN_HAND); + if (serverPlayer.canInstabuild() || !BlockStateUtils.isCorrectTool(blockState, item)) { + preventDropFromBasePart(args[0], args[1], blockState, player); + } + return superMethod.call(); + } + + private void preventDropFromBasePart(Object level, Object pos, ImmutableBlockState state, Object player) { + MultiHighBlockBehavior behavior = state.behavior().getAs(MultiHighBlockBehavior.class).orElse(null); + if (behavior == null) { + return; + } + IntegerProperty property = behavior.highProperty; + int high = state.get(property); + if (high == property.min) { + return; + } + Object basePos = LocationUtils.below(pos, high - property.min); + Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, basePos); + ImmutableBlockState baseState = BlockStateUtils.getOptionalCustomBlockState(blockState).orElse(null); + if (baseState == null || baseState.isEmpty()) { + return; + } + Optional baseBehavior = baseState.behavior().getAs(MultiHighBlockBehavior.class); + if (baseBehavior.isEmpty()) { + return; + } + IntegerProperty baseProperty = baseBehavior.get().highProperty; + if (baseState.get(baseProperty) != baseProperty.min) { + return; + } + Object emptyState = FastNMS.INSTANCE.method$FluidState$getType(FastNMS.INSTANCE.field$BlockBehaviour$BlockStateBase$fluidState(blockState)) == MFluids.WATER + ? MBlocks.WATER$defaultState + : MBlocks.AIR$defaultState; + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, basePos, emptyState, UpdateOption.builder().updateSuppressDrops().updateClients().updateNeighbors().build().flags()); + FastNMS.INSTANCE.method$LevelAccessor$levelEvent(level, player, WorldEvents.BLOCK_BREAK_EFFECT, basePos, baseState.customBlockState().registryId()); + } + + @Override + public boolean canSurvive(Object thisBlock, Object[] args, Callable superMethod) throws Exception { + Object state = args[0]; + Object world = args[1]; + Object blockPos = args[2]; + ImmutableBlockState customState = BlockStateUtils.getOptionalCustomBlockState(state).orElse(null); + if (customState == null || customState.isEmpty()) { + return false; + } + MultiHighBlockBehavior behavior = customState.behavior().getAs(MultiHighBlockBehavior.class).orElse(null); + if (behavior == null) { + return false; + } + IntegerProperty property = behavior.highProperty; + int high = customState.get(property); + if (high != property.min && high != property.max) { + Object aboveState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, LocationUtils.above(blockPos)); + Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, LocationUtils.below(blockPos)); + CustomBlock aboveCustomBlock = BlockStateUtils.getOptionalCustomBlockState(aboveState).map(blockState -> blockState.owner().value()).orElse(null); + CustomBlock belowCustomBlock = BlockStateUtils.getOptionalCustomBlockState(belowState).map(blockState -> blockState.owner().value()).orElse(null); + return aboveCustomBlock == behavior.customBlock && belowCustomBlock == behavior.customBlock; + } else if (high == property.max) { + Object belowState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(world, LocationUtils.below(blockPos)); + CustomBlock belowCustomBlock = BlockStateUtils.getOptionalCustomBlockState(belowState).map(blockState -> blockState.owner().value()).orElse(null); + return belowCustomBlock == behavior.customBlock; + } + return true; + } + + @Override + public void placeMultiState(Object thisBlock, Object[] args, Callable superMethod) { + Object blockState = args[2]; + Object pos = args[1]; + ImmutableBlockState state = BlockStateUtils.getOptionalCustomBlockState(blockState).orElse(null); + if (state == null) { + return; + } + MultiHighBlockBehavior behavior = state.behavior().getAs(MultiHighBlockBehavior.class).orElse(null); + if (behavior == null) { + return; + } + IntegerProperty property = behavior.highProperty; + for (int i = property.min + 1; i <= property.max; i++) { + FastNMS.INSTANCE.method$LevelWriter$setBlock(args[0], LocationUtils.above(pos, i), state.with(property, i).customBlockState().literalObject(), UpdateOption.UPDATE_ALL.flags()); + } + } + + @Override + public boolean hasMultiState(ImmutableBlockState baseState) { + return this.highProperty.max - this.highProperty.min > 0; + } + + @Override + public boolean canPlaceMultiState(BlockAccessor accessor, BlockPos pos, ImmutableBlockState state) { + MultiHighBlockBehavior behavior = state.behavior().getAs(MultiHighBlockBehavior.class).orElse(null); + if (behavior == null) { + return false; + } + IntegerProperty property = behavior.highProperty; + if (pos.y() >= accessor.worldHeight().getMaxBuildHeight() - property.max) { + return false; + } + for (int i = property.min + 1; i <= property.max; i++) { + if (!accessor.getBlockState(pos.relative(Direction.UP, i)).isAir()) { + return false; + } + } + return true; + } + + @Override + public ImmutableBlockState updateStateForPlacement(BlockPlaceContext context, ImmutableBlockState state) { + World world = context.getLevel(); + BlockPos pos = context.getClickedPos(); + MultiHighBlockBehavior behavior = state.behavior().getAs(MultiHighBlockBehavior.class).orElse(null); + if (behavior == null) { + return null; + } + IntegerProperty property = behavior.highProperty; + if (pos.y() >= context.getLevel().worldHeight().getMaxBuildHeight() - property.max) { + return null; + } + for (int i = property.min + 1; i <= property.max; i++) { + if (!world.getBlock(pos.relative(Direction.UP, i)).canBeReplaced(context)) { + return null; + } + } + return state.with(property, property.min); + } + + public static class Factory implements BlockBehaviorFactory { + + @Override + public BlockBehavior create(CustomBlock block, Map arguments) { + IntegerProperty high = (IntegerProperty) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("high"), "warning.config.block.behavior.multi_high.missing_high"); + return new MultiHighBlockBehavior(block, high); + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java index f236c8c29..a8a8a1ee6 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/BukkitItemBehaviors.java @@ -16,6 +16,7 @@ public class BukkitItemBehaviors extends ItemBehaviors { public static final Key WALL_BLOCK_ITEM = Key.from("craftengine:wall_block_item"); public static final Key CEILING_BLOCK_ITEM = Key.from("craftengine:ceiling_block_item"); public static final Key GROUND_BLOCK_ITEM = Key.from("craftengine:ground_block_item"); + public static final Key MULTI_HIGH_BLOCK_ITEM = Key.from("craftengine:multi_high_block_item"); public static void init() { register(EMPTY, EmptyItemBehavior.FACTORY); @@ -30,5 +31,6 @@ public class BukkitItemBehaviors extends ItemBehaviors { register(WALL_BLOCK_ITEM, WallBlockItemBehavior.FACTORY); register(CEILING_BLOCK_ITEM, CeilingBlockItemBehavior.FACTORY); register(GROUND_BLOCK_ITEM, GroundBlockItemBehavior.FACTORY); + register(MULTI_HIGH_BLOCK_ITEM, MultiHighBlockItemBehavior.FACTORY); } } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/MultiHighBlockItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/MultiHighBlockItemBehavior.java new file mode 100644 index 000000000..fad14c59c --- /dev/null +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/MultiHighBlockItemBehavior.java @@ -0,0 +1,122 @@ +package net.momirealms.craftengine.bukkit.item.behavior; + +import net.momirealms.craftengine.bukkit.block.behavior.MultiHighBlockBehavior; +import net.momirealms.craftengine.bukkit.nms.FastNMS; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBlocks; +import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids; +import net.momirealms.craftengine.bukkit.util.LocationUtils; +import net.momirealms.craftengine.core.block.ImmutableBlockState; +import net.momirealms.craftengine.core.block.UpdateOption; +import net.momirealms.craftengine.core.block.properties.IntegerProperty; +import net.momirealms.craftengine.core.entity.player.InteractionHand; +import net.momirealms.craftengine.core.entity.player.Player; +import net.momirealms.craftengine.core.item.behavior.ItemBehavior; +import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; +import net.momirealms.craftengine.core.item.context.BlockPlaceContext; +import net.momirealms.craftengine.core.pack.Pack; +import net.momirealms.craftengine.core.plugin.CraftEngine; +import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; +import net.momirealms.craftengine.core.util.Direction; +import net.momirealms.craftengine.core.util.Key; +import net.momirealms.craftengine.core.util.VersionHelper; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.block.data.BlockData; +import org.bukkit.event.block.BlockCanBuildEvent; +import org.bukkit.inventory.EquipmentSlot; + +import java.nio.file.Path; +import java.util.List; +import java.util.Map; + +public class MultiHighBlockItemBehavior extends BlockItemBehavior { + public static final Factory FACTORY = new Factory(); + + public MultiHighBlockItemBehavior(Key blockId) { + super(blockId); + } + + @SuppressWarnings({"UnstableApiUsage", "DuplicatedCode"}) + @Override + protected boolean canPlace(BlockPlaceContext context, ImmutableBlockState state) { + if (!super.canPlace(context, state)) { + return false; + } + MultiHighBlockBehavior behavior = state.behavior().getAs(MultiHighBlockBehavior.class).orElse(null); + if (behavior == null) { + return false; + } + IntegerProperty property = behavior.highProperty; + Player cePlayer = context.getPlayer(); + Object player = cePlayer != null ? cePlayer.serverPlayer() : null; + Object blockState = state.customBlockState().literalObject(); + for (int i = property.min + 1; i <= property.max; i++) { + Object blockPos = LocationUtils.toBlockPos(context.getClickedPos().relative(Direction.UP, i)); + try { + Object voxelShape; + if (VersionHelper.isOrAbove1_21_6()) { + voxelShape = CoreReflections.method$CollisionContext$placementContext.invoke(null, player); + } else if (player != null) { + voxelShape = CoreReflections.method$CollisionContext$of.invoke(null, player); + } else { + voxelShape = CoreReflections.instance$CollisionContext$empty; + } + Object world = FastNMS.INSTANCE.field$CraftWorld$ServerLevel((World) context.getLevel().platformWorld()); + boolean defaultReturn = (boolean) CoreReflections.method$ServerLevel$checkEntityCollision.invoke(world, blockState, player, voxelShape, blockPos, true); // paper only + Block block = FastNMS.INSTANCE.method$CraftBlock$at(world, blockPos); + BlockData blockData = FastNMS.INSTANCE.method$CraftBlockData$fromData(blockState); + BlockCanBuildEvent canBuildEvent = new BlockCanBuildEvent( + block, cePlayer != null ? (org.bukkit.entity.Player) cePlayer.platformPlayer() : null, blockData, defaultReturn, + context.getHand() == InteractionHand.MAIN_HAND ? EquipmentSlot.HAND : EquipmentSlot.OFF_HAND + ); + Bukkit.getPluginManager().callEvent(canBuildEvent); + if (!canBuildEvent.isBuildable()) { + return false; + } + } catch (ReflectiveOperationException e) { + CraftEngine.instance().logger().warn("Failed to check canPlace", e); + return false; + } + } + return true; + } + + @Override + protected boolean placeBlock(Location location, ImmutableBlockState blockState, List revertState) { + MultiHighBlockBehavior behavior = blockState.behavior().getAs(MultiHighBlockBehavior.class).orElse(null); + if (behavior == null) { + return false; + } + IntegerProperty property = behavior.highProperty; + for (int i = property.min + 1; i <= property.max; i++) { + Object level = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()); + Object blockPos = FastNMS.INSTANCE.constructor$BlockPos(location.getBlockX(), location.getBlockY() + i, location.getBlockZ()); + UpdateOption option = UpdateOption.builder().updateNeighbors().updateClients().updateImmediate().updateKnownShape().build(); + Object fluidData = FastNMS.INSTANCE.method$BlockGetter$getFluidState(level, blockPos); + Object stateToPlace = fluidData == MFluids.WATER$defaultState ? MBlocks.WATER$defaultState : MBlocks.AIR$defaultState; + revertState.add(location.getWorld().getBlockAt(location.getBlockX(), location.getBlockY() + i, location.getBlockZ()).getState()); + FastNMS.INSTANCE.method$LevelWriter$setBlock(level, blockPos, stateToPlace, option.flags()); + } + return super.placeBlock(location, blockState, revertState); + } + + public static class Factory implements ItemBehaviorFactory { + @Override + public ItemBehavior create(Pack pack, Path path, String node, Key key, Map arguments) { + Object id = arguments.get("block"); + if (id == null) { + throw new LocalizedResourceConfigException("warning.config.item.behavior.multi_high.missing_block"); + } + if (id instanceof Map map) { + addPendingSection(pack, path, node, key, map); + return new MultiHighBlockItemBehavior(key); + } else { + return new MultiHighBlockItemBehavior(Key.of(id.toString())); + } + } + } +} diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LocationUtils.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LocationUtils.java index f421ddedd..83b564ac9 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LocationUtils.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/util/LocationUtils.java @@ -45,10 +45,18 @@ public final class LocationUtils { return toBlockPos(FastNMS.INSTANCE.field$Vec3i$x(blockPos), FastNMS.INSTANCE.field$Vec3i$y(blockPos) + 1, FastNMS.INSTANCE.field$Vec3i$z(blockPos)); } + public static Object above(Object blockPos, int y) { + return toBlockPos(FastNMS.INSTANCE.field$Vec3i$x(blockPos), FastNMS.INSTANCE.field$Vec3i$y(blockPos) + y, FastNMS.INSTANCE.field$Vec3i$z(blockPos)); + } + public static Object below(Object blockPos) { return toBlockPos(FastNMS.INSTANCE.field$Vec3i$x(blockPos), FastNMS.INSTANCE.field$Vec3i$y(blockPos) - 1, FastNMS.INSTANCE.field$Vec3i$z(blockPos)); } + public static Object below(Object blockPos, int y) { + return toBlockPos(FastNMS.INSTANCE.field$Vec3i$x(blockPos), FastNMS.INSTANCE.field$Vec3i$y(blockPos) - y, FastNMS.INSTANCE.field$Vec3i$z(blockPos)); + } + public static Object toBlockPos(int x, int y, int z) { return FastNMS.INSTANCE.constructor$BlockPos(x, y, z); } diff --git a/common-files/src/main/resources/config.yml b/common-files/src/main/resources/config.yml index d4517cb2a..9aa139ed0 100644 --- a/common-files/src/main/resources/config.yml +++ b/common-files/src/main/resources/config.yml @@ -110,10 +110,13 @@ resource-pack: # If your image is special, for example, containing color pixels that need to be specifically recognized by a shader, the optimization might break it. You can add exclusions here. exclude: - assets/minecraft/textures/block/do_not_optimize.png + exclude-path: + - assets/minecraft/textures/block/do_not_optimize_path # .json / .mcmeta json: enable: true exclude: [] + exclude-path: [] # [Premium Exclusive] # Protect your resource pack from being cracked by others protection: diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml index f8f325359..f543e69db 100644 --- a/common-files/src/main/resources/translations/en.yml +++ b/common-files/src/main/resources/translations/en.yml @@ -289,6 +289,7 @@ warning.config.item.behavior.ground_block.missing_block: "Issue found in warning.config.item.behavior.furniture.missing_furniture: "Issue found in file - The item '' is missing the required 'furniture' argument for 'furniture_item' behavior." warning.config.item.behavior.liquid_collision.missing_block: "Issue found in file - The item '' is missing the required 'block' argument for 'liquid_collision_block_item' behavior." warning.config.item.behavior.double_high.missing_block: "Issue found in file - The item '' is missing the required 'block' argument for 'double_high_block_item' behavior." +warning.config.item.behavior.multi_high.missing_block: "Issue found in file - The item '' is missing the required 'block' argument for 'multi_high_block_item' behavior." warning.config.item.legacy_model.missing_path: "Issue found in file - The item '' is missing the require 'path' argument for legacy-model." warning.config.item.legacy_model.overrides.missing_path: "Issue found in file - The item '' is missing the require 'path' argument for legacy-model overrides." warning.config.item.legacy_model.overrides.missing_predicate: "Issue found in file - The item '' is missing the require 'predicate' argument for legacy-model overrides." @@ -433,6 +434,7 @@ warning.config.block.behavior.chime.missing_sounds_projectile_hit: "Issu warning.config.block.behavior.surface_spreading.missing_base_block: "Issue found in file - The block '' is missing the required 'base-block' argument for 'surface_spreading_block' behavior." warning.config.block.behavior.snowy.missing_snowy: "Issue found in file - The block '' is missing the required 'snowy' property for 'snowy_block' behavior." warning.config.block.behavior.hangable.missing_hanging: "Issue found in file - The block '' is missing the required 'hanging' property for 'hangable_block' behavior." +warning.config.block.behavior.multi_high.missing_high: "Issue found in file - The block '' is missing the required 'high' property for 'multi_high_block' behavior." warning.config.model.generation.missing_parent: "Issue found in file - The config '' is missing the required 'parent' argument in 'generation' section." warning.config.model.generation.conflict: "Issue found in file - Failed to generate model for '' as two or more configurations attempt to generate different json models with the same path: ''." warning.config.model.generation.invalid_display_position: "Issue found in file - The config '' is using an invalid display position '' in 'generation.display' section. Allowed display positions: []" diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml index ee38b4de6..6367c9c05 100644 --- a/common-files/src/main/resources/translations/zh_cn.yml +++ b/common-files/src/main/resources/translations/zh_cn.yml @@ -289,6 +289,7 @@ warning.config.item.behavior.ground_block.missing_block: "在文件 在文件 发现问题 - 物品 '' 的 'furniture_item' 行为缺少必需的 'furniture' 参数" warning.config.item.behavior.liquid_collision.missing_block: "在文件 发现问题 - 物品 '' 的 'liquid_collision_block_item' 行为缺少必需的 'block' 参数" warning.config.item.behavior.double_high.missing_block: "在文件 发现问题 - 物品 '' 的 'double_high_block_item' 行为缺少必需的 'block' 参数" +warning.config.item.behavior.multi_high.missing_block: "在文件 发现问题 - 物品 '' 的 'multi_high_block_item' 行为缺少必需的 'block' 参数" warning.config.item.legacy_model.missing_path: "在文件 发现问题 - 物品 '' 的旧版模型(legacy-model)缺少必需的 'path' 参数" warning.config.item.legacy_model.overrides.missing_path: "在文件 发现问题 - 物品 '' 的旧版模型覆写规则(overrides)缺少必需的 'path' 参数" warning.config.item.legacy_model.overrides.missing_predicate: "在文件 发现问题 - 物品 '' 的旧版模型覆写规则(overrides)缺少必需的 'predicate' 参数" @@ -433,6 +434,7 @@ warning.config.block.behavior.chime.missing_sounds_projectile_hit: "在 warning.config.block.behavior.surface_spreading.missing_base_block: "在文件 发现问题 - 方块 '' 的 'surface_spreading_block' 行为缺少必需的 'base-block' 选项" warning.config.block.behavior.snowy.missing_snowy: "在文件 发现问题 - 方块 '' 的 'snowy_block' 行为缺少必需的 'snowy' 属性" warning.config.block.behavior.hangable.missing_hanging: "在文件 发现问题 - 方块 '' 的 'hangable_block' 行为缺少必需的 'hanging' 属性" +warning.config.block.behavior.multi_high.missing_high: "在文件 发现问题 - 方块 '' 的 'multi_high_block' 行为缺少必需的 'high' 属性" warning.config.model.generation.missing_parent: "在文件 发现问题 - 配置项 '' 的 'generation' 段落缺少必需的 'parent' 参数" warning.config.model.generation.conflict: "在文件 发现问题 - 无法为 '' 生成模型 存在多个配置尝试使用相同路径 '' 生成不同的 JSON 模型" warning.config.model.generation.invalid_display_position: "在文件 发现问题 - 配置项 '' 在 'generation.display' 区域使用了无效的 display 位置类型 ''. 可用展示类型: []" diff --git a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java index f7d6138dd..8caaa6a07 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/pack/AbstractPackManager.java @@ -953,10 +953,22 @@ public abstract class AbstractPackManager implements PackManager { List modelJsonToOptimize = new ArrayList<>(); Set excludeTexture = new HashSet<>(Config.optimizeTextureExclude()); Set excludeJson = new HashSet<>(Config.optimizeJsonExclude()); + Set excludeTexturePath = new HashSet<>(Config.optimizeTextureExcludePath()); + Set excludeJsonPath = new HashSet<>(Config.optimizeJsonExcludePath()); excludeTexture.addAll(this.parser.excludeTexture()); excludeJson.addAll(this.parser.excludeJson()); - Predicate texturePathPredicate = p -> !excludeTexture.contains(CharacterUtils.replaceBackslashWithSlash(path.relativize(p).toString())); - Predicate jsonPathPredicate = p -> !excludeJson.contains(CharacterUtils.replaceBackslashWithSlash(path.relativize(p).toString())); + Predicate texturePathPredicate = p -> { + Path relativize = path.relativize(p); + boolean unFilteredFile = !excludeTexture.contains(CharacterUtils.replaceBackslashWithSlash(relativize.toString())); + boolean unFilteredPath = !excludeTexturePath.contains(CharacterUtils.replaceBackslashWithSlash(String.valueOf(relativize.getParent()))); + return unFilteredFile && unFilteredPath; + }; + Predicate jsonPathPredicate = p -> { + Path relativize = path.relativize(p); + boolean unFilteredFile = !excludeJson.contains(CharacterUtils.replaceBackslashWithSlash(relativize.toString())); + boolean unFilteredPath = !excludeJsonPath.contains(CharacterUtils.replaceBackslashWithSlash(String.valueOf(relativize.getParent()))); + return unFilteredFile && unFilteredPath; + }; if (Config.optimizeJson()) { Path metaPath = path.resolve("pack.mcmeta"); diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java index 87888fe94..7c88cd9ab 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/config/Config.java @@ -109,9 +109,11 @@ public class Config { protected boolean resource_pack$optimization$enable; protected boolean resource_pack$optimization$texture$enable; protected Set resource_pack$optimization$texture$exlude; + protected Set resource_pack$optimization$texture$exclude_path; protected int resource_pack$optimization$texture$zopfli_iterations; protected boolean resource_pack$optimization$json$enable; protected Set resource_pack$optimization$json$exclude; + protected Set resource_pack$optimization$json$exclude_path; protected MinecraftVersion resource_pack$supported_version$min; protected MinecraftVersion resource_pack$supported_version$max; @@ -391,11 +393,19 @@ public class Config { if (!p.endsWith(".png")) return p + ".png"; return p; }).collect(Collectors.toSet()); + resource_pack$optimization$texture$exclude_path = config.getStringList("resource-pack.optimization.texture.exclude-path").stream().map(p -> { + if (p.endsWith("/")) return p.substring(0, p.length() - 1); + return p; + }).collect(Collectors.toSet()); resource_pack$optimization$json$enable = config.getBoolean("resource-pack.optimization.json.enable", true); resource_pack$optimization$json$exclude = config.getStringList("resource-pack.optimization.json.exclude").stream().map(p -> { if (!p.endsWith(".json") && !p.endsWith(".mcmeta")) return p + ".json"; return p; }).collect(Collectors.toSet()); + resource_pack$optimization$json$exclude_path = config.getStringList("resource-pack.optimization.json.exclude-path").stream().map(p -> { + if (p.endsWith("/")) return p.substring(0, p.length() - 1); + return p; + }).collect(Collectors.toSet()); resource_pack$validation$enable = config.getBoolean("resource-pack.validation.enable", true); resource_pack$validation$fix_atlas = config.getBoolean("resource-pack.validation.fix-atlas", true); resource_pack$exclude_core_shaders = config.getBoolean("resource-pack.exclude-core-shaders", false); @@ -1209,6 +1219,10 @@ public class Config { return instance.resource_pack$optimization$texture$exlude; } + public static Set optimizeTextureExcludePath() { + return instance.resource_pack$optimization$texture$exclude_path; + } + public static boolean optimizeJson() { return instance.resource_pack$optimization$json$enable; } @@ -1217,6 +1231,10 @@ public class Config { return instance.resource_pack$optimization$json$exclude; } + public static Set optimizeJsonExcludePath() { + return instance.resource_pack$optimization$json$exclude_path; + } + public static int zopfliIterations() { return instance.resource_pack$optimization$texture$zopfli_iterations; } diff --git a/gradle.properties b/gradle.properties index e1c0bf3ed..e644a7ea3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,8 +2,8 @@ org.gradle.jvmargs=-Xmx4G # Project settings project_version=0.0.66.10 -config_version=65 -lang_version=47 +config_version=66 +lang_version=48 project_group=net.momirealms latest_supported_version=1.21.11