9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-27 10:59:07 +00:00

Merge pull request #515 from jhqwqmc/dev

添加多高方块及物品行为
This commit is contained in:
XiaoMoMi
2025-12-26 04:40:41 +08:00
committed by GitHub
13 changed files with 428 additions and 10 deletions

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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<Object> 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<Object> 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<ItemStack> 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<MultiHighBlockBehavior> 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<Object> 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<Object> 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<String, Object> arguments) {
IntegerProperty high = (IntegerProperty) ResourceConfigUtils.requireNonNullOrThrow(block.getProperty("high"), "warning.config.block.behavior.multi_high.missing_high");
return new MultiHighBlockBehavior(block, high);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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<BlockState> 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<String, Object> 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()));
}
}
}
}

View File

@@ -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);
}

View File

@@ -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:

View File

@@ -289,6 +289,7 @@ warning.config.item.behavior.ground_block.missing_block: "<yellow>Issue found in
warning.config.item.behavior.furniture.missing_furniture: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'furniture' argument for 'furniture_item' behavior.</yellow>"
warning.config.item.behavior.liquid_collision.missing_block: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'block' argument for 'liquid_collision_block_item' behavior.</yellow>"
warning.config.item.behavior.double_high.missing_block: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'block' argument for 'double_high_block_item' behavior.</yellow>"
warning.config.item.behavior.multi_high.missing_block: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the required 'block' argument for 'multi_high_block_item' behavior.</yellow>"
warning.config.item.legacy_model.missing_path: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the require 'path' argument for legacy-model.</yellow>"
warning.config.item.legacy_model.overrides.missing_path: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the require 'path' argument for legacy-model overrides.</yellow>"
warning.config.item.legacy_model.overrides.missing_predicate: "<yellow>Issue found in file <arg:0> - The item '<arg:1>' is missing the require 'predicate' argument for legacy-model overrides.</yellow>"
@@ -433,6 +434,7 @@ warning.config.block.behavior.chime.missing_sounds_projectile_hit: "<yellow>Issu
warning.config.block.behavior.surface_spreading.missing_base_block: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'base-block' argument for 'surface_spreading_block' behavior.</yellow>"
warning.config.block.behavior.snowy.missing_snowy: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'snowy' property for 'snowy_block' behavior.</yellow>"
warning.config.block.behavior.hangable.missing_hanging: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'hanging' property for 'hangable_block' behavior.</yellow>"
warning.config.block.behavior.multi_high.missing_high: "<yellow>Issue found in file <arg:0> - The block '<arg:1>' is missing the required 'high' property for 'multi_high_block' behavior.</yellow>"
warning.config.model.generation.missing_parent: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'parent' argument in 'generation' section.</yellow>"
warning.config.model.generation.conflict: "<yellow>Issue found in file <arg:0> - Failed to generate model for '<arg:1>' as two or more configurations attempt to generate different json models with the same path: '<arg:2>'.</yellow>"
warning.config.model.generation.invalid_display_position: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is using an invalid display position '<arg:2>' in 'generation.display' section. Allowed display positions: [<arg:3>]</yellow>"

View File

@@ -289,6 +289,7 @@ warning.config.item.behavior.ground_block.missing_block: "<yellow>在文件 <arg
warning.config.item.behavior.furniture.missing_furniture: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'furniture_item' 行为缺少必需的 'furniture' 参数</yellow>"
warning.config.item.behavior.liquid_collision.missing_block: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'liquid_collision_block_item' 行为缺少必需的 'block' 参数</yellow>"
warning.config.item.behavior.double_high.missing_block: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'double_high_block_item' 行为缺少必需的 'block' 参数</yellow>"
warning.config.item.behavior.multi_high.missing_block: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的 'multi_high_block_item' 行为缺少必需的 'block' 参数</yellow>"
warning.config.item.legacy_model.missing_path: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的旧版模型(legacy-model)缺少必需的 'path' 参数</yellow>"
warning.config.item.legacy_model.overrides.missing_path: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的旧版模型覆写规则(overrides)缺少必需的 'path' 参数</yellow>"
warning.config.item.legacy_model.overrides.missing_predicate: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的旧版模型覆写规则(overrides)缺少必需的 'predicate' 参数</yellow>"
@@ -433,6 +434,7 @@ warning.config.block.behavior.chime.missing_sounds_projectile_hit: "<yellow>在
warning.config.block.behavior.surface_spreading.missing_base_block: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'surface_spreading_block' 行为缺少必需的 'base-block' 选项</yellow>"
warning.config.block.behavior.snowy.missing_snowy: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'snowy_block' 行为缺少必需的 'snowy' 属性</yellow>"
warning.config.block.behavior.hangable.missing_hanging: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'hangable_block' 行为缺少必需的 'hanging' 属性</yellow>"
warning.config.block.behavior.multi_high.missing_high: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的 'multi_high_block' 行为缺少必需的 'high' 属性</yellow>"
warning.config.model.generation.missing_parent: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 的 'generation' 段落缺少必需的 'parent' 参数</yellow>"
warning.config.model.generation.conflict: "<yellow>在文件 <arg:0> 发现问题 - 无法为 '<arg:1>' 生成模型 存在多个配置尝试使用相同路径 '<arg:2>' 生成不同的 JSON 模型</yellow>"
warning.config.model.generation.invalid_display_position: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 在 'generation.display' 区域使用了无效的 display 位置类型 '<arg:2>'. 可用展示类型: [<arg:3>]</yellow>"

View File

@@ -953,10 +953,22 @@ public abstract class AbstractPackManager implements PackManager {
List<Path> modelJsonToOptimize = new ArrayList<>();
Set<String> excludeTexture = new HashSet<>(Config.optimizeTextureExclude());
Set<String> excludeJson = new HashSet<>(Config.optimizeJsonExclude());
Set<String> excludeTexturePath = new HashSet<>(Config.optimizeTextureExcludePath());
Set<String> excludeJsonPath = new HashSet<>(Config.optimizeJsonExcludePath());
excludeTexture.addAll(this.parser.excludeTexture());
excludeJson.addAll(this.parser.excludeJson());
Predicate<Path> texturePathPredicate = p -> !excludeTexture.contains(CharacterUtils.replaceBackslashWithSlash(path.relativize(p).toString()));
Predicate<Path> jsonPathPredicate = p -> !excludeJson.contains(CharacterUtils.replaceBackslashWithSlash(path.relativize(p).toString()));
Predicate<Path> 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<Path> 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");

View File

@@ -109,9 +109,11 @@ public class Config {
protected boolean resource_pack$optimization$enable;
protected boolean resource_pack$optimization$texture$enable;
protected Set<String> resource_pack$optimization$texture$exlude;
protected Set<String> resource_pack$optimization$texture$exclude_path;
protected int resource_pack$optimization$texture$zopfli_iterations;
protected boolean resource_pack$optimization$json$enable;
protected Set<String> resource_pack$optimization$json$exclude;
protected Set<String> 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<String> 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<String> optimizeJsonExcludePath() {
return instance.resource_pack$optimization$json$exclude_path;
}
public static int zopfliIterations() {
return instance.resource_pack$optimization$texture$zopfli_iterations;
}

View File

@@ -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