9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-19 15:09:15 +00:00

Merge pull request #448 from Xiao-MoMi/dev

部分修复
This commit is contained in:
XiaoMoMi
2025-11-09 15:36:22 +08:00
committed by GitHub
92 changed files with 1210 additions and 347 deletions

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "wiki"]
path = wiki
url = https://github.com/Xiao-MoMi/craft-engine-wiki.git

View File

@@ -75,7 +75,7 @@ repositories {
```
```kotlin
dependencies {
compileOnly("net.momirealms:craft-engine-core:0.0.64")
compileOnly("net.momirealms:craft-engine-bukkit:0.0.64")
compileOnly("net.momirealms:craft-engine-core:0.0.65")
compileOnly("net.momirealms:craft-engine-bukkit:0.0.65")
}
```

View File

@@ -37,8 +37,9 @@ dependencies {
compileOnly("com.infernalsuite.asp:api:4.0.0-SNAPSHOT")
// ModelEngine
compileOnly("com.ticxo.modelengine:ModelEngine:R4.0.8")
// BetterModels
compileOnly("io.github.toxicity188:BetterModel:1.7.0")
// BetterModel
compileOnly("io.github.toxicity188:bettermodel:1.14.0")
compileOnly("com.mojang:authlib:${rootProject.properties["authlib_version"]}")
// MMOItems
compileOnly("net.Indyuce:MMOItems-API:6.10-SNAPSHOT")
compileOnly("io.lumine:MythicLib-dist:1.6.2-SNAPSHOT")

View File

@@ -26,6 +26,14 @@ public class LegacySlimeWorldDataStorage implements WorldDataStorage {
return slimeWorld.get();
}
@Override
public CEChunk readNewChunkAt(CEWorld world, ChunkPos pos) {
SlimeChunk slimeChunk = getWorld().getChunk(pos.x, pos.z);
if (slimeChunk == null) return new CEChunk(world, pos);
slimeChunk.getExtraData().getValue().remove("craftengine");
return new CEChunk(world, pos);
}
@Override
public @NotNull CEChunk readChunkAt(@NotNull CEWorld world, @NotNull ChunkPos pos) {
SlimeChunk slimeChunk = getWorld().getChunk(pos.x, pos.z);
@@ -57,6 +65,13 @@ public class LegacySlimeWorldDataStorage implements WorldDataStorage {
}
}
@Override
public void clearChunkAt(@NotNull ChunkPos pos) {
SlimeChunk slimeChunk = getWorld().getChunk(pos.x, pos.z);
if (slimeChunk == null) return;
slimeChunk.getExtraData().getValue().remove("craftengine");
}
@Override
public void flush() {
}

View File

@@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.compatibility.model.bettermodel;
import kr.toxicity.model.api.BetterModel;
import kr.toxicity.model.api.data.renderer.ModelRenderer;
import kr.toxicity.model.api.tracker.DummyTracker;
import kr.toxicity.model.api.tracker.TrackerModifier;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.world.BlockPos;
@@ -23,11 +24,13 @@ public class BetterModelBlockEntityElement implements BlockEntityElement {
}
private DummyTracker createDummyTracker() {
ModelRenderer modelRenderer = BetterModel.plugin().modelManager().renderer(this.config.model());
ModelRenderer modelRenderer = BetterModel.plugin().modelManager().model(this.config.model());
if (modelRenderer == null) {
return null;
} else {
return modelRenderer.create(this.location);
return modelRenderer.create(this.location, TrackerModifier.builder()
.sightTrace(this.config.sightTrace())
.build());
}
}

View File

@@ -15,28 +15,38 @@ public class BetterModelBlockEntityElementConfig implements BlockEntityElementCo
private final float yaw;
private final float pitch;
private final String model;
private final boolean sightTrace;
public BetterModelBlockEntityElementConfig(String model, Vector3f position, float yaw, float pitch) {
public BetterModelBlockEntityElementConfig(String model,
Vector3f position,
float yaw,
float pitch,
boolean sightTrace) {
this.pitch = pitch;
this.position = position;
this.yaw = yaw;
this.model = model;
this.sightTrace = sightTrace;
}
public String model() {
return model;
return this.model;
}
public float pitch() {
return pitch;
return this.pitch;
}
public Vector3f position() {
return position;
return this.position;
}
public float yaw() {
return yaw;
return this.yaw;
}
public boolean sightTrace() {
return this.sightTrace;
}
@Override
@@ -44,6 +54,11 @@ public class BetterModelBlockEntityElementConfig implements BlockEntityElementCo
return new BetterModelBlockEntityElement(world, pos, this);
}
@Override
public Class<BetterModelBlockEntityElement> elementClass() {
return BetterModelBlockEntityElement.class;
}
public static class Factory implements BlockEntityElementConfigFactory {
@SuppressWarnings("unchecked")
@@ -54,7 +69,8 @@ public class BetterModelBlockEntityElementConfig implements BlockEntityElementCo
model,
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"),
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"),
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 0f), "pitch")
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 0f), "pitch"),
ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("sight-trace", true), "sight-trace")
);
}
}

View File

@@ -9,7 +9,7 @@ import org.bukkit.entity.Entity;
public class BetterModelUtils {
public static void bindModel(Entity base, String id) {
ModelRenderer renderer = BetterModel.plugin().modelManager().renderer(id);
ModelRenderer renderer = BetterModel.plugin().modelManager().model(id);
if (renderer == null) {
throw new NullPointerException("Could not find BetterModel blueprint " + id);
}

View File

@@ -44,6 +44,11 @@ public class ModelEngineBlockEntityElementConfig implements BlockEntityElementCo
return new ModelEngineBlockEntityElement(world, pos, this);
}
@Override
public Class<ModelEngineBlockEntityElement> elementClass() {
return ModelEngineBlockEntityElement.class;
}
public static class Factory implements BlockEntityElementConfigFactory {
@SuppressWarnings("unchecked")

View File

@@ -27,6 +27,14 @@ public class SlimeWorldDataStorage implements WorldDataStorage {
return slimeWorld.get();
}
@Override
public CEChunk readNewChunkAt(CEWorld world, ChunkPos pos) {
SlimeChunk slimeChunk = getWorld().getChunk(pos.x, pos.z);
if (slimeChunk == null) return new CEChunk(world, pos);
slimeChunk.getExtraData().remove("craftengine");
return new CEChunk(world, pos);
}
@Override
public @NotNull CEChunk readChunkAt(@NotNull CEWorld world, @NotNull ChunkPos pos) {
SlimeChunk slimeChunk = getWorld().getChunk(pos.x, pos.z);
@@ -42,7 +50,7 @@ public class SlimeWorldDataStorage implements WorldDataStorage {
}
}
@SuppressWarnings("unchecked")
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public void writeChunkAt(@NotNull ChunkPos pos, @NotNull CEChunk chunk) {
SlimeChunk slimeChunk = getWorld().getChunk(pos.x, pos.z);
@@ -53,8 +61,7 @@ public class SlimeWorldDataStorage implements WorldDataStorage {
} else {
try {
Object tag = adaptor.bytesToByteArrayTag(NBT.toBytes(nbt));
Map<String, ?> data1 = slimeChunk.getExtraData();
Map<String, Object> data2 = (Map<String, Object>) data1;
Map<String, Object> data2 = (Map) slimeChunk.getExtraData();
data2.put("craftengine", tag);
} catch (Exception e) {
throw new RuntimeException("Failed to write chunk tag to slime world. " + pos, e);
@@ -62,6 +69,13 @@ public class SlimeWorldDataStorage implements WorldDataStorage {
}
}
@Override
public void clearChunkAt(@NotNull ChunkPos pos) {
SlimeChunk slimeChunk = getWorld().getChunk(pos.x, pos.z);
if (slimeChunk == null) return;
slimeChunk.getExtraData().remove("craftengine");
}
@Override
public void flush() {
}

View File

@@ -59,7 +59,7 @@ public final class BukkitBlockManager extends AbstractBlockManager {
private Map<Integer, List<String>> clientBoundTags = Map.of();
private Map<Integer, List<String>> previousClientBoundTags = Map.of();
// 缓存的原版方块tag包
private Object cachedUpdateTagsPacket;
private List<TagUtils.TagEntry> cachedUpdateTags = List.of();
// 被移除声音的原版方块
private Set<Object> missingPlaceSounds = Set.of();
private Set<Object> missingBreakSounds = Set.of();
@@ -150,23 +150,14 @@ public final class BukkitBlockManager extends AbstractBlockManager {
}
@Override
protected void resendTags() {
protected void updateTags() {
// if there's no change
if (this.clientBoundTags.equals(this.previousClientBoundTags)) return;
List<TagUtils.TagEntry> list = new ArrayList<>();
for (Map.Entry<Integer, List<String>> entry : this.clientBoundTags.entrySet()) {
list.add(new TagUtils.TagEntry(entry.getKey(), entry.getValue()));
}
Object packet = TagUtils.createUpdateTagsPacket(Map.of(MRegistries.BLOCK, list));
for (BukkitServerPlayer player : this.plugin.networkManager().onlineUsers()) {
player.sendPacket(packet, false);
}
// 如果空,那么新来的玩家就没必要收到更新包了
if (list.isEmpty()) {
this.cachedUpdateTagsPacket = null;
} else {
this.cachedUpdateTagsPacket = packet;
}
this.cachedUpdateTags = list;
}
@Nullable
@@ -299,6 +290,10 @@ public final class BukkitBlockManager extends AbstractBlockManager {
this.burnOdds.put(nmsBlock, settings.fireSpreadChance());
this.burnableBlocks.add(nmsBlock);
}
Key vanillaBlockId = state.vanillaBlockState().ownerId();
BlockGenerator.field$CraftEngineBlock$isNoteBlock().set(nmsBlock, vanillaBlockId.equals(BlockKeys.NOTE_BLOCK));
BlockGenerator.field$CraftEngineBlock$isTripwire().set(nmsBlock, vanillaBlockId.equals(BlockKeys.TRIPWIRE));
} catch (ReflectiveOperationException e) {
this.plugin.logger().warn("Failed to apply platform block settings for block state " + state, e);
}
@@ -364,8 +359,8 @@ public final class BukkitBlockManager extends AbstractBlockManager {
}
}
public Object cachedUpdateTagsPacket() {
return this.cachedUpdateTagsPacket;
public List<TagUtils.TagEntry> cachedUpdateTags() {
return this.cachedUpdateTags;
}
public VisualBlockStatePacket cachedVisualBlockStatePacket() {

View File

@@ -1,6 +1,5 @@
package net.momirealms.craftengine.bukkit.block.entity;
import net.momirealms.craftengine.bukkit.block.behavior.SeatBlockBehavior;
import net.momirealms.craftengine.bukkit.entity.seat.BukkitSeat;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.entity.BlockEntity;
@@ -14,8 +13,6 @@ import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.sparrow.nbt.CompoundTag;
import java.util.Optional;
public class SeatBlockEntity extends BlockEntity implements SeatOwner {
private final Seat<SeatBlockEntity>[] seats;
@@ -41,25 +38,20 @@ public class SeatBlockEntity extends BlockEntity implements SeatOwner {
}
public boolean spawnSeat(Player player) {
Optional<SeatBlockBehavior> seatBehavior = super.blockState.behavior().getAs(SeatBlockBehavior.class);
if (seatBehavior.isEmpty()) {
return false;
}
float yRot = 0;
Property<HorizontalDirection> directionProperty = seatBehavior.get().directionProperty();
if (directionProperty != null) {
HorizontalDirection direction = super.blockState.get(directionProperty);
if (direction == HorizontalDirection.NORTH) {
yRot = 180;
} else if (direction == HorizontalDirection.EAST) {
yRot = -90;
} else if (direction == HorizontalDirection.WEST) {
yRot = 90;
}
Property<?> facing = super.blockState.owner().value().getProperty("facing");
int yRot = 0;
if (facing != null && facing.valueClass() == HorizontalDirection.class) {
HorizontalDirection direction = (HorizontalDirection) super.blockState.get(facing);
yRot = switch (direction) {
case NORTH -> 0;
case SOUTH -> 180;
case WEST -> 270;
case EAST -> 90;
};
}
for (Seat<SeatBlockEntity> seat : this.seats) {
if (!seat.isOccupied()) {
if (seat.spawnSeat(player, new WorldPosition(super.world.world(), super.pos.x() + 0.5, super.pos.y(), super.pos.z() + 0.5, 0, 180 - yRot))) {
if (seat.spawnSeat(player, new WorldPosition(super.world.world(), super.pos.x() + 0.5, super.pos.y(), super.pos.z() + 0.5, 0, yRot))) {
return true;
}
}

View File

@@ -7,6 +7,7 @@ public class BukkitBlockEntityElementConfigs extends BlockEntityElementConfigs {
static {
register(ITEM_DISPLAY, ItemDisplayBlockEntityElementConfig.FACTORY);
register(TEXT_DISPLAY, TextDisplayBlockEntityElementConfig.FACTORY);
register(ITEM, ItemBlockEntityElementConfig.FACTORY);
}
private BukkitBlockEntityElementConfigs() {}

View File

@@ -0,0 +1,65 @@
package net.momirealms.craftengine.bukkit.block.entity.renderer.element;
import it.unimi.dsi.fastutil.ints.IntList;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.world.BlockPos;
import org.joml.Vector3f;
import java.util.List;
import java.util.UUID;
public class ItemBlockEntityElement implements BlockEntityElement {
public final ItemBlockEntityElementConfig config;
public final Object cachedSpawnPacket1;
public final Object cachedSpawnPacket2;
public final Object cachedRidePacket;
public final Object cachedDespawnPacket;
public final Object cachedUpdatePosPacket;
public final int entityId1;
public final int entityId2;
public ItemBlockEntityElement(ItemBlockEntityElementConfig config, BlockPos pos) {
this(config, pos, CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(), CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(), false);
}
public ItemBlockEntityElement(ItemBlockEntityElementConfig config, BlockPos pos, int entityId1, int entityId2, boolean posChanged) {
this.config = config;
Vector3f position = config.position();
this.cachedSpawnPacket1 = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityId1, UUID.randomUUID(), pos.x() + position.x, pos.y() + position.y, pos.z() + position.z,
0, 0, MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0
);
this.cachedSpawnPacket2 = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityId2, UUID.randomUUID(), pos.x() + position.x, pos.y() + position.y, pos.z() + position.z,
0, 0, MEntityTypes.ITEM, 0, CoreReflections.instance$Vec3$Zero, 0
);
this.cachedRidePacket = FastNMS.INSTANCE.constructor$ClientboundSetPassengersPacket(entityId1, entityId2);
this.cachedDespawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(IntList.of(entityId1, entityId2));
this.entityId1 = entityId1;
this.entityId2 = entityId2;
this.cachedUpdatePosPacket = posChanged ? FastNMS.INSTANCE.constructor$ClientboundEntityPositionSyncPacket(this.entityId1, pos.x() + position.x, pos.y() + position.y, pos.z() + position.z, 0, 0, false) : null;
}
@Override
public void hide(Player player) {
player.sendPacket(this.cachedDespawnPacket, false);
}
@Override
public void show(Player player) {
player.sendPackets(List.of(this.cachedSpawnPacket1, this.cachedSpawnPacket2, this.cachedRidePacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId2, this.config.metadataValues(player))), false);
}
@Override
public void transform(Player player) {
if (this.cachedUpdatePosPacket != null) {
player.sendPackets(List.of(this.cachedUpdatePosPacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId2, this.config.metadataValues(player))), false);
} else {
player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId2, this.config.metadataValues(player)), false);
}
}
}

View File

@@ -0,0 +1,77 @@
package net.momirealms.craftengine.bukkit.block.entity.renderer.element;
import net.momirealms.craftengine.bukkit.entity.data.ItemEntityData;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.World;
import org.joml.Vector3f;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
public class ItemBlockEntityElementConfig implements BlockEntityElementConfig<ItemBlockEntityElement> {
public static final Factory FACTORY = new Factory();
private final Function<Player, List<Object>> lazyMetadataPacket;
private final Function<Player, Item<?>> item;
private final Vector3f position;
public ItemBlockEntityElementConfig(Function<Player, Item<?>> item, Vector3f position) {
this.item = item;
this.position = position;
this.lazyMetadataPacket = player -> {
List<Object> dataValues = new ArrayList<>();
ItemEntityData.Item.addEntityDataIfNotDefaultValue(item.apply(player).getLiteralObject(), dataValues);
ItemEntityData.NoGravity.addEntityDataIfNotDefaultValue(true, dataValues);
return dataValues;
};
}
@Override
public ItemBlockEntityElement create(World world, BlockPos pos) {
return new ItemBlockEntityElement(this, pos);
}
@Override
public ItemBlockEntityElement create(World world, BlockPos pos, ItemBlockEntityElement previous) {
return new ItemBlockEntityElement(this, pos, previous.entityId1, previous.entityId2, !previous.config.position.equals(this.position));
}
@Override
public Class<ItemBlockEntityElement> elementClass() {
return ItemBlockEntityElement.class;
}
public Vector3f position() {
return position;
}
public Item<?> item(Player player) {
return this.item.apply(player);
}
public List<Object> metadataValues(Player player) {
return this.lazyMetadataPacket.apply(player);
}
public static class Factory implements BlockEntityElementConfigFactory {
@SuppressWarnings("unchecked")
@Override
public <E extends BlockEntityElement> BlockEntityElementConfig<E> create(Map<String, Object> arguments) {
Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.block.state.entity_renderer.item_display.missing_item"));
return (BlockEntityElementConfig<E>) new ItemBlockEntityElementConfig(
player -> BukkitItemManager.instance().createWrappedItem(itemId, player),
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position")
);
}
}
}

View File

@@ -13,13 +13,17 @@ import java.util.List;
import java.util.UUID;
public class ItemDisplayBlockEntityElement implements BlockEntityElement {
private final ItemDisplayBlockEntityElementConfig config;
private final Object cachedSpawnPacket;
private final Object cachedDespawnPacket;
private final int entityId;
public final ItemDisplayBlockEntityElementConfig config;
public final Object cachedSpawnPacket;
public final Object cachedDespawnPacket;
public final Object cachedUpdatePosPacket;
public final int entityId;
public ItemDisplayBlockEntityElement(ItemDisplayBlockEntityElementConfig config, BlockPos pos) {
int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet();
this(config, pos, CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(), false);
}
public ItemDisplayBlockEntityElement(ItemDisplayBlockEntityElementConfig config, BlockPos pos, int entityId, boolean posChanged) {
Vector3f position = config.position();
this.cachedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityId, UUID.randomUUID(), pos.x() + position.x, pos.y() + position.y, pos.z() + position.z,
@@ -28,6 +32,7 @@ public class ItemDisplayBlockEntityElement implements BlockEntityElement {
this.config = config;
this.cachedDespawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(IntList.of(entityId));
this.entityId = entityId;
this.cachedUpdatePosPacket = posChanged ? FastNMS.INSTANCE.constructor$ClientboundEntityPositionSyncPacket(this.entityId, pos.x() + position.x, pos.y() + position.y, pos.z() + position.z, config.yRot(), config.xRot(), false) : null;
}
@Override
@@ -37,6 +42,15 @@ public class ItemDisplayBlockEntityElement implements BlockEntityElement {
@Override
public void show(Player player) {
player.sendPackets(List.of(this.cachedSpawnPacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player))), true);
player.sendPackets(List.of(this.cachedSpawnPacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player))), false);
}
@Override
public void transform(Player player) {
if (this.cachedUpdatePosPacket != null) {
player.sendPackets(List.of(this.cachedUpdatePosPacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player))), false);
} else {
player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player)), false);
}
}
}

View File

@@ -78,6 +78,20 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo
return new ItemDisplayBlockEntityElement(this, pos);
}
@Override
public ItemDisplayBlockEntityElement create(World world, BlockPos pos, ItemDisplayBlockEntityElement previous) {
Quaternionf previousRotation = previous.config.rotation;
if (previousRotation.x != 0 || previousRotation.y != 0 || previousRotation.z != 0 || previousRotation.w != 1) {
return null;
}
return new ItemDisplayBlockEntityElement(this, pos, previous.entityId, previous.config.yRot != this.yRot || previous.config.xRot != this.xRot || !previous.config.position.equals(this.position));
}
@Override
public Class<ItemDisplayBlockEntityElement> elementClass() {
return ItemDisplayBlockEntityElement.class;
}
public Item<?> item(Player player) {
return this.item.apply(player);
}

View File

@@ -13,13 +13,17 @@ import java.util.List;
import java.util.UUID;
public class TextDisplayBlockEntityElement implements BlockEntityElement {
private final TextDisplayBlockEntityElementConfig config;
private final Object cachedSpawnPacket;
private final Object cachedDespawnPacket;
private final int entityId;
public final TextDisplayBlockEntityElementConfig config;
public final Object cachedSpawnPacket;
public final Object cachedDespawnPacket;
public final Object cachedUpdatePosPacket;
public final int entityId;
public TextDisplayBlockEntityElement(TextDisplayBlockEntityElementConfig config, BlockPos pos) {
int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet();
this(config, pos, CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(), false);
}
public TextDisplayBlockEntityElement(TextDisplayBlockEntityElementConfig config, BlockPos pos, int entityId, boolean posChanged) {
Vector3f position = config.position();
this.cachedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityId, UUID.randomUUID(), pos.x() + position.x, pos.y() + position.y, pos.z() + position.z,
@@ -28,6 +32,7 @@ public class TextDisplayBlockEntityElement implements BlockEntityElement {
this.config = config;
this.cachedDespawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(IntList.of(entityId));
this.entityId = entityId;
this.cachedUpdatePosPacket = posChanged ? FastNMS.INSTANCE.constructor$ClientboundEntityPositionSyncPacket(this.entityId, pos.x() + position.x, pos.y() + position.y, pos.z() + position.z, config.yRot(), config.xRot(), false) : null;
}
@Override
@@ -37,6 +42,19 @@ public class TextDisplayBlockEntityElement implements BlockEntityElement {
@Override
public void show(Player player) {
player.sendPackets(List.of(this.cachedSpawnPacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player))), true);
player.sendPackets(List.of(this.cachedSpawnPacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player))), false);
}
@Override
public void transform(Player player) {
if (this.cachedUpdatePosPacket != null) {
player.sendPackets(List.of(this.cachedUpdatePosPacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player))), false);
} else {
player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player)), false);
}
}
public int entityId() {
return entityId;
}
}

View File

@@ -66,6 +66,20 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo
return new TextDisplayBlockEntityElement(this, pos);
}
@Override
public TextDisplayBlockEntityElement create(World world, BlockPos pos, TextDisplayBlockEntityElement previous) {
Quaternionf previousRotation = previous.config.rotation;
if (previousRotation.x != 0 || previousRotation.y != 0 || previousRotation.z != 0 || previousRotation.w != 1) {
return null;
}
return new TextDisplayBlockEntityElement(this, pos, previous.entityId, previous.config.yRot != this.yRot || previous.config.xRot != this.xRot || !previous.config.position.equals(this.position));
}
@Override
public Class<TextDisplayBlockEntityElement> elementClass() {
return TextDisplayBlockEntityElement.class;
}
public Component text(Player player) {
return AdventureHelper.miniMessage().deserialize(this.text, PlayerOptionalContext.of(player).tagResolvers());
}

View File

@@ -0,0 +1,11 @@
package net.momirealms.craftengine.bukkit.entity.data;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
public class ItemEntityData<T> extends BaseEntityData<T> {
public static final ItemEntityData<Object> Item = new ItemEntityData<>(ItemEntityData.class, EntityDataValue.Serializers$ITEM_STACK, CoreReflections.instance$ItemStack$EMPTY);
public ItemEntityData(Class<?> clazz, Object serializer, T defaultValue) {
super(clazz, serializer, defaultValue);
}
}

View File

@@ -28,7 +28,7 @@ import org.jetbrains.annotations.NotNull;
import java.io.IOException;
public class BukkitSeatManager implements SeatManager {
public class BukkitSeatManager implements SeatManager, Listener {
private static BukkitSeatManager instance;
public static final NamespacedKey SEAT_KEY = KeyUtils.toNamespacedKey(SeatManager.SEAT_KEY);
public static final NamespacedKey SEAT_EXTRA_DATA_KEY = KeyUtils.toNamespacedKey(SeatManager.SEAT_EXTRA_DATA_KEY);
@@ -113,9 +113,9 @@ public class BukkitSeatManager implements SeatManager {
if (!isSeat) return;
Location location = seat.getLocation();
if (seat instanceof ArmorStand) {
location.add(0, 0.9875,0);
location.add(0, 0.3875,0);
} else {
location.add(0,0.25,0);
location.add(0,-0.35,0);
}
seat.remove();
EntityUtils.safeDismount(player, location);

View File

@@ -501,10 +501,17 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
if (trim == null) {
item.resetComponent(DataComponentTypes.TRIM);
} else {
item.setJavaComponent(DataComponentTypes.TRIM, Map.of(
"pattern", trim.pattern().asString(),
"material", trim.material().asString()
));
try {
item.setJavaComponent(DataComponentTypes.TRIM, Map.of(
"pattern", trim.pattern().asString(),
"material", trim.material().asString()
));
} catch (Exception e) {
// 预防未启用基于纹饰盔甲时,锁链甲可能产生的网络问题(由用户配置决定)
if (!trim.material().equals(Key.of("minecraft", "custom")) && !trim.pattern().equals(Key.of("minecraft", "chainmail"))) {
this.plugin.logger().warn("Failed to apply trim " + trim, e);
}
}
}
}

View File

@@ -201,6 +201,14 @@ public final class BlockGenerator {
field$CraftEngineBlock$isTripwire = clazz$CraftEngineBlock.getField("isClientSideTripwire");
}
public static Field field$CraftEngineBlock$isNoteBlock() {
return field$CraftEngineBlock$isNoteBlock;
}
public static Field field$CraftEngineBlock$isTripwire() {
return field$CraftEngineBlock$isTripwire;
}
public static DelegatingBlock generateBlock(Key blockId) throws Throwable {
ObjectHolder<BlockBehavior> behaviorHolder = new ObjectHolder<>(EmptyBlockBehavior.INSTANCE);
ObjectHolder<BlockShape> shapeHolder = new ObjectHolder<>(STONE_SHAPE);

View File

@@ -19,6 +19,7 @@ import net.momirealms.craftengine.core.block.DelegatingBlockState;
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.render.ConstantBlockEntityRenderer;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.util.ReflectionUtils;
@@ -226,7 +227,10 @@ public final class WorldStorageInjector {
// 处理 自定义块到自定义块或原版块到自定义块
CEChunk chunk = holder.ceChunk();
chunk.setDirty(true);
ConstantBlockEntityRenderer previousRenderer = null;
// 如果两个方块没有相同的主人 且 旧方块有方块实体
if (!previousImmutableBlockState.isEmpty()) {
if (previousImmutableBlockState.owner() != newImmutableBlockState.owner() && previousImmutableBlockState.hasBlockEntity()) {
BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z);
@@ -242,9 +246,11 @@ public final class WorldStorageInjector {
}
if (previousImmutableBlockState.hasConstantBlockEntityRenderer()) {
BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z);
chunk.removeConstantBlockEntityRenderer(pos);
// 如果新状态没有entity renderer,那么直接移除,否则暂存
previousRenderer = chunk.removeConstantBlockEntityRenderer(pos, !newImmutableBlockState.hasConstantBlockEntityRenderer());
}
}
if (newImmutableBlockState.hasBlockEntity()) {
BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z);
BlockEntity blockEntity = chunk.getBlockEntity(pos, false);
@@ -264,10 +270,13 @@ public final class WorldStorageInjector {
chunk.createDynamicBlockEntityRenderer(blockEntity);
}
}
// 处理新老entity renderer更替
if (newImmutableBlockState.hasConstantBlockEntityRenderer()) {
BlockPos pos = new BlockPos(chunk.chunkPos.x * 16 + x, section.sectionY * 16 + y, chunk.chunkPos.z * 16 + z);
chunk.addConstantBlockEntityRenderer(pos, newImmutableBlockState);
chunk.addConstantBlockEntityRenderer(pos, newImmutableBlockState, previousRenderer);
}
// 如果新方块的光照属性和客户端认为的不同
if (Config.enableLightSystem()) {
if (previousImmutableBlockState.isEmpty()) {

View File

@@ -241,6 +241,19 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
this.c2sGamePacketListeners[id] = new ByteBufferPacketListenerHolder(name, listener);
}
@Override
public void delayedLoad() {
this.resendTags();
}
@SuppressWarnings("unchecked")
public void resendTags() {
Object packet = TagUtils.createUpdateTagsPacket(Map.of(MRegistries.BLOCK, BukkitBlockManager.instance().cachedUpdateTags()), FastNMS.INSTANCE.method$TagNetworkSerialization$serializeTagsToNetwork());
for (BukkitServerPlayer player : onlineUsers()) {
player.sendPacket(packet, false);
}
}
public void addFakePlayer(Player player) {
FakeBukkitServerPlayer fakePlayer = new FakeBukkitServerPlayer(this.plugin);
fakePlayer.setPlayer(player);
@@ -1857,11 +1870,17 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
public static class UpdateTagsListener implements NMSPacketListener {
@SuppressWarnings("unchecked")
@Override
public void onPacketSend(NetWorkUser user, NMSPacketEvent event, Object packet) {
Object modifiedPacket = BukkitBlockManager.instance().cachedUpdateTagsPacket();
if (packet.equals(modifiedPacket) || modifiedPacket == null) return;
event.replacePacket(modifiedPacket);
List<TagUtils.TagEntry> cachedUpdateTags = BukkitBlockManager.instance().cachedUpdateTags();
if (cachedUpdateTags.isEmpty()) return;
Map<Object, Object> tags = FastNMS.INSTANCE.field$ClientboundUpdateTagsPacket$tags(packet);
// 已经替换过了
if (tags instanceof MarkedHashMap<Object, Object>) return;
// 需要虚假的block
if (tags.get(MRegistries.BLOCK) == null) return;
event.replacePacket(TagUtils.createUpdateTagsPacket(Map.of(MRegistries.BLOCK, cachedUpdateTags), tags));
}
}
@@ -4132,16 +4151,22 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
CompoundTag tag = (CompoundTag) buf.readNbt(named);
// todo 刷怪笼里的物品?
// 展示架
if (VersionHelper.isOrAbove1_21_9() && tag != null && tag.containsKey("Items")) {
// 通用方块实体存储的物品
if (tag != null && tag.containsKey("Items")) {
BukkitItemManager itemManager = BukkitItemManager.instance();
ListTag itemsTag = tag.getList("Items");
List<Pair<Byte, ItemStack>> items = new ArrayList<>();
for (Tag itemTag : itemsTag) {
if (itemTag instanceof CompoundTag itemCompoundTag) {
byte slot = itemCompoundTag.getByte("Slot");
Object nmsStack = CoreReflections.instance$ItemStack$CODEC.parse(MRegistryOps.SPARROW_NBT, itemCompoundTag)
.resultOrPartial((error) -> CraftEngine.instance().logger().severe("Tried to parse invalid item: '" + error + "'")).orElse(null);
Object nmsStack;
if (VersionHelper.isOrAbove1_20_5()) {
nmsStack = CoreReflections.instance$ItemStack$CODEC.parse(MRegistryOps.SPARROW_NBT, itemCompoundTag)
.resultOrPartial((error) -> CraftEngine.instance().logger().severe("Tried to parse invalid item: '" + error + "'")).orElse(null);
} else {
Object nmsTag = MRegistryOps.SPARROW_NBT.convertTo(MRegistryOps.NBT, itemTag);
nmsStack = FastNMS.INSTANCE.method$ItemStack$of(nmsTag);
}
ItemStack bukkitStack = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsStack);
Optional<ItemStack> optional = itemManager.s2c(bukkitStack, (BukkitServerPlayer) user);
if (optional.isPresent()) {
@@ -4155,8 +4180,14 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
if (changed) {
ListTag newItemsTag = new ListTag();
for (Pair<Byte, ItemStack> pair : items) {
CompoundTag newItemCompoundTag = (CompoundTag) CoreReflections.instance$ItemStack$CODEC.encodeStart(MRegistryOps.SPARROW_NBT, FastNMS.INSTANCE.field$CraftItemStack$handle(pair.right()))
.resultOrPartial((error) -> CraftEngine.instance().logger().severe("Tried to encode invalid item: '" + error + "'")).orElse(null);
CompoundTag newItemCompoundTag;
if (VersionHelper.isOrAbove1_20_5()) {
newItemCompoundTag = (CompoundTag) CoreReflections.instance$ItemStack$CODEC.encodeStart(MRegistryOps.SPARROW_NBT, FastNMS.INSTANCE.field$CraftItemStack$handle(pair.right()))
.resultOrPartial((error) -> CraftEngine.instance().logger().severe("Tried to encode invalid item: '" + error + "'")).orElse(null);
} else {
Object nmsTag = FastNMS.INSTANCE.method$itemStack$save(FastNMS.INSTANCE.field$CraftItemStack$handle(pair.right()), FastNMS.INSTANCE.constructor$CompoundTag());
newItemCompoundTag = (CompoundTag) MRegistryOps.NBT.convertTo(MRegistryOps.SPARROW_NBT, nmsTag);
}
if (newItemCompoundTag != null) {
newItemCompoundTag.putByte("Slot", pair.left());
newItemsTag.add(newItemCompoundTag);

View File

@@ -56,7 +56,7 @@ public record ClientCustomBlockPacket(int vanillaSize, int currentSize) implemen
return;
}
int serverBlockRegistrySize = RegistryUtils.currentBlockRegistrySize();
if (this.currentSize != serverBlockRegistrySize) {
if (this.currentSize < serverBlockRegistrySize) {
user.kick(Component.translatable(
"disconnect.craftengine.current_block_registry_mismatch",
TranslationArgument.numeric(this.currentSize),

View File

@@ -1082,7 +1082,7 @@ public final class CoreReflections {
public static final Class<?> clazz$MobEffect = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("world.effect.MobEffectList"), // 这里paper会自动获取到NM.world.effect.MobEffect
BukkitReflectionUtils.assembleMCClass("world.effect.MobEffect") // 如果插件是mojmap就会走这个
BukkitReflectionUtils.assembleMCClass("world.effect.MobEffect") // paper柠檬酸了
)
);
@@ -1125,7 +1125,7 @@ public final class CoreReflections {
public static final Class<?> clazz$Fluid = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("world.level.material.FluidType"), // 这里paper会自动获取到NM.world.level.material.Fluid
BukkitReflectionUtils.assembleMCClass("world.level.material.Fluid") // 如果插件是mojmap就会走这个
BukkitReflectionUtils.assembleMCClass("world.level.material.Fluid") // paper柠檬酸了
)
);
@@ -3133,9 +3133,9 @@ public final class CoreReflections {
);
public static final Class<?> clazz$ChunkMap = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"server.level.PlayerChunkMap",
"server.level.ChunkMap"
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("server.level.PlayerChunkMap"), // 这里paper会自动获取到NM.server.level.ChunkMap
BukkitReflectionUtils.assembleMCClass("server.level.ChunkMap") // paper柠檬酸了
)
);
@@ -3238,7 +3238,7 @@ public final class CoreReflections {
public static final Class<?> clazz$ResourceManager = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("server.packs.resources.IResourceManager"), // 这里paper会自动获取到NM.server.packs.resources.ResourceManager
BukkitReflectionUtils.assembleMCClass("server.packs.resources.ResourceManager") // 如果插件是mojmap就会走这个
BukkitReflectionUtils.assembleMCClass("server.packs.resources.ResourceManager") // paper柠檬酸了
)
);
@@ -4536,4 +4536,16 @@ public final class CoreReflections {
public static final Method method$DismountHelper$canDismountTo1 = requireNonNull(
ReflectionUtils.getStaticMethod(clazz$DismountHelper, boolean.class, clazz$CollisionGetter, clazz$Vec3, clazz$LivingEntity, clazz$Pose)
);
public static final Class<?> clazz$WorldGenContext = MiscUtils.requireNonNullIf(
ReflectionUtils.getClazz(BukkitReflectionUtils.assembleMCClass("world.level.chunk.status.WorldGenContext")), VersionHelper.isOrAbove1_20_5()
);
public static final Field field$ChunkMap$worldGenContext = MiscUtils.requireNonNullIf(
ReflectionUtils.getDeclaredField(clazz$ChunkMap, clazz$WorldGenContext, 0), VersionHelper.isOrAbove1_20_5()
);
public static final Field field$ChunkMap$chunkGenerator = MiscUtils.requireNonNullIf(
ReflectionUtils.getDeclaredField(clazz$ChunkMap, clazz$ChunkGenerator, 0), !VersionHelper.isOrAbove1_20_5()
);
}

View File

@@ -1720,4 +1720,10 @@ public final class NetworkReflections {
"network.protocol.game.ClientboundBlockEntityDataPacket"
)
);
public static final Field field$ClientboundUpdateTagsPacket$tags = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$ClientboundUpdateTagsPacket, Map.class, 0
)
);
}

View File

@@ -63,32 +63,34 @@ public final class EntityUtils {
BlockPos pos = new BlockPos(MiscUtils.fastFloor(x), MiscUtils.fastFloor(y), MiscUtils.fastFloor(z));
try {
double floorHeight = (double) CoreReflections.method$BlockGetter$getBlockFloorHeight.invoke(serverLevel, LocationUtils.toBlockPos(pos));
if (pos.y() + floorHeight > y + 0.75) {
if (pos.y() + floorHeight > y + 0.75 || !isBlockFloorValid(floorHeight)) {
floorHeight = (double) CoreReflections.method$BlockGetter$getBlockFloorHeight.invoke(serverLevel, LocationUtils.toBlockPos(pos.below()));
if (pos.y() + floorHeight - 1 < y - 0.75 || !isBlockFloorValid(floorHeight)) {
continue;
}
floorHeight -= 1;
}
Object aabb = CoreReflections.method$LivingEntity$getLocalBoundsForPose.invoke(serverPlayer, pose);
Object vec3 = FastNMS.INSTANCE.constructor$Vec3(x, pos.y() + floorHeight, z);
Object newAABB = FastNMS.INSTANCE.method$AABB$move(aabb, vec3);
boolean canDismount = (boolean) CoreReflections.method$DismountHelper$canDismountTo0.invoke(null, serverLevel, serverPlayer, newAABB);
if (!canDismount) {
continue;
}
if (isBlockFloorValid(floorHeight)) {
Object aabb = CoreReflections.method$LivingEntity$getLocalBoundsForPose.invoke(serverPlayer, pose);
Object vec3 = FastNMS.INSTANCE.constructor$Vec3(x, pos.y() + floorHeight, z);
Object newAABB = FastNMS.INSTANCE.method$AABB$move(aabb, vec3);
boolean canDismount = (boolean) CoreReflections.method$DismountHelper$canDismountTo0.invoke(null, serverLevel, serverPlayer, newAABB);
if (!canDismount) {
continue;
}
if (!FastNMS.INSTANCE.checkEntityCollision(serverLevel, List.of(newAABB))) {
continue;
}
if (VersionHelper.isFolia()) {
player.teleportAsync(new Location(player.getWorld(), x, pos.y() + floorHeight, z, player.getYaw(), player.getPitch()));
} else {
player.teleport(new Location(player.getWorld(), x, pos.y() + floorHeight, z, player.getYaw(), player.getPitch()));
}
if (pose == CoreReflections.instance$Pose$STANDING) {
player.setPose(Pose.STANDING);
} else if (pose == CoreReflections.instance$Pose$CROUCHING) {
player.setPose(Pose.SNEAKING);
} else if (pose == CoreReflections.instance$Pose$SWIMMING) {
player.setPose(Pose.SWIMMING);
}
if (!FastNMS.INSTANCE.checkEntityCollision(serverLevel, List.of(newAABB))) {
continue;
}
if (VersionHelper.isFolia()) {
player.teleportAsync(new Location(player.getWorld(), x, pos.y() + floorHeight, z, player.getYaw(), player.getPitch()));
} else {
player.teleport(new Location(player.getWorld(), x, pos.y() + floorHeight, z, player.getYaw(), player.getPitch()));
}
if (pose == CoreReflections.instance$Pose$STANDING) {
player.setPose(Pose.STANDING);
} else if (pose == CoreReflections.instance$Pose$CROUCHING) {
player.setPose(Pose.SNEAKING);
} else if (pose == CoreReflections.instance$Pose$SWIMMING) {
player.setPose(Pose.SWIMMING);
}
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);

View File

@@ -34,6 +34,7 @@ import org.bukkit.attribute.AttributeInstance;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Directional;
import org.bukkit.block.data.Levelled;
import org.bukkit.block.data.Lightable;
import org.bukkit.block.data.type.*;
@@ -714,6 +715,37 @@ public final class InteractUtils {
registerInteraction(BlockKeys.BAMBOO_WALL_HANGING_SIGN, (player, item, blockState, result) -> true);
registerInteraction(BlockKeys.CRIMSON_WALL_HANGING_SIGN, (player, item, blockState, result) -> true);
registerInteraction(BlockKeys.WARPED_WALL_HANGING_SIGN, (player, item, blockState, result) -> true);
// 展示柜
registerInteraction(BlockKeys.OAK_SHELF, (player, item, blockState, result) -> blockState instanceof Directional directional && DirectionUtils.toBlockFace(result.getDirection()).equals(directional.getFacing()));
registerInteraction(BlockKeys.SPRUCE_SHELF, (player, item, blockState, result) -> blockState instanceof Directional directional && DirectionUtils.toBlockFace(result.getDirection()).equals(directional.getFacing()));
registerInteraction(BlockKeys.BIRCH_SHELF, (player, item, blockState, result) -> blockState instanceof Directional directional && DirectionUtils.toBlockFace(result.getDirection()).equals(directional.getFacing()));
registerInteraction(BlockKeys.JUNGLE_SHELF, (player, item, blockState, result) -> blockState instanceof Directional directional && DirectionUtils.toBlockFace(result.getDirection()).equals(directional.getFacing()));
registerInteraction(BlockKeys.ACACIA_SHELF, (player, item, blockState, result) -> blockState instanceof Directional directional && DirectionUtils.toBlockFace(result.getDirection()).equals(directional.getFacing()));
registerInteraction(BlockKeys.DARK_OAK_SHELF, (player, item, blockState, result) -> blockState instanceof Directional directional && DirectionUtils.toBlockFace(result.getDirection()).equals(directional.getFacing()));
registerInteraction(BlockKeys.MANGROVE_SHELF, (player, item, blockState, result) -> blockState instanceof Directional directional && DirectionUtils.toBlockFace(result.getDirection()).equals(directional.getFacing()));
registerInteraction(BlockKeys.CHERRY_SHELF, (player, item, blockState, result) -> blockState instanceof Directional directional && DirectionUtils.toBlockFace(result.getDirection()).equals(directional.getFacing()));
registerInteraction(BlockKeys.PALE_OAK_SHELF, (player, item, blockState, result) -> blockState instanceof Directional directional && DirectionUtils.toBlockFace(result.getDirection()).equals(directional.getFacing()));
registerInteraction(BlockKeys.BAMBOO_SHELF, (player, item, blockState, result) -> blockState instanceof Directional directional && DirectionUtils.toBlockFace(result.getDirection()).equals(directional.getFacing()));
registerInteraction(BlockKeys.CRIMSON_SHELF, (player, item, blockState, result) -> blockState instanceof Directional directional && DirectionUtils.toBlockFace(result.getDirection()).equals(directional.getFacing()));
registerInteraction(BlockKeys.WARPED_SHELF, (player, item, blockState, result) -> blockState instanceof Directional directional && DirectionUtils.toBlockFace(result.getDirection()).equals(directional.getFacing()));
// 铜傀儡雕像
registerInteraction(BlockKeys.COPPER_GOLEM_STATUE, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.EXPOSED_COPPER_GOLEM_STATUE, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.WEATHERED_COPPER_GOLEM_STATUE, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.OXIDIZED_COPPER_GOLEM_STATUE, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.WAXED_COPPER_GOLEM_STATUE, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.WAXED_EXPOSED_COPPER_GOLEM_STATUE, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.WAXED_WEATHERED_COPPER_GOLEM_STATUE, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.WAXED_OXIDIZED_COPPER_GOLEM_STATUE, ((player, item, blockData, result) -> true));
// 铜箱子
registerInteraction(BlockKeys.COPPER_CHEST, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.EXPOSED_COPPER_CHEST, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.WEATHERED_COPPER_CHEST, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.OXIDIZED_COPPER_CHEST, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.WAXED_COPPER_CHEST, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.WAXED_EXPOSED_COPPER_CHEST, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.WAXED_WEATHERED_COPPER_CHEST, ((player, item, blockData, result) -> true));
registerInteraction(BlockKeys.WAXED_OXIDIZED_COPPER_CHEST, ((player, item, blockData, result) -> true));
}
static {

View File

@@ -5,6 +5,7 @@ import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.core.util.FriendlyByteBuf;
import net.momirealms.craftengine.core.util.MarkedHashMap;
import java.util.ArrayList;
import java.util.HashMap;
@@ -15,6 +16,9 @@ public final class TagUtils {
private TagUtils() {}
public record TagEntry(int id, List<String> tags) {
}
/**
* 构建模拟标签更新数据包(用于向客户端添加虚拟标签)
*
@@ -39,11 +43,9 @@ public final class TagUtils {
*
* @return 可发送给客户端的 ClientboundUpdateTagsPacket 数据包对象
*/
@SuppressWarnings("unchecked")
public static Object createUpdateTagsPacket(Map<Object, List<TagEntry>> tags) {
Map<Object, Object> registriesNetworkPayload = (Map<Object, Object>) FastNMS.INSTANCE.method$TagNetworkSerialization$serializeTagsToNetwork();
Map<Object, Object> modified = new HashMap<>();
for (Map.Entry<Object, Object> payload : registriesNetworkPayload.entrySet()) {
public static Object createUpdateTagsPacket(Map<Object, List<TagEntry>> tags, Map<Object, Object> existingTags) {
Map<Object, Object> modified = new MarkedHashMap<>();
for (Map.Entry<Object, Object> payload : existingTags.entrySet()) {
List<TagEntry> overrides = tags.get(payload.getKey());
if (overrides == null || overrides.isEmpty()) {
modified.put(payload.getKey(), payload.getValue());
@@ -83,7 +85,4 @@ public final class TagUtils {
}
return FastNMS.INSTANCE.constructor$ClientboundUpdateTagsPacket(modified);
}
public record TagEntry(int id, List<String> tags) {
}
}

View File

@@ -92,10 +92,12 @@ public class BukkitWorldManager implements WorldManager, Listener {
public void delayedInit() {
// load loaded chunks
for (World world : Bukkit.getWorlds()) {
BukkitWorld wrappedWorld = new BukkitWorld(world);
try {
CEWorld ceWorld = this.worlds.computeIfAbsent(world.getUID(), k -> new BukkitCEWorld(new BukkitWorld(world), this.storageAdaptor));
CEWorld ceWorld = this.worlds.computeIfAbsent(world.getUID(), k -> new BukkitCEWorld(wrappedWorld, this.storageAdaptor));
injectChunkGenerator(ceWorld);
for (Chunk chunk : world.getLoadedChunks()) {
handleChunkLoad(ceWorld, chunk);
handleChunkLoad(ceWorld, chunk, false);
}
ceWorld.setTicking(true);
} catch (Exception e) {
@@ -142,8 +144,9 @@ public class BukkitWorldManager implements WorldManager, Listener {
CEWorld ceWorld = new BukkitCEWorld(world, this.storageAdaptor);
this.worlds.put(uuid, ceWorld);
this.resetWorldArray();
this.injectChunkGenerator(ceWorld);
for (Chunk chunk : ((World) world.platformWorld()).getLoadedChunks()) {
handleChunkLoad(ceWorld, chunk);
handleChunkLoad(ceWorld, chunk, false);
}
ceWorld.setTicking(true);
}
@@ -154,12 +157,20 @@ public class BukkitWorldManager implements WorldManager, Listener {
if (this.worlds.containsKey(uuid)) return;
this.worlds.put(uuid, world);
this.resetWorldArray();
this.injectChunkGenerator(world);
for (Chunk chunk : ((World) world.world().platformWorld()).getLoadedChunks()) {
handleChunkLoad(world, chunk);
handleChunkLoad(world, chunk, false);
}
world.setTicking(true);
}
private void injectChunkGenerator(CEWorld world) {
Object serverLevel = world.world.serverWorld();
Object serverChunkCache = FastNMS.INSTANCE.method$ServerLevel$getChunkSource(serverLevel);
Object chunkMap = FastNMS.INSTANCE.field$ServerChunkCache$chunkMap(serverChunkCache);
FastNMS.INSTANCE.injectedWorldGen(world, chunkMap);
}
@Override
public CEWorld createWorld(net.momirealms.craftengine.core.world.World world, WorldDataStorage storage) {
return new BukkitCEWorld(world, storage);
@@ -219,7 +230,7 @@ public class BukkitWorldManager implements WorldManager, Listener {
if (world == null) {
return;
}
handleChunkLoad(world, event.getChunk());
handleChunkLoad(world, event.getChunk(), event.isNewChunk());
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
@@ -273,14 +284,42 @@ public class BukkitWorldManager implements WorldManager, Listener {
FastNMS.INSTANCE.method$LevelChunk$markUnsaved(levelChunk);
}
ceChunk.unload();
ceChunk.deactivateAllBlockEntities();
}
}
private void handleChunkLoad(CEWorld ceWorld, Chunk chunk) {
public void handleChunkGenerate(CEWorld ceWorld, ChunkPos chunkPos, Object chunkAccess) {
Object[] sections = FastNMS.INSTANCE.method$ChunkAccess$getSections(chunkAccess);
CEChunk ceChunk;
try {
ceChunk = ceWorld.worldDataStorage().readNewChunkAt(ceWorld, chunkPos);
CESection[] ceSections = ceChunk.sections();
synchronized (sections) {
for (int i = 0; i < ceSections.length; i++) {
CESection ceSection = ceSections[i];
Object section = sections[i];
int finalI = i;
WorldStorageInjector.injectLevelChunkSection(section, ceSection, ceChunk, new SectionPos(chunkPos.x, ceChunk.sectionY(i), chunkPos.z),
(injected) -> sections[finalI] = injected);
}
}
ceChunk.load();
} catch (IOException e) {
this.plugin.logger().warn("Failed to read new chunk at " + chunkPos.x + " " + chunkPos.z, e);
}
}
private void handleChunkLoad(CEWorld ceWorld, Chunk chunk, boolean isNew) {
int chunkX = chunk.getX();
int chunkZ = chunk.getZ();
ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
if (ceWorld.isChunkLoaded(chunkPos.longKey)) return;
CEChunk chunkAtIfLoaded = ceWorld.getChunkAtIfLoaded(chunkPos.longKey);
if (chunkAtIfLoaded != null) {
if (isNew) {
chunkAtIfLoaded.activateAllBlockEntities();
}
return;
}
CEChunk ceChunk;
try {
ceChunk = ceWorld.worldDataStorage().readChunkAt(ceWorld, chunkPos);
@@ -382,5 +421,6 @@ public class BukkitWorldManager implements WorldManager, Listener {
return;
}
ceChunk.load();
ceChunk.activateAllBlockEntities();
}
}

View File

@@ -81,6 +81,15 @@ resource-pack:
suffix: "minecraft/atlases"
resolution:
type: merge_atlas
- term:
type: any_of
terms:
- type: exact
path: "assets/minecraft/font/default.json"
- type: exact
path: "assets/minecraft/font/uniform.json"
resolution:
type: merge_font
# Validate if there is any error in the resource pack, such as missing textures or models
# If your resource pack is compliant with the standard, you can disable validation to improve the resource pack generation speed.
validation:

View File

@@ -2,7 +2,7 @@ items:
default:amethyst_torch:
material: nether_brick
data:
item-name: <!i><i18n:item.amethyst_torch>
item-name: <!i><l10n:item.amethyst_torch>
model:
type: minecraft:model
path: minecraft:item/custom/amethyst_torch
@@ -20,7 +20,7 @@ items:
default:amethyst_standing_torch:
material: nether_brick
data:
item-name: <!i><i18n:item.amethyst_torch>
item-name: <!i><l10n:item.amethyst_torch>
model:
type: minecraft:model
path: minecraft:block/custom/amethyst_torch
@@ -31,7 +31,7 @@ items:
default:amethyst_wall_torch:
material: nether_brick
data:
item-name: <!i><i18n:item.amethyst_torch>
item-name: <!i><l10n:item.amethyst_torch>
model:
type: minecraft:model
path: minecraft:block/custom/amethyst_wall_torch

View File

@@ -2,7 +2,7 @@ items:
default:chessboard_block:
material: nether_brick
data:
item-name: <!i><i18n:item.chessboard_block>
item-name: <!i><l10n:item.chessboard_block>
model:
type: minecraft:model
path: minecraft:item/custom/chessboard_block

View File

@@ -2,7 +2,7 @@ items:
default:chinese_lantern:
material: nether_brick
data:
item-name: <!i><i18n:item.chinese_lantern>
item-name: <!i><l10n:item.chinese_lantern>
model:
type: minecraft:model
path: minecraft:item/custom/chinese_lantern

View File

@@ -2,7 +2,7 @@ items:
default:copper_coil:
material: nether_brick
data:
item-name: <!i><i18n:item.copper_coil>
item-name: <!i><l10n:item.copper_coil>
model:
type: minecraft:model
path: minecraft:item/custom/copper_coil

View File

@@ -2,7 +2,7 @@ items:
default:ender_pearl_flower_seeds:
material: nether_brick
data:
item-name: <!i><i18n:item.ender_pearl_flower_seeds>
item-name: <!i><l10n:item.ender_pearl_flower_seeds>
model:
template: default:model/simplified_generated
arguments:

View File

@@ -2,7 +2,7 @@ items:
default:fairy_flower:
material: nether_brick
data:
item-name: <!i><i18n:item.fairy_flower>
item-name: <!i><l10n:item.fairy_flower>
model:
template: default:model/simplified_generated
arguments:

View File

@@ -2,7 +2,7 @@ items:
default:flame_cane:
material: nether_brick
data:
item-name: <!i><i18n:item.flame_cane>
item-name: <!i><l10n:item.flame_cane>
model:
template: default:model/simplified_generated
arguments:

View File

@@ -2,7 +2,7 @@ items:
default:gunpowder_block:
material: nether_brick
data:
item-name: <!i><i18n:item.gunpowder_block>
item-name: <!i><l10n:item.gunpowder_block>
model:
type: minecraft:model
path: minecraft:item/custom/gunpowder_block
@@ -37,7 +37,7 @@ items:
default:solid_gunpowder_block:
material: nether_brick
data:
item-name: <!i><i18n:item.solid_gunpowder_block>
item-name: <!i><l10n:item.solid_gunpowder_block>
model:
type: minecraft:model
path: minecraft:item/custom/solid_gunpowder_block

View File

@@ -2,7 +2,7 @@ items:
default:hami_melon_slice:
material: melon_slice
data:
item-name: <!i><i18n:item.hami_melon_slice>
item-name: <!i><l10n:item.hami_melon_slice>
$$>=1.20.5:
food:
nutrition: 2
@@ -20,7 +20,7 @@ items:
default:hami_melon:
material: nether_brick
data:
item-name: <!i><i18n:item.hami_melon>
item-name: <!i><l10n:item.hami_melon>
model:
path: minecraft:item/custom/hami_melon
generation:
@@ -31,7 +31,7 @@ items:
default:hami_melon_seeds:
material: nether_brick
data:
item-name: <!i><i18n:item.hami_melon_seeds>
item-name: <!i><l10n:item.hami_melon_seeds>
model:
template: default:model/simplified_generated
arguments:

View File

@@ -6,7 +6,7 @@ items:
arguments:
path: minecraft:item/custom/magma_fruit
data:
item-name: <!i><white><i18n:item.magma_fruit>
item-name: <!i><white><l10n:item.magma_fruit>
$$>=1.20.5:
food:
nutrition: 3

View File

@@ -2,7 +2,7 @@ items:
default:netherite_anvil:
material: nether_brick
data:
item-name: <!i><i18n:item.netherite_anvil>
item-name: <!i><l10n:item.netherite_anvil>
model:
type: minecraft:model
path: minecraft:item/custom/netherite_anvil

View File

@@ -8,7 +8,7 @@ items:
- minecraft:logs
- minecraft:logs_that_burn
data:
item-name: <!i><i18n:item.palm_log>
item-name: <!i><l10n:item.palm_log>
model:
type: minecraft:model
path: minecraft:item/custom/palm_log
@@ -40,7 +40,7 @@ items:
- minecraft:logs
- minecraft:logs_that_burn
data:
item-name: <!i><i18n:item.stripped_palm_log>
item-name: <!i><l10n:item.stripped_palm_log>
model:
type: minecraft:model
path: minecraft:item/custom/stripped_palm_log
@@ -69,7 +69,7 @@ items:
- minecraft:logs
- minecraft:logs_that_burn
data:
item-name: <!i><i18n:item.palm_wood>
item-name: <!i><l10n:item.palm_wood>
model:
type: minecraft:model
path: minecraft:item/custom/palm_wood
@@ -101,7 +101,7 @@ items:
- minecraft:logs
- minecraft:logs_that_burn
data:
item-name: <!i><i18n:item.stripped_palm_wood>
item-name: <!i><l10n:item.stripped_palm_wood>
model:
type: minecraft:model
path: minecraft:item/custom/stripped_palm_wood
@@ -129,7 +129,7 @@ items:
- minecraft:planks
- minecraft:wooden_tool_materials
data:
item-name: <!i><i18n:item.palm_planks>
item-name: <!i><l10n:item.palm_planks>
model:
type: minecraft:model
path: minecraft:item/custom/palm_planks
@@ -153,7 +153,7 @@ items:
settings:
fuel-time: 100
data:
item-name: <!i><i18n:item.palm_sapling>
item-name: <!i><l10n:item.palm_sapling>
lore:
- "<!i><gray>Requires the datapack tree configuration to function."
- "<!i><gray>If not configured, an oak tree will grow by default."
@@ -197,7 +197,7 @@ items:
default:palm_leaves:
material: oak_leaves
data:
item-name: <!i><i18n:item.palm_leaves>
item-name: <!i><l10n:item.palm_leaves>
block-state: default:palm_leaves[persistent=true,waterlogged=false,distance=7]
model:
type: minecraft:model
@@ -227,7 +227,7 @@ items:
default:palm_trapdoor:
material: nether_brick
data:
item-name: <!i><i18n:item.palm_trapdoor>
item-name: <!i><l10n:item.palm_trapdoor>
settings:
fuel-time: 300
model:
@@ -281,7 +281,7 @@ items:
default:palm_door:
material: nether_brick
data:
item-name: <!i><i18n:item.palm_door>
item-name: <!i><l10n:item.palm_door>
settings:
fuel-time: 200
model:
@@ -355,7 +355,7 @@ items:
default:palm_fence_gate:
material: nether_brick
data:
item-name: <!i><i18n:item.palm_fence_gate>
item-name: <!i><l10n:item.palm_fence_gate>
settings:
fuel-time: 300
model:
@@ -411,7 +411,7 @@ items:
default:palm_slab:
material: nether_brick
data:
item-name: <!i><i18n:item.palm_slab>
item-name: <!i><l10n:item.palm_slab>
settings:
fuel-time: 150
model:
@@ -462,7 +462,7 @@ items:
generation:
parent: minecraft:block/custom/palm_stairs
data:
item-name: <!i><i18n:item.palm_stairs>
item-name: <!i><l10n:item.palm_stairs>
settings:
fuel-time: 300
behavior:
@@ -511,7 +511,7 @@ items:
generation:
parent: minecraft:block/custom/palm_pressure_plate
data:
item-name: <!i><i18n:item.palm_pressure_plate>
item-name: <!i><l10n:item.palm_pressure_plate>
settings:
fuel-time: 300
behavior:
@@ -560,7 +560,7 @@ items#pfence:
default:palm_fence:
material: nether_brick
data:
item-name: <!i><i18n:item.palm_fence>
item-name: <!i><l10n:item.palm_fence>
model:
type: minecraft:model
path: minecraft:item/custom/palm_fence_inventory
@@ -632,7 +632,7 @@ items#button:
textures:
texture: minecraft:block/custom/palm_planks
data:
item-name: <!i><i18n:item.palm_button>
item-name: <!i><l10n:item.palm_button>
settings:
fuel-time: 100
behavior:

View File

@@ -2,7 +2,7 @@ items:
default:pebble:
material: nether_brick
data:
item-name: <!i><i18n:item.pebble>
item-name: <!i><l10n:item.pebble>
model:
template: default:model/simplified_generated
arguments:

View File

@@ -2,7 +2,7 @@ items:
default:reed:
material: nether_brick
data:
item-name: <!i><i18n:item.reed>
item-name: <!i><l10n:item.reed>
model:
template: default:model/simplified_generated
arguments:

View File

@@ -2,7 +2,7 @@ items:
default:safe_block:
material: nether_brick
data:
item-name: <!i><i18n:item.safe_block>
item-name: <!i><l10n:item.safe_block>
model:
type: minecraft:model
path: minecraft:item/custom/safe_block
@@ -31,7 +31,7 @@ items:
step: minecraft:block.stone.step
behavior:
type: simple_storage_block
title: "<i18n:item.safe_block>"
title: "<l10n:item.safe_block>"
rows: 1
sounds:
open: minecraft:block.iron_trapdoor.open
@@ -58,6 +58,7 @@ items:
front: minecraft:block/custom/safe_block_front
side: minecraft:block/custom/safe_block_side
top: minecraft:block/custom/safe_block_top
bottom: minecraft:block/custom/safe_block_bottom
east_open:
auto-state: note_block
model:
@@ -69,6 +70,7 @@ items:
front: minecraft:block/custom/safe_block_front_open
side: minecraft:block/custom/safe_block_side
top: minecraft:block/custom/safe_block_top
bottom: minecraft:block/custom/safe_block_bottom
north:
auto-state: note_block
model:

View File

@@ -2,7 +2,7 @@ items:
default:sleeper_sofa:
material: nether_brick
data:
item-name: <!i><i18n:item.sofa>
item-name: <!i><l10n:item.sofa>
model:
type: minecraft:model
path: minecraft:item/custom/sleeper_sofa
@@ -50,7 +50,7 @@ items:
default:sofa:
material: nether_brick
data:
item-name: <!i><i18n:item.sofa>
item-name: <!i><l10n:item.sofa>
model:
type: minecraft:model
path: minecraft:item/custom/sofa

View File

@@ -2,7 +2,7 @@ items:
default:table_lamp:
material: nether_brick
data:
item-name: <!i><i18n:item.table_lamp>
item-name: <!i><l10n:item.table_lamp>
model:
type: minecraft:model
path: minecraft:item/custom/table_lamp

View File

@@ -2,7 +2,7 @@ items:
default:topaz_ore:
material: nether_brick
data:
item-name: <!i><i18n:item.topaz_ore>
item-name: <!i><l10n:item.topaz_ore>
model:
type: minecraft:model
path: minecraft:item/custom/topaz_ore
@@ -14,7 +14,7 @@ items:
default:deepslate_topaz_ore:
material: nether_brick
data:
item-name: <!i><i18n:item.deepslate_topaz_ore>
item-name: <!i><l10n:item.deepslate_topaz_ore>
model:
type: minecraft:model
path: minecraft:item/custom/deepslate_topaz_ore
@@ -31,7 +31,7 @@ items:
- '#default:topaz_tools'
percent: 0.25
data:
item-name: <!i><#FF8C00><i18n:item.topaz>
item-name: <!i><#FF8C00><l10n:item.topaz>
model:
template: default:model/simplified_generated
arguments:

View File

@@ -1,16 +1,16 @@
categories:
default:default:
priority: 1
name: <!i><white><i18n:category.default.name></white>
name: <!i><white><l10n:category.default.name></white>
lore:
- <!i><gray><i18n:category.default.lore>
- <!i><gray><l10n:category.default.lore>
icon: default:topaz
list:
- '#default:palm_tree'
- '#default:topaz'
- '#default:misc'
default:palm_tree:
name: <!i><green><i18n:category.palm_tree></green>
name: <!i><green><l10n:category.palm_tree></green>
hidden: true
icon: default:palm_log
list:
@@ -30,7 +30,7 @@ categories:
- default:palm_pressure_plate
- default:palm_button
default:topaz:
name: <!i><#FF8C00><i18n:category.topaz></#FF8C00>
name: <!i><#FF8C00><l10n:category.topaz></#FF8C00>
hidden: true
icon: default:topaz
list:
@@ -51,7 +51,7 @@ categories:
- default:topaz_leggings
- default:topaz_boots
default:misc:
name: <!i><gray><i18n:category.misc></gray>
name: <!i><gray><l10n:category.misc></gray>
hidden: true
icon: default:chinese_lantern
list:

View File

@@ -1,6 +1,6 @@
templates:
default:emoji/basic:
content: <hover:show_text:'<i18n:emoji.tip>'><!shadow><white><arg:emoji></white></!shadow></hover>
content: <hover:show_text:'<l10n:emoji.tip>'><!shadow><white><arg:emoji></white></!shadow></hover>
emoji:
default:emoji_smiley:
template: default:emoji/basic

View File

@@ -2,7 +2,7 @@ items:
default:bench:
material: nether_brick
data:
item-name: <!i><i18n:item.bench>
item-name: <!i><l10n:item.bench>
model:
type: minecraft:model
path: minecraft:item/custom/bench

View File

@@ -2,7 +2,7 @@ items:
default:flower_basket:
material: nether_brick
data:
item-name: <!i><i18n:item.flower_basket>
item-name: <!i><l10n:item.flower_basket>
model:
template: default:model/simplified_generated
arguments:

View File

@@ -2,7 +2,7 @@ items:
default:wooden_chair:
material: nether_brick
data:
item-name: <!i><i18n:item.wooden_chair>
item-name: <!i><l10n:item.wooden_chair>
model:
type: minecraft:model
path: minecraft:item/custom/wooden_chair

View File

@@ -4,7 +4,7 @@ items:
$$<=1.21.1:
client-bound-material: leather_horse_armor
data:
item-name: <!i><white><i18n:item.cap>
item-name: <!i><white><l10n:item.cap>
unbreakable: true
remove-components:
- attribute_modifiers

View File

@@ -8,7 +8,7 @@ items:
asset-id: flame
wings: flame_elytra
data:
item-name: <!i><#FF8C00><i18n:item.flame_elytra>
item-name: <!i><#FF8C00><l10n:item.flame_elytra>
model:
template: default:model/simplified_elytra
arguments:

View File

@@ -2,7 +2,7 @@ templates:
default:armor/topaz:
material: chainmail_${part}
data:
item-name: <!i><#FF8C00><i18n:item.topaz_${part}>
item-name: <!i><#FF8C00><l10n:item.topaz_${part}>
tooltip-style: minecraft:topaz
settings:
tags:

View File

@@ -5,7 +5,7 @@ items:
tags:
- default:topaz_tools
data:
item-name: <!i><#FF8C00><i18n:item.topaz_rod>
item-name: <!i><#FF8C00><l10n:item.topaz_rod>
tooltip-style: minecraft:topaz
model:
template: default:model/simplified_fishing_rod_2d
@@ -18,7 +18,7 @@ items:
tags:
- default:topaz_tools
data:
item-name: <!i><#FF8C00><i18n:item.topaz_bow>
item-name: <!i><#FF8C00><l10n:item.topaz_bow>
tooltip-style: minecraft:topaz
model:
template: default:model/simplified_bow_2d
@@ -33,7 +33,7 @@ items:
tags:
- default:topaz_tools
data:
item-name: <!i><#FF8C00><i18n:item.topaz_crossbow>
item-name: <!i><#FF8C00><l10n:item.topaz_crossbow>
tooltip-style: minecraft:topaz
model:
template: default:model/simplified_crossbow_2d
@@ -50,7 +50,7 @@ items:
tags:
- default:topaz_tools
data:
item-name: <!i><#FF8C00><i18n:item.topaz_pickaxe>
item-name: <!i><#FF8C00><l10n:item.topaz_pickaxe>
tooltip-style: minecraft:topaz
max-damage: 64
model:
@@ -63,7 +63,7 @@ items:
tags:
- default:topaz_tools
data:
item-name: <!i><#FF8C00><i18n:item.topaz_axe>
item-name: <!i><#FF8C00><l10n:item.topaz_axe>
tooltip-style: minecraft:topaz
max-damage: 64
model:
@@ -76,7 +76,7 @@ items:
tags:
- default:topaz_tools
data:
item-name: <!i><#FF8C00><i18n:item.topaz_hoe>
item-name: <!i><#FF8C00><l10n:item.topaz_hoe>
tooltip-style: minecraft:topaz
max-damage: 64
model:
@@ -89,7 +89,7 @@ items:
tags:
- default:topaz_tools
data:
item-name: <!i><#FF8C00><i18n:item.topaz_shovel>
item-name: <!i><#FF8C00><l10n:item.topaz_shovel>
tooltip-style: minecraft:topaz
max-damage: 64
model:
@@ -102,7 +102,7 @@ items:
tags:
- default:topaz_tools
data:
item-name: <!i><#FF8C00><i18n:item.topaz_sword>
item-name: <!i><#FF8C00><l10n:item.topaz_sword>
tooltip-style: minecraft:topaz
max-damage: 64
model:
@@ -122,7 +122,7 @@ items:
tags:
- default:topaz_tools
data:
item-name: <!i><#FF8C00><i18n:item.topaz_trident>
item-name: <!i><#FF8C00><l10n:item.topaz_trident>
tooltip-style: minecraft:topaz
max-damage: 300
model:
@@ -163,7 +163,7 @@ items:
$$1.20.1~1.21.1: bow
$$1.21.2~1.21.3: honey_bottle
data:
item-name: <!i><#FF8C00><i18n:item.topaz_trident>
item-name: <!i><#FF8C00><l10n:item.topaz_trident>
components:
minecraft:max_damage: 300
$$>=1.21.2:

View File

@@ -2682,354 +2682,354 @@ templates#block_states:
state: ${base_block}[east=false,north=false,south=false,waterlogged=false,west=false]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
east=true,north=false,south=false,waterlogged=false,west=false:
state: ${base_block}[east=true,north=false,south=false,waterlogged=false,west=false]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 270
yaw: 270
east=false,north=true,south=false,waterlogged=false,west=false:
state: ${base_block}[east=false,north=true,south=false,waterlogged=false,west=false]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 180
yaw: 180
east=false,north=false,south=true,waterlogged=false,west=false:
state: ${base_block}[east=false,north=false,south=true,waterlogged=false,west=false]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 0
yaw: 0
east=false,north=false,south=false,waterlogged=false,west=true:
state: ${base_block}[east=false,north=false,south=false,waterlogged=false,west=true]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 90
yaw: 90
east=true,north=true,south=false,waterlogged=false,west=false:
state: ${base_block}[east=true,north=true,south=false,waterlogged=false,west=false]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 180
yaw: 180
- item: ${fence_side_item}
rotation: 270
yaw: 270
east=true,north=false,south=true,waterlogged=false,west=false:
state: ${base_block}[east=true,north=false,south=true,waterlogged=false,west=false]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 0
yaw: 0
- item: ${fence_side_item}
rotation: 270
yaw: 270
east=true,north=false,south=false,waterlogged=false,west=true:
state: ${base_block}[east=true,north=false,south=false,waterlogged=false,west=true]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 90
yaw: 90
- item: ${fence_side_item}
rotation: 270
yaw: 270
east=false,north=true,south=true,waterlogged=false,west=false:
state: ${base_block}[east=false,north=true,south=true,waterlogged=false,west=false]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 0
yaw: 0
- item: ${fence_side_item}
rotation: 180
yaw: 180
east=false,north=true,south=false,waterlogged=false,west=true:
state: ${base_block}[east=false,north=true,south=false,waterlogged=false,west=true]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 90
yaw: 90
- item: ${fence_side_item}
rotation: 180
yaw: 180
east=false,north=false,south=true,waterlogged=false,west=true:
state: ${base_block}[east=false,north=false,south=true,waterlogged=false,west=true]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 0
yaw: 0
- item: ${fence_side_item}
rotation: 90
yaw: 90
east=true,north=true,south=true,waterlogged=false,west=false:
state: ${base_block}[east=true,north=true,south=true,waterlogged=false,west=false]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 0
yaw: 0
- item: ${fence_side_item}
rotation: 180
yaw: 180
- item: ${fence_side_item}
rotation: 270
yaw: 270
east=true,north=true,south=false,waterlogged=false,west=true:
state: ${base_block}[east=true,north=true,south=false,waterlogged=false,west=true]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 90
yaw: 90
- item: ${fence_side_item}
rotation: 180
yaw: 180
- item: ${fence_side_item}
rotation: 270
yaw: 270
east=true,north=false,south=true,waterlogged=false,west=true:
state: ${base_block}[east=true,north=false,south=true,waterlogged=false,west=true]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 0
yaw: 0
- item: ${fence_side_item}
rotation: 90
yaw: 90
- item: ${fence_side_item}
rotation: 270
yaw: 270
east=false,north=true,south=true,waterlogged=false,west=true:
state: ${base_block}[east=false,north=true,south=true,waterlogged=false,west=true]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 0
yaw: 0
- item: ${fence_side_item}
rotation: 90
yaw: 90
- item: ${fence_side_item}
rotation: 180
yaw: 180
east=true,north=true,south=true,waterlogged=false,west=true:
state: ${base_block}[east=true,north=true,south=true,waterlogged=false,west=true]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 0
yaw: 0
- item: ${fence_side_item}
rotation: 90
yaw: 90
- item: ${fence_side_item}
rotation: 180
yaw: 180
- item: ${fence_side_item}
rotation: 270
yaw: 270
east=false,north=false,south=false,waterlogged=true,west=false:
state: ${base_block}[east=false,north=false,south=false,waterlogged=true,west=false]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
east=true,north=false,south=false,waterlogged=true,west=false:
state: ${base_block}[east=true,north=false,south=false,waterlogged=true,west=false]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 270
yaw: 270
east=false,north=true,south=false,waterlogged=true,west=false:
state: ${base_block}[east=false,north=true,south=false,waterlogged=true,west=false]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 180
yaw: 180
east=false,north=false,south=true,waterlogged=true,west=false:
state: ${base_block}[east=false,north=false,south=true,waterlogged=true,west=false]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 0
yaw: 0
east=false,north=false,south=false,waterlogged=true,west=true:
state: ${base_block}[east=false,north=false,south=false,waterlogged=true,west=true]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 90
yaw: 90
east=true,north=true,south=false,waterlogged=true,west=false:
state: ${base_block}[east=true,north=true,south=false,waterlogged=true,west=false]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 180
yaw: 180
- item: ${fence_side_item}
rotation: 270
yaw: 270
east=true,north=false,south=true,waterlogged=true,west=false:
state: ${base_block}[east=true,north=false,south=true,waterlogged=true,west=false]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 0
yaw: 0
- item: ${fence_side_item}
rotation: 270
yaw: 270
east=true,north=false,south=false,waterlogged=true,west=true:
state: ${base_block}[east=true,north=false,south=false,waterlogged=true,west=true]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 90
yaw: 90
- item: ${fence_side_item}
rotation: 270
yaw: 270
east=false,north=true,south=true,waterlogged=true,west=false:
state: ${base_block}[east=false,north=true,south=true,waterlogged=true,west=false]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 0
yaw: 0
- item: ${fence_side_item}
rotation: 180
yaw: 180
east=false,north=true,south=false,waterlogged=true,west=true:
state: ${base_block}[east=false,north=true,south=false,waterlogged=true,west=true]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 90
yaw: 90
- item: ${fence_side_item}
rotation: 180
yaw: 180
east=false,north=false,south=true,waterlogged=true,west=true:
state: ${base_block}[east=false,north=false,south=true,waterlogged=true,west=true]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 0
yaw: 0
- item: ${fence_side_item}
rotation: 90
yaw: 90
east=true,north=true,south=true,waterlogged=true,west=false:
state: ${base_block}[east=true,north=true,south=true,waterlogged=true,west=false]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 0
yaw: 0
- item: ${fence_side_item}
rotation: 180
yaw: 180
- item: ${fence_side_item}
rotation: 270
yaw: 270
east=true,north=true,south=false,waterlogged=true,west=true:
state: ${base_block}[east=true,north=true,south=false,waterlogged=true,west=true]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 90
yaw: 90
- item: ${fence_side_item}
rotation: 180
yaw: 180
- item: ${fence_side_item}
rotation: 270
yaw: 270
east=true,north=false,south=true,waterlogged=true,west=true:
state: ${base_block}[east=true,north=false,south=true,waterlogged=true,west=true]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 0
yaw: 0
- item: ${fence_side_item}
rotation: 90
yaw: 90
- item: ${fence_side_item}
rotation: 270
yaw: 270
east=false,north=true,south=true,waterlogged=true,west=true:
state: ${base_block}[east=false,north=true,south=true,waterlogged=true,west=true]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 0
yaw: 0
- item: ${fence_side_item}
rotation: 90
yaw: 90
- item: ${fence_side_item}
rotation: 180
yaw: 180
east=true,north=true,south=true,waterlogged=true,west=true:
state: ${base_block}[east=true,north=true,south=true,waterlogged=true,west=true]
entity-renderer:
- item: ${fence_post_item}
rotation: 180
yaw: 180
scale: 1.0003
translation: 0,0.0001,0
- item: ${fence_side_item}
rotation: 0
yaw: 0
- item: ${fence_side_item}
rotation: 90
yaw: 90
- item: ${fence_side_item}
rotation: 180
yaw: 180
- item: ${fence_side_item}
rotation: 270
yaw: 270
variants:
waterlogged=true:
settings:

View File

@@ -2,6 +2,4 @@ author: XiaoMoMi
version: 0.0.1
description: Fix broken vanilla armor
namespace: minecraft
enable:
$$>=1.21.2: false
$$<1.21.2: true
enable: false

View File

@@ -374,6 +374,7 @@ warning.config.loot_table.function.apply_bonus.missing_enchantment: "<yellow>Iss
warning.config.loot_table.function.apply_bonus.missing_formula: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, function 'apply_bonus' is missing the required 'formula' argument.</yellow>"
warning.config.loot_table.function.drop_exp.missing_count: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, function 'drop_exp' is missing the required 'count' argument.</yellow>"
warning.config.loot_table.function.set_count.missing_count: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, function 'set_count' is missing the required 'count' argument.</yellow>"
warning.config.loot_table.function.apply_data.missing_data: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, function 'apply_data' is missing the required 'data' argument.</yellow>"
warning.config.loot_table.entry.missing_type: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, one of the entries is missing the required 'type' argument.</yellow>"
warning.config.loot_table.entry.invalid_type: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, one of the entries is using an invalid entry type '<arg:2>'.</yellow>"
warning.config.loot_table.entry.exp.missing_count: "<yellow>Issue found in file <arg:0> - '<arg:1>' has a misconfigured loot table, entry 'exp' is missing the required 'count' argument.</yellow>"

View File

@@ -65,6 +65,9 @@ command.upload.failure.not_supported: "<red>当前托管模式 '<arg:0>' 不支
command.upload.on_progress: "<white>已开始上传进程. 检查控制台以获取详细信息</white>"
command.send_resource_pack.success.single: "<white>发送资源包给 <arg:0></white>"
command.send_resource_pack.success.multiple: "<white>发送资源包给 <arg:0> 个玩家</white>"
command.locale.set.failure: "<red>区域设置格式无效: <arg:0></red>"
command.locale.set.success: "<white>已为 <arg:1> 更新选定区域设置为 <arg:0></white>"
command.locale.unset.success: "<white>已清除 <arg:0> 的选定区域设置</white>"
warning.network.resource_pack.unverified_uuid: "<yellow>玩家 <arg:0> 使用未经服务器验证的 UUID (<arg:1>) 尝试请求获取资源包</yellow>"
warning.config.pack.duplicated_files: "<red>发现重复文件 请通过 config.yml 的 'resource-pack.duplicated-files-handler' 部分解决</red>"
warning.config.yaml.duplicated_key: "<red>在文件 <arg:0> 发现问题 - 在第<arg:2>行发现重复的键 '<arg:1>', 这可能会导致一些意料之外的问题</red>"
@@ -160,10 +163,10 @@ warning.config.recipe.result.post_processor.missing_type: "<yellow>在文件 <ar
warning.config.recipe.result.post_processor.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 配方 '<arg:1>' 使用了无效结果后处理器类型 '<arg:2>'</yellow>"
warning.config.translation.unknown_locale: "<yellow>在文件 <arg:0> 发现问题 - 未知的语言环境 '<arg:1>'</yellow>"
warning.config.template.duplicate: "<yellow>在文件 <arg:0> 发现问题 - 重复的模板 '<arg:1>' 请检查其他文件中是否存在相同配置</yellow>"
warning.config.template.invalid: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 使用了无效的模板 '<arg:2>'</yellow>"
warning.config.template.invalid: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 使用了无效的模板 '<arg:2>'</yellow>"
warning.config.template.argument.self_increase_int.invalid_range: "<yellow>在文件 <arg:0> 发现问题 - 模板 '<arg:1>' 在 'self_increase_int' 参数中使用了一个起始值 '<arg:2>' 大于终止值 '<arg:3>'</yellow>"
warning.config.template.argument.list.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 模板 '<arg:1>' 的 'list' 参数需要列表类型 但输入参数类型为 '<arg:2>'</yellow>"
warning.config.template.argument.missing_value: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 缺少了 '<arg:2>' 必要的模板参数值. 请使用 arguments 选项进行配置或为此参数设定默认值</yellow>"
warning.config.template.argument.missing_value: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 缺少了 '<arg:2>' 必要的模板参数值. 请使用 arguments 选项进行配置或为此参数设定默认值</yellow>"
warning.config.vanilla_loot.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 原版战利品 '<arg:1>' 缺少必需的 'type' 参数</yellow>"
warning.config.vanilla_loot.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 原版战利品 '<arg:1>' 使用了无效类型 '<arg:2>' 允许的类型: [<arg:3>]</yellow>"
warning.config.vanilla_loot.block.invalid_target: "<yellow>在文件 <arg:0> 发现问题 - 原版战利品 '<arg:1>' 中存在无效的方块目标 '<arg:2>'</yellow>"
@@ -184,6 +187,10 @@ warning.config.item.settings.invulnerable.invalid_damage_source: "<yellow>在文
warning.config.item.settings.equipment.missing_asset_id: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少 'equipment' 设置所需的 'asset-id' 参数</yellow>"
warning.config.item.settings.equipment.invalid_asset_id: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 为 'equipment' 设置配置了无效的 'asset-id'. 这可能是因为你没有创建装备配置或是错误地拼写了 asset-id</yellow>"
warning.config.item.settings.projectile.missing_item: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少 'projectile' 设置所需的 'item' 参数</yellow>"
warning.config.item.settings.craft_remainder.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少 'craft-remainder' 所需的 'type' 参数</yellow>"
warning.config.item.settings.craft_remainder.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 使用了无效的合成剩余物品类型 '<arg:2>'</yellow>"
warning.config.item.settings.craft_remainder.fixed.missing_item: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 的固定 'craft-remainder' 缺少所需的 'item' 参数</yellow>"
warning.config.item.settings.craft_remainder.recipe_based.missing_terms: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少基于配方的 'craft-remainder' 所需的 'terms' 参数</yellow>"
warning.config.item.data.attribute_modifiers.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少 'attribute-modifiers' 数据所需的 'type' 参数</yellow>"
warning.config.item.data.attribute_modifiers.missing_amount: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少 'attribute-modifiers' 数据所需的 'amount' 参数</yellow>"
warning.config.item.data.attribute_modifiers.missing_operation: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少 'attribute-modifiers' 数据所需的 'operation' 参数</yellow>"
@@ -263,8 +270,8 @@ warning.config.item.model.special.copper_golem_statue.missing_texture: "<yellow>
warning.config.item.updater.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少物品更新器必需的参数 'type'</yellow>"
warning.config.item.updater.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 在物品更新器中使用了无效的 'type' 参数值 '<arg:2>'</yellow>"
warning.config.item.updater.transmute.missing_material: "<yellow>在文件 <arg:0> 发现问题 - 物品 '<arg:1>' 缺少物品转换更新所需的 'material' 参数</yellow>"
warning.config.block_state_mapping.invalid_state: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 使用了无效的方块状态 '<arg:2>'</yellow>"
warning.config.block_state_mapping.conflict: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 无法将方块状态 <arg:2> 映射到方块状态 <arg:3>, 因为该状态已被映射到 <arg:4></yellow>"
warning.config.block_state_mapping.invalid_state: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 使用了无效的方块状态 '<arg:2>'</yellow>"
warning.config.block_state_mapping.conflict: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 无法将方块状态 <arg:2> 映射到方块状态 <arg:3>, 因为该状态已被映射到 <arg:4></yellow>"
warning.config.block.duplicate: "<yellow>在文件 <arg:0> 发现问题 - 重复的方块 '<arg:1>' 请检查其他文件中是否存在相同配置</yellow>"
warning.config.block.missing_state: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 缺少必需的 'state' 参数</yellow>"
warning.config.block.state.property.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 方块 '<arg:1>' 的属性 '<arg:2>' 缺少必需的 'type' 参数</yellow>"
@@ -454,14 +461,21 @@ warning.config.function.remove_cooldown.missing_id: "<yellow>在文件 <arg:0>
warning.config.function.mythic_mobs_skill.missing_skill: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 缺少 'mythic_mobs_skill' 函数必需的 'skill' 参数</yellow>"
warning.config.function.spawn_furniture.missing_furniture_id: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 缺少 'spawn_furniture' 函数必需的 'furniture-id' 参数</yellow>"
warning.config.function.replace_furniture.missing_furniture_id: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 缺少 'replace_furniture' 函数必需的 'furniture-id' 参数</yellow>"
warning.config.function.teleport.missing_x: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 缺少 'teleport' 函数所需的 'x' 参数</yellow>"
warning.config.function.teleport.missing_y: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 缺少 'teleport' 函数所需的 'y' 参数</yellow>"
warning.config.function.teleport.missing_z: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 缺少 'teleport' 函数所需的 'z' 参数</yellow>"
warning.config.function.set_variable.missing_name: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 缺少 'set_variable' 函数所需的 'name' 参数</yellow>"
warning.config.function.set_variable.missing_value: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 缺少 'set_variable' 函数所需的 'number' 或 'text' 参数</yellow>"
warning.config.function.toast.missing_toast: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 缺少 'toast' 函数所需的 'toast' 参数</yellow>"
warning.config.function.toast.missing_icon: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 缺少 'toast' 函数所需的 'icon' 参数</yellow>"
warning.config.function.toast.invalid_advancement_type: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 为 'toast' 函数使用了无效的进度类型 '<arg:2>'. 允许的类型: [<arg:3>]</yellow>"
warning.config.function.teleport.missing_x: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 缺少 'teleport' 函数所需的 'x' 参数</yellow>"
warning.config.function.teleport.missing_y: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 缺少 'teleport' 函数所需的 'y' 参数</yellow>"
warning.config.function.teleport.missing_z: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 缺少 'teleport' 函数所需的 'z' 参数</yellow>"
warning.config.function.set_variable.missing_name: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 缺少 'set_variable' 函数所需的 'name' 参数</yellow>"
warning.config.function.set_variable.missing_value: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 缺少 'set_variable' 函数所需的 'number' 或 'text' 参数</yellow>"
warning.config.function.toast.missing_toast: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 缺少 'toast' 函数所需的 'toast' 参数</yellow>"
warning.config.function.toast.missing_icon: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 缺少 'toast' 函数所需的 'icon' 参数</yellow>"
warning.config.function.toast.invalid_advancement_type: "<yellow>在文件 <arg:0> 发现问题 - 配置 '<arg:1>' 为 'toast' 函数使用了无效的进度类型 '<arg:2>'. 允许的类型: [<arg:3>]</yellow>"
warning.config.function.merchant_trade.missing_offers: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 缺少 'merchant_trade' 函数所需的 'offers' 参数</yellow>"
warning.config.function.merchant_trade.offer.missing_cost_1: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 缺少'merchant_trade' 函数所需的 'cost-1' 参数</yellow>"
warning.config.function.merchant_trade.offer.missing_result: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 缺少'merchant_trade' 函数所需的 'result' 参数</yellow>"
warning.config.function.when.missing_source: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 缺少 'when' 函数所需的 'source' 参数</yellow>"
warning.config.function.if_else.missing_rules: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 缺少 'if_else' 函数所需的 'rules' 参数</yellow>"
warning.config.function.update_block_property.missing_properties: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 缺少 'update_block_property' 函数所需的 'properties' 参数</yellow>"
warning.config.function.transform_block.missing_block: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 缺少 'transform_block' 函数所需的 'block' 参数</yellow>"
warning.config.selector.missing_type: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 缺少选择器必需的 'type' 参数</yellow>"
warning.config.selector.invalid_type: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 使用了无效的选择器类型 '<arg:2>'</yellow>"
warning.config.selector.invalid_target: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 使用了无效的选择器目标 '<arg:2>'</yellow>"
@@ -474,6 +488,7 @@ warning.config.resource_pack.generation.missing_item_model: "<yellow>物品'<arg
warning.config.resource_pack.generation.missing_block_model: "<yellow>方块状态'<arg:0>'缺少模型文件: '<arg:1>'</yellow>"
warning.config.resource_pack.generation.missing_parent_model: "<yellow>模型'<arg:0>'找不到父级模型文件: '<arg:1>'</yellow>"
warning.config.resource_pack.generation.missing_equipment_texture: "<yellow>装备 '<arg:0>' 缺少纹理 '<arg:1>'</yellow>"
warning.config.resource_pack.generation.missing_sound: "<yellow>声音事件 '<arg:0>' 缺少 ogg 文件 '<arg:1>'</yellow>"
warning.config.resource_pack.generation.texture_not_in_atlas: "<yellow>纹理'<arg:0>'不在图集内. 你需要将纹理路径或文件夹前缀添加到图集内, 或者启用 config.yml 中的 'fix-atlas' 选项</yellow>"
warning.config.resource_pack.invalid_overlay_format: "<yellow>在 config.yml 的 'resource-pack.overlay-format' 处发现问题 - 无效的overlay格式 '<arg:0>'. Overlay格式必须包含占位符 '{version}'</yellow>"
warning.config.equipment.duplicate: "<yellow>在文件 <arg:0> 发现问题 - 重复的装备配置 '<arg:1>'. 请检查其他文件中是否存在相同配置</yellow>"

View File

@@ -143,7 +143,7 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem
@Override
public void delayedLoad() {
this.initSuggestions();
this.resendTags();
this.updateTags();
this.processSounds();
this.clearCache();
}
@@ -232,7 +232,7 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem
public abstract BlockBehavior createBlockBehavior(CustomBlock customBlock, List<Map<String, Object>> behaviorConfig);
protected abstract void resendTags();
protected abstract void updateTags();
protected abstract boolean isVanillaBlock(Key id);

View File

@@ -330,6 +330,37 @@ public final class BlockKeys {
public static final Key CHERRY_SAPLING = Key.of("minecraft:cherry_sapling");
public static final Key PALE_OAK_SAPLING = Key.of("minecraft:pale_oak_sapling");
public static final Key OAK_SHELF = Key.of("minecraft:oak_shelf");
public static final Key SPRUCE_SHELF = Key.of("minecraft:spruce_shelf");
public static final Key BIRCH_SHELF = Key.of("minecraft:birch_shelf");
public static final Key JUNGLE_SHELF = Key.of("minecraft:jungle_shelf");
public static final Key ACACIA_SHELF = Key.of("minecraft:acacia_shelf");
public static final Key DARK_OAK_SHELF = Key.of("minecraft:dark_oak_shelf");
public static final Key MANGROVE_SHELF = Key.of("minecraft:mangrove_shelf");
public static final Key CHERRY_SHELF = Key.of("minecraft:cherry_shelf");
public static final Key PALE_OAK_SHELF = Key.of("minecraft:pale_oak_shelf");
public static final Key BAMBOO_SHELF = Key.of("minecraft:bamboo_shelf");
public static final Key CRIMSON_SHELF = Key.of("minecraft:crimson_shelf");
public static final Key WARPED_SHELF = Key.of("minecraft:warped_shelf");
public static final Key COPPER_GOLEM_STATUE = Key.of("minecraft:copper_golem_statue");
public static final Key EXPOSED_COPPER_GOLEM_STATUE = Key.of("minecraft:exposed_copper_golem_statue");
public static final Key WEATHERED_COPPER_GOLEM_STATUE = Key.of("minecraft:weathered_copper_golem_statue");
public static final Key OXIDIZED_COPPER_GOLEM_STATUE = Key.of("minecraft:oxidized_copper_golem_statue");
public static final Key WAXED_COPPER_GOLEM_STATUE = Key.of("minecraft:waxed_copper_golem_statue");
public static final Key WAXED_EXPOSED_COPPER_GOLEM_STATUE = Key.of("minecraft:waxed_exposed_copper_golem_statue");
public static final Key WAXED_WEATHERED_COPPER_GOLEM_STATUE = Key.of("minecraft:waxed_weathered_copper_golem_statue");
public static final Key WAXED_OXIDIZED_COPPER_GOLEM_STATUE = Key.of("minecraft:waxed_oxidized_copper_golem_statue");
public static final Key COPPER_CHEST = Key.of("minecraft:copper_chest");
public static final Key EXPOSED_COPPER_CHEST = Key.of("minecraft:exposed_copper_chest");
public static final Key WEATHERED_COPPER_CHEST = Key.of("minecraft:weathered_copper_chest");
public static final Key OXIDIZED_COPPER_CHEST = Key.of("minecraft:oxidized_copper_chest");
public static final Key WAXED_COPPER_CHEST = Key.of("minecraft:waxed_copper_chest");
public static final Key WAXED_EXPOSED_COPPER_CHEST = Key.of("minecraft:waxed_exposed_copper_chest");
public static final Key WAXED_WEATHERED_COPPER_CHEST = Key.of("minecraft:waxed_weathered_copper_chest");
public static final Key WAXED_OXIDIZED_COPPER_CHEST = Key.of("minecraft:waxed_oxidized_copper_chest");
public static final Key[] BUTTONS = new Key[]{
OAK_BUTTON, SPRUCE_BUTTON, BIRCH_BUTTON, JUNGLE_BUTTON, ACACIA_BUTTON, DARK_OAK_BUTTON, MANGROVE_BUTTON, CHERRY_BUTTON,
PALE_OAK_BUTTON, BAMBOO_BUTTON, CRIMSON_BUTTON, WARPED_BUTTON, STONE_BUTTON, POLISHED_BLACKSTONE_BUTTON

View File

@@ -14,25 +14,37 @@ public class ConstantBlockEntityRenderer {
public void show(Player player) {
for (BlockEntityElement element : this.elements) {
element.show(player);
if (element != null) {
element.show(player);
}
}
}
public void hide(Player player) {
for (BlockEntityElement element : this.elements) {
element.hide(player);
if (element != null) {
element.hide(player);
}
}
}
public void deactivate() {
for (BlockEntityElement element : this.elements) {
element.deactivate();
if (element != null) {
element.deactivate();
}
}
}
public void activate() {
for (BlockEntityElement element : this.elements) {
element.activate();
if (element != null) {
element.activate();
}
}
}
public BlockEntityElement[] elements() {
return this.elements;
}
}

View File

@@ -10,6 +10,8 @@ public interface BlockEntityElement {
void hide(Player player);
default void transform(Player player) {}
default void deactivate() {}
default void activate() {}

View File

@@ -6,4 +6,10 @@ import net.momirealms.craftengine.core.world.World;
public interface BlockEntityElementConfig<E extends BlockEntityElement> {
E create(World world, BlockPos pos);
default E create(World world, BlockPos pos, E previous) {
return null;
}
Class<E> elementClass();
}

View File

@@ -13,6 +13,7 @@ import java.util.Optional;
public abstract class BlockEntityElementConfigs {
public static final Key ITEM_DISPLAY = Key.of("craftengine:item_display");
public static final Key TEXT_DISPLAY = Key.of("craftengine:text_display");
public static final Key ITEM = Key.of("craftengine:item");
public static void register(Key key, BlockEntityElementConfigFactory type) {
((WritableRegistry<BlockEntityElementConfigFactory>) BuiltInRegistries.BLOCK_ENTITY_ELEMENT_TYPE)

View File

@@ -4,6 +4,7 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.util.HorizontalDirection;
import net.momirealms.craftengine.core.util.SegmentedAngle;
import net.momirealms.sparrow.nbt.Tag;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -33,7 +34,7 @@ public abstract class Property<T extends Comparable<T>> {
Property<Direction> directionProperty = (Property<Direction>) property;
return (context, state) -> state.with(directionProperty, context.getNearestLookingDirection().opposite());
} else {
throw new IllegalArgumentException("Unsupported property type used in hard-coded `facing` property: " + property.valueClass());
return (context, state) -> state;
}
}));
HARD_CODED_PLACEMENTS.put("facing_clockwise", (property -> {
@@ -41,13 +42,26 @@ public abstract class Property<T extends Comparable<T>> {
Property<HorizontalDirection> directionProperty = (Property<HorizontalDirection>) property;
return (context, state) -> state.with(directionProperty, context.getHorizontalDirection().clockWise().toHorizontalDirection());
} else {
throw new IllegalArgumentException("Unsupported property type used in hard-coded `facing_clockwise` property: " + property.valueClass());
return (context, state) -> state;
}
}));
HARD_CODED_PLACEMENTS.put("waterlogged", (property -> {
Property<Boolean> waterloggedProperty = (Property<Boolean>) property;
return (context, state) -> state.with(waterloggedProperty, context.isWaterSource());
}));
HARD_CODED_PLACEMENTS.put("rotation", property -> {
if (property.valueClass() == Integer.class) {
IntegerProperty rotationProperty = (IntegerProperty) property;
if (rotationProperty.min == 0 && rotationProperty.max > 0) {
return (context, state) -> {
float rotation = context.getRotation();
SegmentedAngle segmentedAngle = new SegmentedAngle(rotationProperty.max + 1);
return state.with(rotationProperty, segmentedAngle.fromDegrees(rotation + 180));
};
}
}
return (context, state) -> state;
});
}
private final Class<T> clazz;

View File

@@ -73,6 +73,10 @@ public class UseOnContext {
return this.level;
}
public World getWorld() {
return this.level;
}
public Direction getHorizontalDirection() {
return this.player == null ? Direction.NORTH : this.player.getDirection();
}

View File

@@ -28,6 +28,7 @@ public final class ItemDataModifiers {
public static final Key CUSTOM_NAME = Key.of("craftengine:custom-name");
public static final Key CUSTOM_MODEL_DATA = Key.of("craftengine:custom-model-data");
public static final Key COMPONENTS = Key.of("craftengine:components");
public static final Key COMPONENT = Key.of("craftengine:component");
public static final Key ATTRIBUTE_MODIFIERS = Key.of("craftengine:attribute-modifiers");
public static final Key ATTRIBUTES = Key.of("craftengine:attributes");
public static final Key ARGUMENTS = Key.of("craftengine:arguments");
@@ -37,6 +38,7 @@ public final class ItemDataModifiers {
public static final Key OVERWRITABLE_ITEM_NAME = Key.of("craftengine:overwritable-item-name");
public static final Key JUKEBOX_PLAYABLE = Key.of("craftengine:jukebox-playable");
public static final Key REMOVE_COMPONENTS = Key.of("craftengine:remove-components");
public static final Key REMOVE_COMPONENT = Key.of("craftengine:remove-component");
public static final Key TAGS = Key.of("craftengine:tags");
public static final Key NBT = Key.of("craftengine:nbt");
public static final Key TOOLTIP_STYLE = Key.of("craftengine:tooltip-style");
@@ -84,7 +86,9 @@ public final class ItemDataModifiers {
register(ITEM_NAME, ItemNameModifier.FACTORY);
register(DISPLAY_NAME, ItemNameModifier.FACTORY);
register(COMPONENTS, ComponentsModifier.FACTORY);
register(COMPONENT, ComponentsModifier.FACTORY);
register(REMOVE_COMPONENTS, RemoveComponentModifier.FACTORY);
register(REMOVE_COMPONENT, RemoveComponentModifier.FACTORY);
register(FOOD, FoodModifier.FACTORY);
register(MAX_DAMAGE, MaxDamageModifier.FACTORY);
} else {

View File

@@ -43,7 +43,7 @@ public class SingleItemLootEntryContainer<T> extends AbstractSingleLootEntryCont
@SuppressWarnings("unchecked")
@Override
public LootEntryContainer<A> create(Map<String, Object> arguments) {
String itemObj = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.loot_table.entry.item.missing_item");
String itemObj = ResourceConfigUtils.requireNonEmptyStringOrThrow(ResourceConfigUtils.get(arguments, "item", "id"), "warning.config.loot_table.entry.item.missing_item");
Key item = Key.from(itemObj);
int weight = ResourceConfigUtils.getAsInt(arguments.getOrDefault("weight", 1), "weight");
int quality = ResourceConfigUtils.getAsInt(arguments.getOrDefault("quality", 0), "quality");

View File

@@ -0,0 +1,58 @@
package net.momirealms.craftengine.core.loot.function;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import net.momirealms.craftengine.core.item.modifier.ItemDataModifier;
import net.momirealms.craftengine.core.item.recipe.result.ApplyItemDataPostProcessor;
import net.momirealms.craftengine.core.loot.LootConditions;
import net.momirealms.craftengine.core.loot.LootContext;
import net.momirealms.craftengine.core.plugin.context.Condition;
import net.momirealms.craftengine.core.plugin.context.number.NumberProvider;
import net.momirealms.craftengine.core.plugin.context.number.NumberProviders;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import java.util.*;
public class ApplyDataFunction<T> extends AbstractLootConditionalFunction<T> {
public static final Factory<?> FACTORY = new Factory<>();
private final ItemDataModifier<?>[] modifiers;
public ApplyDataFunction(List<Condition<LootContext>> conditions, ItemDataModifier<?>[] modifiers) {
super(conditions);
this.modifiers = modifiers;
}
@Override
public Key type() {
return LootFunctions.APPLY_DATA;
}
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
protected Item<T> applyInternal(Item<T> item, LootContext context) {
ItemBuildContext ctx = ItemBuildContext.of(context.player());
for (ItemDataModifier modifier : this.modifiers) {
item = modifier.apply(item, ctx);
}
return item;
}
public static class Factory<A> implements LootFunctionFactory<A> {
@SuppressWarnings("unchecked")
@Override
public LootFunction<A> create(Map<String, Object> arguments) {
List<ItemDataModifier<?>> modifiers = new ArrayList<>();
Map<String, Object> data = ResourceConfigUtils.getAsMap(ResourceConfigUtils.requireNonNullOrThrow(arguments.get("data"), "warning.config.loot_table.function.apply_data.missing_data"), "data");
for (Map.Entry<String, Object> entry : data.entrySet()) {
Optional.ofNullable(BuiltInRegistries.ITEM_DATA_MODIFIER_FACTORY.getValue(Key.withDefaultNamespace(entry.getKey(), Key.DEFAULT_NAMESPACE)))
.ifPresent(factory -> modifiers.add(factory.create(entry.getValue())));
}
List<Condition<LootContext>> conditions = Optional.ofNullable(arguments.get("conditions"))
.map(it -> LootConditions.fromMapList((List<Map<String, Object>>) it))
.orElse(Collections.emptyList());
return new ApplyDataFunction<>(conditions, modifiers.toArray(new ItemDataModifier[0]));
}
}
}

View File

@@ -17,6 +17,7 @@ import java.util.function.BiFunction;
public class LootFunctions {
public static final Key APPLY_BONUS = Key.from("craftengine:apply_bonus");
public static final Key APPLY_DATA = Key.from("craftengine:apply_data");
public static final Key SET_COUNT = Key.from("craftengine:set_count");
public static final Key EXPLOSION_DECAY = Key.from("craftengine:explosion_decay");
public static final Key DROP_EXP = Key.from("craftengine:drop_exp");
@@ -24,6 +25,7 @@ public class LootFunctions {
static {
register(SET_COUNT, SetCountFunction.FACTORY);
register(APPLY_DATA, ApplyDataFunction.FACTORY);
register(EXPLOSION_DECAY, ExplosionDecayFunction.FACTORY);
register(APPLY_BONUS, ApplyBonusCountFunction.FACTORY);
register(DROP_EXP, DropExpFunction.FACTORY);

View File

@@ -40,10 +40,10 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.parser.ParserException;
import org.yaml.snakeyaml.scanner.ScannerException;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.charset.StandardCharsets;
@@ -620,6 +620,9 @@ public abstract class AbstractPackManager implements PackManager {
AbstractPackManager.this.plugin.logger().severe("Error found while reading config file: " + path, e);
}
return FileVisitResult.CONTINUE;
} catch (ParserException e) {
AbstractPackManager.this.plugin.logger().severe("Invalid YAML file found: " + path + ".\n" + e.getMessage() + "\nIt is recommended to use Visual Studio Code as your YAML editor to fix problems more quickly.");
return FileVisitResult.CONTINUE;
} catch (LocalizedException e) {
e.setArgument(0, path.toString());
TranslationManager.instance().log(e.node(), e.arguments());
@@ -734,10 +737,6 @@ public abstract class AbstractPackManager implements PackManager {
this.plugin.logger().info("Optimized resource pack in " + (time4 - time3) + "ms");
Path finalPath = resourcePackPath();
Files.createDirectories(finalPath.getParent());
if (!VersionHelper.PREMIUM && Config.enableObfuscation()) {
Config.instance().setObf(false);
this.plugin.logger().warn("Resource pack obfuscation requires Premium Edition.");
}
try {
this.zipGenerator.accept(generatedPackPath, finalPath);
} catch (Exception e) {

View File

@@ -38,7 +38,7 @@ public class ResolutionMergeAltas implements Resolution {
j3.add("sources", ja3);
GsonHelper.writeJsonFile(j3, existing.path());
} catch (Exception e) {
CraftEngine.instance().logger().severe("Failed to merge json when resolving file conflicts", e);
CraftEngine.instance().logger().severe("Failed to merge altas when resolving file conflicts", e);
}
}

View File

@@ -0,0 +1,57 @@
package net.momirealms.craftengine.core.pack.conflict.resolution;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import net.momirealms.craftengine.core.pack.conflict.PathContext;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.GsonHelper;
import net.momirealms.craftengine.core.util.Key;
import java.util.HashSet;
import java.util.Map;
public class ResolutionMergeFont implements Resolution {
public static final Factory FACTORY = new Factory();
public static final ResolutionMergeFont INSTANCE = new ResolutionMergeFont();
@Override
public void run(PathContext existing, PathContext conflict) {
try {
JsonObject j1 = GsonHelper.readJsonFile(existing.path()).getAsJsonObject();
JsonObject j2 = GsonHelper.readJsonFile(conflict.path()).getAsJsonObject();
JsonObject j3 = new JsonObject();
JsonArray ja1 = j1.getAsJsonArray("providers");
JsonArray ja2 = j2.getAsJsonArray("providers");
JsonArray ja3 = new JsonArray();
HashSet<String> elements = new HashSet<>();
for (JsonElement je : ja1) {
if (elements.add(je.toString())) {
ja3.add(je);
}
}
for (JsonElement je : ja2) {
if (elements.add(je.toString())) {
ja3.add(je);
}
}
j3.add("providers", ja3);
GsonHelper.writeJsonFile(j3, existing.path());
} catch (Exception e) {
CraftEngine.instance().logger().severe("Failed to merge font when resolving file conflicts", e);
}
}
@Override
public Key type() {
return Resolutions.MERGE_FONT;
}
public static class Factory implements ResolutionFactory {
@Override
public Resolution create(Map<String, Object> arguments) {
return INSTANCE;
}
}
}

View File

@@ -14,6 +14,7 @@ public class Resolutions {
public static final Key RETAIN_MATCHING = Key.of("craftengine:retain_matching");
public static final Key MERGE_JSON = Key.of("craftengine:merge_json");
public static final Key MERGE_ATLAS = Key.of("craftengine:merge_atlas");
public static final Key MERGE_FONT = Key.of("craftengine:merge_font");
public static final Key CONDITIONAL = Key.of("craftengine:conditional");
public static final Key MERGE_PACK_MCMETA = Key.of("craftengine:merge_pack_mcmeta");
public static final Key MERGE_LEGACY_MODEL = Key.of("craftengine:merge_legacy_model");
@@ -25,6 +26,7 @@ public class Resolutions {
register(MERGE_PACK_MCMETA, ResolutionMergePackMcMeta.FACTORY);
register(MERGE_ATLAS, ResolutionMergeAltas.FACTORY);
register(MERGE_LEGACY_MODEL, ResolutionMergeLegacyModel.FACTORY);
register(MERGE_FONT, ResolutionMergeFont.FACTORY);
}
public static void register(Key key, ResolutionFactory factory) {

View File

@@ -184,6 +184,8 @@ public abstract class CraftEngine implements Plugin {
delayedLoadTasks.add(CompletableFuture.runAsync(() -> this.recipeManager.delayedLoad(), this.scheduler.async()));
}
CompletableFutures.allOf(delayedLoadTasks).join();
// 重新发送tags需要等待tags更新完成
this.networkManager.delayedLoad();
long time2 = System.currentTimeMillis();
asyncTime = time2 - time1;
} finally {

View File

@@ -2,22 +2,32 @@ 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.number.NumberProvider;
import net.momirealms.craftengine.core.plugin.context.number.NumberProviders;
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 net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.WorldPosition;
import java.util.*;
public class MatchBlockCondition<CTX extends Context> implements Condition<CTX> {
private final Set<String> ids;
private final boolean regexMatch;
private final NumberProvider x;
private final NumberProvider y;
private final NumberProvider z;
public MatchBlockCondition(Collection<String> ids, boolean regexMatch) {
public MatchBlockCondition(Collection<String> ids, boolean regexMatch, NumberProvider x, NumberProvider y, NumberProvider z) {
this.ids = new HashSet<>(ids);
this.regexMatch = regexMatch;
this.x = x;
this.y = y;
this.z = z;
}
@Override
@@ -27,8 +37,13 @@ public class MatchBlockCondition<CTX extends Context> implements Condition<CTX>
@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();
Optional<WorldPosition> optionalWorldPosition = ctx.getOptionalParameter(DirectContextParameters.POSITION);
if (optionalWorldPosition.isPresent()) {
World world = optionalWorldPosition.get().world();
ExistingBlock blockAt = world.getBlockAt(MiscUtils.fastFloor(this.x.getDouble(ctx)), MiscUtils.fastFloor(this.y.getDouble(ctx)), MiscUtils.fastFloor(this.z.getDouble(ctx)));
return MiscUtils.matchRegex(blockAt.id().asString(), this.ids, this.regexMatch);
}
return false;
}
public static class FactoryImpl<CTX extends Context> implements ConditionFactory<CTX> {
@@ -40,7 +55,10 @@ public class MatchBlockCondition<CTX extends Context> implements Condition<CTX>
throw new LocalizedResourceConfigException("warning.config.condition.match_block.missing_id");
}
boolean regex = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("regex", false), "regex");
return new MatchBlockCondition<>(ids, regex);
return new MatchBlockCondition<>(ids, regex,
NumberProviders.fromObject(arguments.getOrDefault("x", "<arg:position.x>")),
NumberProviders.fromObject(arguments.getOrDefault("y", "<arg:position.y>")),
NumberProviders.fromObject(arguments.getOrDefault("z", "<arg:position.z>")));
}
}
}

View File

@@ -10,6 +10,7 @@ import net.momirealms.craftengine.core.plugin.context.number.NumberProviders;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
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.World;
import net.momirealms.craftengine.core.world.WorldPosition;
@@ -22,13 +23,15 @@ public class DropLootFunction<CTX extends Context> extends AbstractConditionalFu
private final NumberProvider y;
private final NumberProvider z;
private final LootTable<?> lootTable;
private final boolean toInv;
public DropLootFunction(NumberProvider x, NumberProvider y, NumberProvider z, LootTable<?> lootTable, List<Condition<CTX>> predicates) {
public DropLootFunction(List<Condition<CTX>> predicates, NumberProvider x, NumberProvider y, NumberProvider z, LootTable<?> lootTable, boolean toInv) {
super(predicates);
this.x = x;
this.y = y;
this.z = z;
this.lootTable = lootTable;
this.toInv = toInv;
}
@Override
@@ -39,8 +42,14 @@ public class DropLootFunction<CTX extends Context> extends AbstractConditionalFu
WorldPosition position = new WorldPosition(world, x.getDouble(ctx), y.getDouble(ctx), z.getDouble(ctx));
Player player = ctx.getOptionalParameter(DirectContextParameters.PLAYER).orElse(null);
List<? extends Item<?>> items = lootTable.getRandomItems(ctx.contexts(), world, player);
for (Item<?> item : items) {
world.dropItemNaturally(position, item);
if (this.toInv && player != null) {
for (Item<?> item : items) {
player.giveItem(item);
}
} else {
for (Item<?> item : items) {
world.dropItemNaturally(position, item);
}
}
}
}
@@ -62,7 +71,8 @@ public class DropLootFunction<CTX extends Context> extends AbstractConditionalFu
NumberProvider y = NumberProviders.fromObject(arguments.getOrDefault("y", "<arg:position.y>"));
NumberProvider z = NumberProviders.fromObject(arguments.getOrDefault("z", "<arg:position.z>"));
LootTable<?> loots = LootTable.fromMap(MiscUtils.castToMap(arguments.get("loot"), true));
return new DropLootFunction<>(x, y, z, loots, getPredicates(arguments));
boolean toInv = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("to-inventory", false), "to-inventory");
return new DropLootFunction<>(getPredicates(arguments), x, y, z, loots, toInv);
}
}
}

View File

@@ -0,0 +1,6 @@
package net.momirealms.craftengine.core.util;
import java.util.HashMap;
public class MarkedHashMap<K, V> extends HashMap<K, V> {
}

View File

@@ -0,0 +1,58 @@
package net.momirealms.craftengine.core.util;
public class SegmentedAngle {
private final int segments;
private final float degreeToAngle;
private final float angleToDegree;
public SegmentedAngle(int segments) {
if (segments < 2) {
throw new IllegalArgumentException("Segments cannot be less than 2");
} else if (segments > 360) {
throw new IllegalArgumentException("Segments cannot be greater than 360");
} else {
this.segments = segments;
this.degreeToAngle = (float) segments / 360.0F;
this.angleToDegree = 360.0F / (float) segments;
}
}
public boolean isSameAxis(int alpha, int beta) {
int halfSegments = this.segments / 2;
return (alpha % halfSegments) == (beta % halfSegments);
}
public int fromDirection(Direction direction) {
if (direction.axis().isVertical()) {
return 0;
} else {
int i = direction.data2d();
return i * (this.segments / 4);
}
}
public int fromDegreesWithTurns(float degrees) {
return Math.round(degrees * this.degreeToAngle);
}
public int fromDegrees(float degrees) {
return this.normalize(this.fromDegreesWithTurns(degrees));
}
public float toDegreesWithTurns(int rotation) {
return (float) rotation * this.angleToDegree;
}
public float toDegrees(int rotation) {
float f = this.toDegreesWithTurns(this.normalize(rotation));
return f >= 180.0F ? f - 360.0F : f;
}
public int normalize(int rotationBits) {
return ((rotationBits % this.segments) + this.segments) % this.segments;
}
public int getSegments() {
return this.segments;
}
}

View File

@@ -5,6 +5,7 @@ import com.google.gson.JsonObject;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
public class VersionHelper {
public static final boolean PREMIUM = false;
@@ -35,20 +36,30 @@ public class VersionHelper {
private static final boolean v1_21_8;
private static final boolean v1_21_9;
private static final boolean v1_21_10;
private static final Class<?> UNOBFUSCATED_CLAZZ = Objects.requireNonNull(ReflectionUtils.getClazz(
"net.minecraft.obfuscate.DontObfuscate", // 因为无混淆版本没有这个类所以说多写几个防止找不到了
"net.minecraft.data.Main",
"net.minecraft.server.Main",
"net.minecraft.gametest.Main",
"net.minecraft.client.main.Main",
"net.minecraft.client.data.Main"
));
static {
try (InputStream inputStream = Class.forName("net.minecraft.obfuscate.DontObfuscate").getResourceAsStream("/version.json")) {
try (InputStream inputStream = UNOBFUSCATED_CLAZZ.getResourceAsStream("/version.json")) {
if (inputStream == null) {
throw new IOException("Failed to load version.json");
}
JsonObject json = GsonHelper.parseJsonToJsonObject(new String(inputStream.readAllBytes(), StandardCharsets.UTF_8));
String versionString = json.getAsJsonPrimitive("id").getAsString().split("-", 2)[0];
String versionString = json.getAsJsonPrimitive("id").getAsString()
.split("-", 2)[0] // 1.21.10-rc1 -> 1.21.10
.split("_", 2)[0]; // 1.21.11_unobfuscated -> 1.21.11
MINECRAFT_VERSION = new MinecraftVersion(versionString);
String[] split = versionString.split("\\.");
int major = Integer.parseInt(split[1]);
int minor = split.length == 3 ? Integer.parseInt(split[2].split("-", 2)[0]) : 0;
int minor = split.length == 3 ? Integer.parseInt(split[2]) : 0;
// 12001 = 1.20.1
// 12104 = 1.21.4

View File

@@ -39,6 +39,14 @@ public interface World {
this.setBlockAt(x, y, z, blockState.customBlockState(), flags);
}
default void setBlockAt(BlockPos pos, BlockStateWrapper blockState, int flags) {
this.setBlockAt(pos.x(), pos.y(), pos.z(), blockState, flags);
}
default void setBlockAt(BlockPos pos, ImmutableBlockState blockState, int flags) {
this.setBlockAt(pos.x(), pos.y(), pos.z(), blockState, flags);
}
String name();
Path directory();

View File

@@ -35,6 +35,7 @@ public class CEChunk {
private final ReentrantReadWriteLock renderLock = new ReentrantReadWriteLock();
private volatile boolean dirty;
private volatile boolean loaded;
private volatile boolean activated;
public CEChunk(CEWorld world, ChunkPos chunkPos) {
this.world = world;
@@ -115,40 +116,129 @@ public class CEChunk {
}
}
public void addConstantBlockEntityRenderer(BlockPos pos) {
this.addConstantBlockEntityRenderer(pos, this.getBlockState(pos));
public ConstantBlockEntityRenderer addConstantBlockEntityRenderer(BlockPos pos) {
return this.addConstantBlockEntityRenderer(pos, this.getBlockState(pos), null);
}
public void addConstantBlockEntityRenderer(BlockPos pos, ImmutableBlockState state) {
public ConstantBlockEntityRenderer addConstantBlockEntityRenderer(BlockPos pos, ImmutableBlockState state) {
return this.addConstantBlockEntityRenderer(pos, state, null);
}
@SuppressWarnings({"unchecked", "rawtypes"})
public ConstantBlockEntityRenderer addConstantBlockEntityRenderer(BlockPos pos, ImmutableBlockState state, @Nullable ConstantBlockEntityRenderer previous) {
BlockEntityElementConfig<? extends BlockEntityElement>[] renderers = state.constantRenderers();
if (renderers != null && renderers.length > 0) {
BlockEntityElement[] elements = new BlockEntityElement[renderers.length];
World wrappedWorld = this.world.world();
for (int i = 0; i < elements.length; i++) {
elements[i] = renderers[i].create(wrappedWorld, pos);
}
ConstantBlockEntityRenderer renderer = new ConstantBlockEntityRenderer(elements);
for (Player player : getTrackedBy()) {
renderer.show(player);
World wrappedWorld = this.world.world();
List<Player> trackedBy = getTrackedBy();
boolean hasTrackedBy = trackedBy != null && !trackedBy.isEmpty();
// 处理旧到新的转换
if (previous != null) {
// 由于entity-render的体量基本都很小所以考虑一个特殊情况即前后都是1个renderer对此情况进行简化和优化
BlockEntityElement[] previousElements = previous.elements().clone();
if (previousElements.length == 1 && renderers.length == 1) {
BlockEntityElement previousElement = previousElements[0];
BlockEntityElementConfig<? extends BlockEntityElement> config = renderers[0];
outer: {
if (config.elementClass().isInstance(previousElement)) {
BlockEntityElement element = ((BlockEntityElementConfig) config).create(wrappedWorld, pos, previousElement);
if (element != null) {
elements[0] = element;
if (hasTrackedBy) {
for (Player player : trackedBy) {
element.transform(player);
}
}
break outer;
}
}
BlockEntityElement element = config.create(wrappedWorld, pos);
elements[0] = element;
if (hasTrackedBy) {
for (Player player : trackedBy) {
previousElement.hide(player);
element.show(player);
}
}
}
} else {
outer: for (int i = 0; i < elements.length; i++) {
BlockEntityElementConfig<? extends BlockEntityElement> config = renderers[i];
for (int j = 0; j < previousElements.length; j++) {
BlockEntityElement previousElement = previousElements[j];
if (previousElement != null && config.elementClass().isInstance(previousElement)) {
BlockEntityElement newElement = ((BlockEntityElementConfig) config).create(wrappedWorld, pos, previousElement);
if (newElement != null) {
previousElements[j] = null;
elements[i] = newElement;
if (hasTrackedBy) {
for (Player player : trackedBy) {
newElement.transform(player);
}
}
continue outer;
}
}
}
BlockEntityElement newElement = config.create(wrappedWorld, pos);
elements[i] = newElement;
if (hasTrackedBy) {
for (Player player : trackedBy) {
newElement.show(player);
}
}
}
if (hasTrackedBy) {
for (int i = 0; i < previousElements.length; i++) {
BlockEntityElement previousElement = previousElements[i];
if (previousElement != null) {
for (Player player : trackedBy) {
previousElement.hide(player);
}
}
}
}
}
} else {
for (int i = 0; i < elements.length; i++) {
elements[i] = renderers[i].create(wrappedWorld, pos);
}
if (hasTrackedBy) {
for (Player player : trackedBy) {
renderer.show(player);
}
}
}
try {
this.renderLock.writeLock().lock();
this.constantBlockEntityRenderers.put(pos, renderer);
return renderer;
} finally {
this.renderLock.writeLock().unlock();
}
}
return null;
}
public void removeConstantBlockEntityRenderer(BlockPos pos) {
@Nullable
public ConstantBlockEntityRenderer removeConstantBlockEntityRenderer(BlockPos pos) {
return this.removeConstantBlockEntityRenderer(pos, true);
}
@Nullable
public ConstantBlockEntityRenderer removeConstantBlockEntityRenderer(BlockPos pos, boolean hide) {
try {
this.renderLock.writeLock().lock();
ConstantBlockEntityRenderer removed = this.constantBlockEntityRenderers.remove(pos);
if (removed != null) {
for (Player player : getTrackedBy()) {
removed.hide(player);
if (hide) {
for (Player player : getTrackedBy()) {
removed.hide(player);
}
}
}
return removed;
} finally {
this.renderLock.writeLock().unlock();
}
@@ -183,7 +273,12 @@ public class CEChunk {
this.removeDynamicBlockEntityRenderer(blockPos);
}
public List<Player> getTrackedBy() {
return this.world.world.getTrackedBy(this.chunkPos);
}
public void activateAllBlockEntities() {
if (this.activated) return;
for (BlockEntity blockEntity : this.blockEntities.values()) {
blockEntity.setValid(true);
this.replaceOrCreateTickingBlockEntity(blockEntity);
@@ -192,13 +287,11 @@ public class CEChunk {
for (ConstantBlockEntityRenderer renderer : this.constantBlockEntityRenderers.values()) {
renderer.activate();
}
}
public List<Player> getTrackedBy() {
return this.world.world.getTrackedBy(this.chunkPos);
this.activated = true;
}
public void deactivateAllBlockEntities() {
if (!this.activated) return;
this.blockEntities.values().forEach(e -> e.setValid(false));
this.constantBlockEntityRenderers.values().forEach(ConstantBlockEntityRenderer::deactivate);
this.dynamicBlockEntityRenderers.clear();
@@ -206,6 +299,7 @@ public class CEChunk {
this.tickingSyncBlockEntitiesByPos.clear();
this.tickingAsyncBlockEntitiesByPos.values().forEach((ticker) -> ticker.setTicker(DummyTickingBlockEntity.INSTANCE));
this.tickingAsyncBlockEntitiesByPos.clear();
this.activated = false;
}
@SuppressWarnings("unchecked")
@@ -452,14 +546,12 @@ public class CEChunk {
public void load() {
if (this.loaded) return;
this.world.addLoadedChunk(this);
this.activateAllBlockEntities();
this.loaded = true;
}
public void unload() {
if (!this.loaded) return;
this.world.removeLoadedChunk(this);
this.deactivateAllBlockEntities();
this.loaded = false;
}
}

View File

@@ -26,6 +26,11 @@ public class CachedStorage<T extends WorldDataStorage> implements WorldDataStora
.build();
}
@Override
public CEChunk readNewChunkAt(CEWorld world, ChunkPos pos) throws IOException {
return this.storage.readNewChunkAt(world, pos);
}
@Override
public @NotNull CEChunk readChunkAt(@NotNull CEWorld world, @NotNull ChunkPos pos) throws IOException {
CEChunk chunk = this.chunkCache.getIfPresent(pos);
@@ -42,6 +47,12 @@ public class CachedStorage<T extends WorldDataStorage> implements WorldDataStora
this.storage.writeChunkAt(pos, chunk);
}
@Override
public void clearChunkAt(@NotNull ChunkPos pos) throws IOException {
this.storage.clearChunkAt(pos);
this.chunkCache.invalidate(pos);
}
@Override
public void close() throws IOException {
this.storage.close();

View File

@@ -120,6 +120,19 @@ public class DefaultRegionFileStorage implements WorldDataStorage {
}
}
@Override
public CEChunk readNewChunkAt(CEWorld world, ChunkPos pos) throws IOException {
RegionFile regionFile = this.getRegionFile(pos, false, true);
try {
if (regionFile.doesChunkExist(pos)) {
regionFile.clear(pos);
}
return new CEChunk(world, pos);
} finally {
regionFile.fileLock.unlock();
}
}
@Override
public @NotNull CEChunk readChunkAt(@NotNull CEWorld world, @NotNull ChunkPos pos) throws IOException {
RegionFile regionFile = this.getRegionFile(pos, false, true);
@@ -152,6 +165,11 @@ public class DefaultRegionFileStorage implements WorldDataStorage {
writeChunkTagAt(pos, nbt);
}
@Override
public void clearChunkAt(@NotNull ChunkPos pos) throws IOException {
this.writeChunkTagAt(pos, null);
}
public void writeChunkTagAt(@NotNull ChunkPos pos, @Nullable CompoundTag nbt) throws IOException {
RegionFile regionFile = this.getRegionFile(pos, nbt == null, true);
try {

View File

@@ -9,11 +9,18 @@ import java.io.IOException;
public interface WorldDataStorage {
default CEChunk readNewChunkAt(CEWorld world, ChunkPos pos) throws IOException {
this.clearChunkAt(pos);
return this.readChunkAt(world, pos);
}
@NotNull
CEChunk readChunkAt(@NotNull CEWorld world, @NotNull ChunkPos pos) throws IOException;
void writeChunkAt(@NotNull ChunkPos pos, @NotNull CEChunk chunk) throws IOException;
void clearChunkAt(@NotNull ChunkPos pos) throws IOException;
void flush() throws IOException;
void close() throws IOException;

View File

@@ -2,9 +2,9 @@ org.gradle.jvmargs=-Xmx1G
# Project settings
# Rule: [major update].[feature update].[bug fix]
project_version=0.0.65
config_version=52
lang_version=36
project_version=0.0.65.6
config_version=53
lang_version=37
project_group=net.momirealms
latest_supported_version=1.21.10
@@ -49,7 +49,7 @@ byte_buddy_version=1.17.8
ahocorasick_version=0.6.3
snake_yaml_version=2.5
anti_grief_version=1.0.4
nms_helper_version=1.0.123
nms_helper_version=1.0.127
evalex_version=3.5.0
reactive_streams_version=1.0.4
amazon_awssdk_version=2.34.5

1
wiki Submodule

Submodule wiki added at bec8bb7cf8