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

家具重构part1

This commit is contained in:
XiaoMoMi
2025-12-02 01:48:44 +08:00
parent f0f2181a18
commit d2b794c70d
125 changed files with 1827 additions and 1753 deletions

View File

@@ -711,12 +711,8 @@ public abstract class AbstractBlockManager extends AbstractModelGenerator implem
}
private CullingData parseCullingData(Object arguments) {
if (arguments instanceof Boolean b && !b) {
return null;
}
if (!(arguments instanceof Map)) {
return new CullingData(DEFAULT_BLOCK_ENTITY_AABB, Config.entityCullingViewDistance(), 0.5, true);
}
if (arguments instanceof Boolean b && !b) return null;
if (!(arguments instanceof Map)) return new CullingData(DEFAULT_BLOCK_ENTITY_AABB, Config.entityCullingViewDistance(), 0.5, true);
Map<String, Object> argumentsMap = ResourceConfigUtils.getAsMap(arguments, "entity-culling");
return new CullingData(
ResourceConfigUtils.getAsAABB(argumentsMap.getOrDefault("aabb", 1), "aabb"),

View File

@@ -78,7 +78,7 @@ public abstract class BlockBehavior {
return (boolean) superMethod.call();
}
// 1.20-1.20.4 BlockState state, BlockGetter world, BlockPos pos, PathComputationType type
// 1.20-1.20.4 BlockState state, BlockGetter world, BlockPos pos, PathComputationType id
// 1.20.5+ BlockState state, PathComputationType pathComputationType
public boolean isPathFindable(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
return (boolean) superMethod.call();

View File

@@ -41,6 +41,7 @@ public class BlockSettings {
float friction = 0.6f;
float speedFactor = 1f;
float jumpFactor = 1f;
Map<CustomDataType<?>, Object> customData = new IdentityHashMap<>(4);
private BlockSettings() {}
@@ -107,9 +108,29 @@ public class BlockSettings {
newSettings.speedFactor = settings.speedFactor;
newSettings.jumpFactor = settings.jumpFactor;
newSettings.friction = settings.friction;
newSettings.customData = new IdentityHashMap<>(settings.customData);
return newSettings;
}
@SuppressWarnings("unchecked")
public <T> T getCustomData(CustomDataType<T> type) {
return (T) this.customData.get(type);
}
public void clearCustomData() {
this.customData.clear();
}
@Nullable
@SuppressWarnings("unchecked")
public <T> T removeCustomData(CustomDataType<?> type) {
return (T) this.customData.remove(type);
}
public <T> void addCustomData(CustomDataType<T> key, T value) {
this.customData.put(key, value);
}
public Set<Key> tags() {
return tags;
}
@@ -542,7 +563,7 @@ public class BlockSettings {
}));
}
private static void registerFactory(String id, Modifier.Factory factory) {
public static void registerFactory(String id, Modifier.Factory factory) {
FACTORIES.put(id, factory);
}
}

View File

@@ -4,7 +4,7 @@ import java.util.concurrent.Callable;
public interface IsPathFindableBlockBehavior {
// 1.20-1.20.4 BlockState state, BlockGetter world, BlockPos pos, PathComputationType type
// 1.20-1.20.4 BlockState state, BlockGetter world, BlockPos pos, PathComputationType id
// 1.20.5+ BlockState state, PathComputationType pathComputationType
boolean isPathFindable(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception;
}

View File

@@ -24,7 +24,7 @@ public abstract class BlockEntity {
this.type = type;
}
public final CompoundTag saveAsTag() {
public CompoundTag saveAsTag() {
CompoundTag tag = new CompoundTag();
this.saveId(tag);
this.savePos(tag);

View File

@@ -5,6 +5,7 @@ import net.momirealms.craftengine.core.util.Key;
public final class BlockEntityTypeKeys {
private BlockEntityTypeKeys() {}
public static final Key INACTIVE = Key.of("craftengine:inactive");
public static final Key UNSAFE_COMPOSITE = Key.of("craftengine:unsafe_composite");
public static final Key SIMPLE_STORAGE = Key.of("craftengine:simple_storage");
public static final Key SIMPLE_PARTICLE = Key.of("craftengine:simple_particle");

View File

@@ -7,6 +7,7 @@ import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.ResourceKey;
public abstract class BlockEntityTypes {
public static final BlockEntityType<InactiveBlockEntity> INACTIVE = register(BlockEntityTypeKeys.INACTIVE);
public static <T extends BlockEntity> BlockEntityType<T> register(Key id) {
BlockEntityType<T> type = new BlockEntityType<>(id);

View File

@@ -0,0 +1,21 @@
package net.momirealms.craftengine.core.block.entity;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.sparrow.nbt.CompoundTag;
public class InactiveBlockEntity extends BlockEntity {
private final CompoundTag tag;
public InactiveBlockEntity(BlockPos pos,
ImmutableBlockState blockState,
CompoundTag tag) {
super(BlockEntityTypes.INACTIVE, pos, blockState);
this.tag = tag;
}
@Override
public CompoundTag saveAsTag() {
return this.tag;
}
}

View File

@@ -0,0 +1,96 @@
package net.momirealms.craftengine.core.entity;
import net.momirealms.craftengine.core.world.CEWorld;
import net.momirealms.craftengine.core.world.Cullable;
import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.sparrow.nbt.CompoundTag;
import java.util.UUID;
public abstract class CustomEntity implements Cullable {
protected final CustomEntityType<?> type;
protected final UUID uuid;
protected WorldPosition position;
protected boolean valid = true;
protected CustomEntity(CustomEntityType<?> type, WorldPosition position, UUID uuid) {
this.position = position;
this.type = type;
this.uuid = uuid;
}
public CompoundTag saveAsTag() {
CompoundTag tag = new CompoundTag();
this.saveId(tag);
this.savePos(tag);
this.saveCustomData(tag);
return tag;
}
private void savePos(CompoundTag tag) {
tag.putDouble("x", this.position.x());
tag.putDouble("y", this.position.y());
tag.putDouble("z", this.position.z());
tag.putFloat("x_rot", this.position.xRot());
tag.putFloat("y_rot", this.position.yRot());
}
public boolean isValid() {
return this.valid;
}
public UUID uuid() {
return uuid;
}
public double x() {
return this.position.x();
}
public double y() {
return this.position.y();
}
public double z() {
return this.position.z();
}
public float yRot() {
return this.position.yRot();
}
public float xRot() {
return this.position.xRot();
}
public CustomEntityType<?> entityType() {
return this.type;
}
protected void saveCustomData(CompoundTag tag) {
}
public void loadCustomData(CompoundTag tag) {
}
private void saveId(CompoundTag tag) {
tag.putString("id", this.type.id().asString());
}
public void destroy() {
this.valid = false;
}
public static UUID readUUID(CompoundTag tag) {
return tag.getUUID("uuid");
}
public static WorldPosition readPos(CEWorld world, CompoundTag tag) {
double x = tag.getDouble("x");
double y = tag.getDouble("y");
double z = tag.getDouble("z");
float xRot = tag.getFloat("x_rot");
float yRot = tag.getFloat("y_rot");
return new WorldPosition(world.world, x, y, z, xRot, yRot);
}
}

View File

@@ -0,0 +1,14 @@
package net.momirealms.craftengine.core.entity;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.world.WorldPosition;
import java.util.UUID;
public record CustomEntityType<T extends CustomEntity>(Key id, Factory<T> factory) {
public interface Factory<T extends CustomEntity> {
T create(UUID uuid, WorldPosition position);
}
}

View File

@@ -0,0 +1,10 @@
package net.momirealms.craftengine.core.entity;
import net.momirealms.craftengine.core.util.Key;
public final class CustomEntityTypeKeys {
private CustomEntityTypeKeys() {}
public static final Key FURNITURE = Key.of("craftengine:furniture");
public static final Key INACTIVE = Key.of("craftengine:inactive");
}

View File

@@ -0,0 +1,18 @@
package net.momirealms.craftengine.core.entity;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.registry.Registries;
import net.momirealms.craftengine.core.registry.WritableRegistry;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.ResourceKey;
public class CustomEntityTypes {
public static final CustomEntityType<InactiveCustomEntity> INACTIVE = register(CustomEntityTypeKeys.INACTIVE, InactiveCustomEntity::new);
public static <T extends CustomEntity> CustomEntityType<T> register(Key id, CustomEntityType.Factory<T> factory) {
CustomEntityType<T> type = new CustomEntityType<>(id, factory);
((WritableRegistry<CustomEntityType<?>>) BuiltInRegistries.ENTITY_TYPE)
.register(ResourceKey.create(Registries.ENTITY_TYPE.location(), id), type);
return type;
}
}

View File

@@ -0,0 +1,54 @@
package net.momirealms.craftengine.core.entity;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.plugin.entityculling.CullingData;
import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.sparrow.nbt.CompoundTag;
import net.momirealms.sparrow.nbt.NBT;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.UUID;
public class InactiveCustomEntity extends CustomEntity {
public static final CompoundTag INVALID_TAG = new CompoundTag(Map.of("type", NBT.createString(CustomEntityTypes.INACTIVE.id().asMinimalString())));
private final CompoundTag data;
public InactiveCustomEntity(UUID uuid, WorldPosition position) {
super(CustomEntityTypes.INACTIVE, position, uuid);
this.data = INVALID_TAG;
}
public InactiveCustomEntity(UUID uuid, WorldPosition position, CompoundTag data) {
super(CustomEntityTypes.INACTIVE, position, uuid);
this.data = data;
}
@Override
public CompoundTag saveAsTag() {
return this.data;
}
@Override
public boolean isValid() {
// 不正常的数据不要存储
return this.data != INVALID_TAG;
}
@Override
public void destroy() {
}
@Override
public void show(Player player) {
}
@Override
public void hide(Player player) {
}
@Override
public @Nullable CullingData cullingData() {
return null;
}
}

View File

@@ -1,4 +1,4 @@
package net.momirealms.craftengine.core.entity;
package net.momirealms.craftengine.core.entity.display;
public enum Billboard {
FIXED(0),

View File

@@ -1,4 +1,4 @@
package net.momirealms.craftengine.core.entity;
package net.momirealms.craftengine.core.entity.display;
public enum ItemDisplayContext {
NONE(0),

View File

@@ -1,89 +0,0 @@
package net.momirealms.craftengine.core.entity.furniture;
import net.momirealms.craftengine.core.loot.LootTable;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.plugin.context.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.context.function.Function;
import net.momirealms.craftengine.core.util.Key;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public abstract class AbstractCustomFurniture implements CustomFurniture {
private final Key id;
private final FurnitureSettings settings;
private final Map<AnchorType, Placement> placements;
private final Map<EventTrigger, List<Function<Context>>> events;
@Nullable
private final LootTable<?> lootTable;
private final AnchorType anyType;
protected AbstractCustomFurniture(@NotNull Key id,
@NotNull FurnitureSettings settings,
@NotNull Map<AnchorType, Placement> placements,
@NotNull Map<EventTrigger, List<Function<Context>>> events,
@Nullable LootTable<?> lootTable) {
this.id = id;
this.settings = settings;
this.placements = placements;
this.lootTable = lootTable;
this.events = events;
this.anyType = placements.keySet().stream().findFirst().orElse(null);
}
@Override
public void execute(Context context, EventTrigger trigger) {
for (Function<Context> function : Optional.ofNullable(this.events.get(trigger)).orElse(Collections.emptyList())) {
function.run(context);
}
}
@Override
public Key id() {
return this.id;
}
@Override
public Map<AnchorType, Placement> placements() {
return this.placements;
}
@Override
public FurnitureSettings settings() {
return this.settings;
}
@Override
public @Nullable LootTable<?> lootTable() {
return this.lootTable;
}
@Override
public AnchorType getAnyAnchorType() {
return this.anyType;
}
@Override
public boolean isAllowedPlacement(AnchorType anchorType) {
return this.placements.containsKey(anchorType);
}
@Override
public Placement getPlacement(AnchorType anchorType) {
return this.placements.get(anchorType);
}
@Override
public Placement getValidPlacement(AnchorType anchorType) {
Placement placement = this.placements.get(anchorType);
if (placement == null) {
return this.placements.get(getAnyAnchorType());
}
return placement;
}
}

View File

@@ -1,92 +0,0 @@
package net.momirealms.craftengine.core.entity.furniture;
import net.momirealms.craftengine.core.entity.Billboard;
import net.momirealms.craftengine.core.entity.ItemDisplayContext;
import net.momirealms.craftengine.core.util.Key;
import org.joml.Quaternionf;
import org.joml.Vector3f;
public abstract class AbstractFurnitureElement implements FurnitureElement {
private final Key item;
private final Billboard billboard;
private final ItemDisplayContext transform;
private final Vector3f scale;
private final Vector3f translation;
private final Vector3f position;
private final Quaternionf rotation;
private final boolean applyDyedColor;
private final float shadowRadius;
private final float shadowStrength;
public AbstractFurnitureElement(Key item,
Billboard billboard,
ItemDisplayContext transform,
Vector3f scale,
Vector3f translation,
Vector3f position,
Quaternionf rotation,
float shadowRadius,
float shadowStrength,
boolean applyDyedColor) {
this.billboard = billboard;
this.transform = transform;
this.scale = scale;
this.translation = translation;
this.item = item;
this.rotation = rotation;
this.position = position;
this.applyDyedColor = applyDyedColor;
this.shadowRadius = shadowRadius;
this.shadowStrength = shadowStrength;
}
@Override
public float shadowRadius() {
return shadowRadius;
}
@Override
public float shadowStrength() {
return shadowStrength;
}
@Override
public boolean applyDyedColor() {
return applyDyedColor;
}
@Override
public Quaternionf rotation() {
return rotation;
}
@Override
public Key item() {
return item;
}
@Override
public Billboard billboard() {
return billboard;
}
@Override
public ItemDisplayContext transform() {
return transform;
}
@Override
public Vector3f scale() {
return scale;
}
@Override
public Vector3f translation() {
return translation;
}
@Override
public Vector3f position() {
return position;
}
}

View File

@@ -1,19 +1,24 @@
package net.momirealms.craftengine.core.entity.furniture;
import net.momirealms.craftengine.core.entity.Billboard;
import net.momirealms.craftengine.core.entity.ItemDisplayContext;
import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig;
import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigs;
import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfig;
import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxTypes;
import net.momirealms.craftengine.core.loot.LootTable;
import net.momirealms.craftengine.core.pack.LoadingSequence;
import net.momirealms.craftengine.core.pack.Pack;
import net.momirealms.craftengine.core.pack.PendingConfigSection;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.config.IdSectionConfigParser;
import net.momirealms.craftengine.core.plugin.context.event.EventFunctions;
import net.momirealms.craftengine.core.plugin.entityculling.CullingData;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.util.GsonHelper;
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.collision.AABB;
import org.incendo.cloud.suggestion.Suggestion;
import org.joml.Vector3f;
@@ -21,7 +26,7 @@ import java.nio.file.Path;
import java.util.*;
public abstract class AbstractFurnitureManager implements FurnitureManager {
protected final Map<Key, CustomFurniture> byId = new HashMap<>();
protected final Map<Key, FurnitureConfig> byId = new HashMap<>();
private final CraftEngine plugin;
private final FurnitureParser furnitureParser;
// Cached command suggestions
@@ -56,12 +61,12 @@ public abstract class AbstractFurnitureManager implements FurnitureManager {
}
@Override
public Optional<CustomFurniture> furnitureById(Key id) {
public Optional<FurnitureConfig> furnitureById(Key id) {
return Optional.ofNullable(this.byId.get(id));
}
@Override
public Map<Key, CustomFurniture> loadedFurniture() {
public Map<Key, FurnitureConfig> loadedFurniture() {
return Collections.unmodifiableMap(this.byId);
}
@@ -72,10 +77,6 @@ public abstract class AbstractFurnitureManager implements FurnitureManager {
protected abstract HitBoxConfig defaultHitBox();
protected abstract FurnitureElement.Builder furnitureElementBuilder();
protected abstract CustomFurniture.Builder furnitureBuilder();
public class FurnitureParser extends IdSectionConfigParser {
public static final String[] CONFIG_SECTION_NAME = new String[] { "furniture" };
private final List<PendingConfigSection> pendingConfigSections = new ArrayList<>();
@@ -107,91 +108,72 @@ public abstract class AbstractFurnitureManager implements FurnitureManager {
return LoadingSequence.FURNITURE;
}
@SuppressWarnings("unchecked")
@Override
public void parseSection(Pack pack, Path path, String node, Key id, Map<String, Object> section) {
if (AbstractFurnitureManager.this.byId.containsKey(id)) {
throw new LocalizedResourceConfigException("warning.config.furniture.duplicate");
}
EnumMap<AnchorType, CustomFurniture.Placement> placements = new EnumMap<>(AnchorType.class);
Object placementObj = section.get("placement");
Map<String, Object> placementMap = MiscUtils.castToMap(ResourceConfigUtils.requireNonNullOrThrow(placementObj, "warning.config.furniture.missing_placement"), false);
if (placementMap.isEmpty()) {
throw new LocalizedResourceConfigException("warning.config.furniture.missing_placement");
}
for (Map.Entry<String, Object> entry : placementMap.entrySet()) {
// anchor type
AnchorType anchorType = AnchorType.valueOf(entry.getKey().toUpperCase(Locale.ENGLISH));
Map<String, Object> placementArguments = MiscUtils.castToMap(entry.getValue(), false);
Optional<Vector3f> optionalLootSpawnOffset = Optional.ofNullable(placementArguments.get("loot-spawn-offset")).map(it -> ResourceConfigUtils.getAsVector3f(it, "loot-spawn-offset"));
// furniture display elements
List<FurnitureElement> elements = new ArrayList<>();
List<Map<String, Object>> elementConfigs = (List<Map<String, Object>>) placementArguments.getOrDefault("elements", List.of());
for (Map<String, Object> element : elementConfigs) {
FurnitureElement furnitureElement = furnitureElementBuilder()
.item(Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(element.get("item"), "warning.config.furniture.element.missing_item")))
.applyDyedColor(ResourceConfigUtils.getAsBoolean(element.getOrDefault("apply-dyed-color", true), "apply-dyed-color"))
.billboard(ResourceConfigUtils.getOrDefault(element.get("billboard"), o -> Billboard.valueOf(o.toString().toUpperCase(Locale.ENGLISH)), Billboard.FIXED))
.transform(ResourceConfigUtils.getOrDefault(ResourceConfigUtils.get(element, "transform", "display-transform"), o -> ItemDisplayContext.valueOf(o.toString().toUpperCase(Locale.ENGLISH)), ItemDisplayContext.NONE))
.scale(ResourceConfigUtils.getAsVector3f(element.getOrDefault("scale", "1"), "scale"))
.position(ResourceConfigUtils.getAsVector3f(element.getOrDefault("position", "0"), "position"))
.translation(ResourceConfigUtils.getAsVector3f(element.getOrDefault("translation", "0"), "translation"))
.rotation(ResourceConfigUtils.getAsQuaternionf(element.getOrDefault("rotation", "0"), "rotation"))
.shadowRadius(ResourceConfigUtils.getAsFloat(element.getOrDefault("shadow-radius", 0f), "shadow-radius"))
.shadowStrength(ResourceConfigUtils.getAsFloat(element.getOrDefault("shadow-strength", 1f), "shadow-strength"))
.build();
elements.add(furnitureElement);
}
// external model providers
Map<String, Object> variantsMap = ResourceConfigUtils.getAsMap(ResourceConfigUtils.requireNonNullOrThrow(ResourceConfigUtils.get(section, "variants", "placement", "variant"), "warning.config.furniture.missing_variants"), "variants");
if (variantsMap.isEmpty()) {
throw new LocalizedResourceConfigException("warning.config.furniture.missing_variants");
}
Map<String, FurnitureVariant> variants = new HashMap<>();
for (Map.Entry<String, Object> e0 : variantsMap.entrySet()) {
String variantName = e0.getKey();
Map<String, Object> variantArguments = ResourceConfigUtils.getAsMap(e0.getValue(), variantName);
Optional<Vector3f> optionalLootSpawnOffset = Optional.ofNullable(variantArguments.get("loot-spawn-offset")).map(it -> ResourceConfigUtils.getAsVector3f(it, "loot-spawn-offset"));
List<FurnitureElementConfig<?>> elements = ResourceConfigUtils.parseConfigAsList(variantArguments.get("elements"), FurnitureElementConfigs::fromMap);
// fixme 外部模型不应该在这
Optional<ExternalModel> externalModel;
if (placementArguments.containsKey("model-engine")) {
externalModel = Optional.of(plugin.compatibilityManager().createModel("ModelEngine", placementArguments.get("model-engine").toString()));
} else if (placementArguments.containsKey("better-model")) {
externalModel = Optional.of(plugin.compatibilityManager().createModel("BetterModel", placementArguments.get("better-model").toString()));
if (variantArguments.containsKey("model-engine")) {
externalModel = Optional.of(plugin.compatibilityManager().createModel("ModelEngine", variantArguments.get("model-engine").toString()));
} else if (variantArguments.containsKey("better-model")) {
externalModel = Optional.of(plugin.compatibilityManager().createModel("BetterModel", variantArguments.get("better-model").toString()));
} else {
externalModel = Optional.empty();
}
// add hitboxes
List<HitBoxConfig> hitboxes = ResourceConfigUtils.parseConfigAsList(placementArguments.get("hitboxes"), HitBoxTypes::fromMap);
List<HitBoxConfig> hitboxes = ResourceConfigUtils.parseConfigAsList(variantArguments.get("hitboxes"), HitBoxTypes::fromMap);
if (hitboxes.isEmpty() && externalModel.isEmpty()) {
hitboxes = List.of(defaultHitBox());
}
// rules
Map<String, Object> ruleSection = MiscUtils.castToMap(placementArguments.get("rules"), true);
if (ruleSection != null) {
placements.put(anchorType, new CustomFurniture.Placement(
anchorType,
elements.toArray(new FurnitureElement[0]),
hitboxes.toArray(new HitBoxConfig[0]),
ResourceConfigUtils.getOrDefault(ruleSection.get("rotation"), o -> RotationRule.valueOf(o.toString().toUpperCase(Locale.ENGLISH)), RotationRule.ANY),
ResourceConfigUtils.getOrDefault(ruleSection.get("alignment"), o -> AlignmentRule.valueOf(o.toString().toUpperCase(Locale.ENGLISH)), AlignmentRule.CENTER),
externalModel,
optionalLootSpawnOffset
));
} else {
placements.put(anchorType, new CustomFurniture.Placement(
anchorType,
elements.toArray(new FurnitureElement[0]),
hitboxes.toArray(new HitBoxConfig[0]),
RotationRule.ANY,
AlignmentRule.CENTER,
externalModel,
optionalLootSpawnOffset
));
}
variants.put(variantName, new FurnitureVariant(
elements.toArray(new FurnitureElementConfig[0]),
hitboxes.toArray(new HitBoxConfig[0]),
externalModel,
optionalLootSpawnOffset
));
}
CustomFurniture furniture = furnitureBuilder()
AABB maxAABB = null;
FurnitureConfig furniture = FurnitureConfig.builder()
.id(id)
.settings(FurnitureSettings.fromMap(MiscUtils.castToMap(section.get("settings"), true)))
.placement(placements)
.variants(variants)
.events(EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event")))
.lootTable(LootTable.fromMap(MiscUtils.castToMap(section.get("loot"), true)))
.cullingData(parseCullingData(section.get("entity-culling"), maxAABB))
.build();
AbstractFurnitureManager.this.byId.put(id, furniture);
}
private CullingData parseCullingData(Object arguments, AABB maxHitbox) {
if (arguments instanceof Boolean b && !b)
return null;
if (!(arguments instanceof Map))
return new CullingData(maxHitbox, Config.entityCullingViewDistance(), 0.5, true);
Map<String, Object> argumentsMap = ResourceConfigUtils.getAsMap(arguments, "entity-culling");
return new CullingData(
ResourceConfigUtils.getAsAABB(argumentsMap.getOrDefault("aabb", maxHitbox), "aabb"),
ResourceConfigUtils.getAsInt(argumentsMap.getOrDefault("view-distance", Config.entityCullingViewDistance()), "view-distance"),
ResourceConfigUtils.getAsDouble(argumentsMap.getOrDefault("aabb-expansion", 0.5), "aabb-expansion"),
ResourceConfigUtils.getAsBoolean(argumentsMap.getOrDefault("ray-tracing", true), "ray-tracing")
);
}
}
}

View File

@@ -1,5 +1,6 @@
package net.momirealms.craftengine.core.entity.furniture;
@Deprecated(since = "0.0.66", forRemoval = true)
public enum AnchorType {
GROUND(0),
WALL(1),

View File

@@ -1,60 +0,0 @@
package net.momirealms.craftengine.core.entity.furniture;
import net.momirealms.craftengine.core.loot.LootTable;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.plugin.context.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.context.function.Function;
import net.momirealms.craftengine.core.util.Key;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector3f;
import java.util.List;
import java.util.Map;
import java.util.Optional;
// TODO 家具的设计存在问题。家具也应该存在不同的状态,而不是根据放置规则直接决定状态类型
public interface CustomFurniture {
void execute(Context context, EventTrigger trigger);
Key id();
Map<AnchorType, Placement> placements();
FurnitureSettings settings();
@Nullable
LootTable<?> lootTable();
AnchorType getAnyAnchorType();
boolean isAllowedPlacement(AnchorType anchorType);
Placement getPlacement(AnchorType anchorType);
Placement getValidPlacement(AnchorType anchorType);
interface Builder {
Builder id(Key id);
Builder placement(Map<AnchorType, Placement> placements);
Builder settings(FurnitureSettings settings);
Builder lootTable(LootTable<?> lootTable);
Builder events(Map<EventTrigger, List<Function<Context>>> events);
CustomFurniture build();
}
record Placement(AnchorType anchorType,
FurnitureElement[] elements,
HitBoxConfig[] hitBoxConfigs,
RotationRule rotationRule,
AlignmentRule alignmentRule,
Optional<ExternalModel> externalModel,
Optional<Vector3f> dropOffset) {
}
}

View File

@@ -1,48 +1,28 @@
package net.momirealms.craftengine.core.entity.furniture;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.entity.CustomEntity;
import net.momirealms.craftengine.core.entity.CustomEntityType;
import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.sparrow.nbt.CompoundTag;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
public abstract class Furniture extends CustomEntity {
protected final FurnitureConfig config;
protected final FurnitureDataAccessor dataAccessor;
public interface Furniture {
void initializeColliders();
WorldPosition position();
boolean isValid();
void destroy();
void destroyColliders();
void destroySeats();
UUID uuid();
int baseEntityId();
@Nullable
HitBox hitBoxByEntityId(int id);
@Nullable HitBoxPart hitBoxPartByEntityId(int id);
public Furniture(CustomEntityType<?> type, WorldPosition position, FurnitureConfig config, CompoundTag data) {
super(type, position);
this.dataAccessor = new FurnitureDataAccessor(data);
this.config = config;
}
@NotNull
AnchorType anchorType();
public FurnitureConfig config() {
return this.config;
}
@NotNull
Key id();
@NotNull
CustomFurniture config();
boolean hasExternalModel();
FurnitureExtraData extraData();
void setExtraData(FurnitureExtraData extraData);
void save();
public FurnitureDataAccessor dataAccessor() {
return this.dataAccessor;
}
}

View File

@@ -0,0 +1,67 @@
package net.momirealms.craftengine.core.entity.furniture;
import net.momirealms.craftengine.core.entity.furniture.behavior.FurnitureBehavior;
import net.momirealms.craftengine.core.loot.LootTable;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.plugin.context.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.context.function.Function;
import net.momirealms.craftengine.core.plugin.entityculling.CullingData;
import net.momirealms.craftengine.core.util.Key;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
public interface FurnitureConfig {
void execute(Context context, EventTrigger trigger);
Key id();
FurnitureSettings settings();
@Nullable
LootTable<?> lootTable();
Map<String, FurnitureVariant> variants();
default FurnitureVariant anyVariant() {
return variants().values().stream().findFirst().get();
}
default String anyVariantName() {
return variants().keySet().stream().findFirst().get();
}
@Nullable
FurnitureVariant getVariant(String variantName);
@NotNull
FurnitureBehavior behavior();
CullingData cullingData();
static Builder builder() {
return new FurnitureConfigImpl.BuilderImpl();
}
interface Builder {
Builder id(Key id);
Builder variants(Map<String, FurnitureVariant> variants);
Builder settings(FurnitureSettings settings);
Builder lootTable(LootTable<?> lootTable);
Builder events(Map<EventTrigger, List<Function<Context>>> events);
Builder behavior(FurnitureBehavior behavior);
Builder cullingData(CullingData cullingData);
FurnitureConfig build();
}
}

View File

@@ -0,0 +1,145 @@
package net.momirealms.craftengine.core.entity.furniture;
import com.google.common.collect.ImmutableMap;
import net.momirealms.craftengine.core.entity.furniture.behavior.EmptyFurnitureBehavior;
import net.momirealms.craftengine.core.entity.furniture.behavior.FurnitureBehavior;
import net.momirealms.craftengine.core.loot.LootTable;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.plugin.context.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.context.function.Function;
import net.momirealms.craftengine.core.plugin.entityculling.CullingData;
import net.momirealms.craftengine.core.util.Key;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
class FurnitureConfigImpl implements FurnitureConfig {
private final Key id;
private final FurnitureSettings settings;
private final Map<String, FurnitureVariant> variants;
private final Map<EventTrigger, List<Function<Context>>> events;
private final FurnitureBehavior behavior;
private final CullingData cullingData;
@Nullable
private final LootTable<?> lootTable;
private FurnitureConfigImpl(@NotNull Key id,
@NotNull FurnitureSettings settings,
@NotNull Map<String, FurnitureVariant> variants,
@NotNull Map<EventTrigger, List<Function<Context>>> events,
@NotNull FurnitureBehavior behavior,
@Nullable CullingData cullingData,
@Nullable LootTable<?> lootTable) {
this.id = id;
this.settings = settings;
this.variants = ImmutableMap.copyOf(variants);
this.lootTable = lootTable;
this.behavior = behavior;
this.cullingData = cullingData;
this.events = events;
}
@Override
public void execute(Context context, EventTrigger trigger) {
for (Function<Context> function : Optional.ofNullable(this.events.get(trigger)).orElse(Collections.emptyList())) {
function.run(context);
}
}
@Override
public Key id() {
return this.id;
}
@Override
public FurnitureSettings settings() {
return this.settings;
}
@Override
public @Nullable LootTable<?> lootTable() {
return this.lootTable;
}
@Override
public Map<String, FurnitureVariant> variants() {
return this.variants;
}
@Override
public @NotNull FurnitureBehavior behavior() {
return this.behavior;
}
@Override
public CullingData cullingData() {
return this.cullingData;
}
@Nullable
@Override
public FurnitureVariant getVariant(String variantName) {
return this.variants.get(variantName);
}
public static class BuilderImpl implements Builder {
private Key id;
private Map<String, FurnitureVariant> variants;
private FurnitureSettings settings;
private Map<EventTrigger, List<Function<Context>>> events;
private LootTable<?> lootTable;
private FurnitureBehavior behavior = EmptyFurnitureBehavior.INSTANCE;
private CullingData cullingData;
@Override
public FurnitureConfig build() {
return new FurnitureConfigImpl(this.id, this.settings, this.variants, this.events, this.behavior, this.cullingData, this.lootTable);
}
@Override
public Builder id(Key id) {
this.id = id;
return this;
}
@Override
public Builder variants(Map<String, FurnitureVariant> variants) {
this.variants = variants;
return this;
}
@Override
public Builder settings(FurnitureSettings settings) {
this.settings = settings;
return this;
}
@Override
public Builder lootTable(LootTable<?> lootTable) {
this.lootTable = lootTable;
return this;
}
@Override
public Builder events(Map<EventTrigger, List<Function<Context>>> events) {
this.events = events;
return this;
}
@Override
public Builder cullingData(CullingData cullingData) {
this.cullingData = cullingData;
return this;
}
@Override
public Builder behavior(FurnitureBehavior behavior) {
this.behavior = behavior;
return this;
}
}
}

View File

@@ -6,34 +6,53 @@ import net.momirealms.craftengine.core.plugin.logger.Debugger;
import net.momirealms.craftengine.core.util.Color;
import net.momirealms.sparrow.nbt.CompoundTag;
import net.momirealms.sparrow.nbt.NBT;
import net.momirealms.sparrow.nbt.Tag;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.Optional;
public class FurnitureExtraData {
public class FurnitureDataAccessor {
public static final String ITEM = "item";
public static final String DYED_COLOR = "dyed_color";
public static final String FIREWORK_EXPLOSION_COLORS = "firework_explosion_colors";
public static final String VARIANT = "variant";
@ApiStatus.Obsolete
public static final String ANCHOR_TYPE = "anchor_type";
private final CompoundTag data;
public FurnitureExtraData(CompoundTag data) {
public FurnitureDataAccessor(CompoundTag data) {
this.data = data;
}
public static FurnitureExtraData of(CompoundTag data) {
return new FurnitureExtraData(data);
public static FurnitureDataAccessor of(CompoundTag data) {
return new FurnitureDataAccessor(data);
}
public CompoundTag copyTag() {
return this.data.copy();
}
@ApiStatus.Internal
public CompoundTag unsafeTag() {
return this.data;
}
public void addCustomData(String key, Tag value) {
this.data.put(key, value);
}
@Nullable
public Tag getCustomData(String key) {
return this.data.get(key);
}
public void removeCustomData(String key) {
this.data.remove(key);
}
public Optional<Item<?>> item() {
byte[] data = this.data.getByteArray(ITEM);
if (data == null) return Optional.empty();
@@ -45,73 +64,57 @@ public class FurnitureExtraData {
}
}
public void setItem(Item<?> item) {
this.data.putByteArray(ITEM, item.toByteArray());
}
public Optional<int[]> fireworkExplosionColors() {
if (this.data.containsKey(FIREWORK_EXPLOSION_COLORS)) return Optional.of(this.data.getIntArray(FIREWORK_EXPLOSION_COLORS));
return Optional.empty();
}
public void setFireworkExplosionColors(int[] colors) {
this.data.putIntArray(FIREWORK_EXPLOSION_COLORS, colors);
}
public Optional<Color> dyedColor() {
if (this.data.containsKey(DYED_COLOR)) return Optional.of(Color.fromDecimal(this.data.getInt(DYED_COLOR)));
return Optional.empty();
}
public void setDyedColor(Color color) {
this.data.putInt(DYED_COLOR, color.color());
}
public Optional<String> variant() {
return Optional.ofNullable(this.data.getString(VARIANT));
}
public void setVariant(String variant) {
this.data.putString(VARIANT, variant);
}
@ApiStatus.Obsolete
public Optional<AnchorType> anchorType() {
if (this.data.containsKey(ANCHOR_TYPE)) return Optional.of(AnchorType.byId(this.data.getInt(ANCHOR_TYPE)));
return Optional.empty();
}
public FurnitureExtraData anchorType(AnchorType type) {
@ApiStatus.Obsolete
public FurnitureDataAccessor anchorType(AnchorType type) {
this.data.putInt(ANCHOR_TYPE, type.getId());
return this;
}
public static Builder builder() {
return new Builder();
public static FurnitureDataAccessor fromBytes(final byte[] data) throws IOException {
return new FurnitureDataAccessor(NBT.fromBytes(data));
}
public static FurnitureExtraData fromBytes(final byte[] data) throws IOException {
return new FurnitureExtraData(NBT.fromBytes(data));
}
public static byte[] toBytes(final FurnitureExtraData data) throws IOException {
public static byte[] toBytes(final FurnitureDataAccessor data) throws IOException {
return NBT.toBytes(data.data);
}
public byte[] toBytes() throws IOException {
return toBytes(this);
}
public static class Builder {
private final CompoundTag data;
public Builder() {
this.data = new CompoundTag();
}
public Builder item(Item<?> item) {
this.data.putByteArray(ITEM, item.toByteArray());
return this;
}
public Builder dyedColor(Color color) {
if (color == null) return this;
this.data.putInt(DYED_COLOR, color.color());
return this;
}
public Builder fireworkExplosionColors(int[] colors) {
if (colors == null) return this;
this.data.putIntArray(FIREWORK_EXPLOSION_COLORS, colors);
return this;
}
public Builder anchorType(AnchorType type) {
this.data.putInt(ANCHOR_TYPE, type.getId());
return this;
}
public FurnitureExtraData build() {
return new FurnitureExtraData(data);
}
}
}

View File

@@ -1,59 +0,0 @@
package net.momirealms.craftengine.core.entity.furniture;
import net.momirealms.craftengine.core.entity.Billboard;
import net.momirealms.craftengine.core.entity.ItemDisplayContext;
import net.momirealms.craftengine.core.util.Key;
import org.jetbrains.annotations.NotNull;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import java.util.function.Consumer;
public interface FurnitureElement {
Quaternionf rotation();
Key item();
Billboard billboard();
ItemDisplayContext transform();
float shadowRadius();
float shadowStrength();
boolean applyDyedColor();
Vector3f scale();
Vector3f translation();
Vector3f position();
void initPackets(Furniture furniture, int entityId, @NotNull Quaternionf conjugated, Consumer<Object> packets);
interface Builder {
Builder item(Key item);
Builder billboard(Billboard billboard);
Builder transform(ItemDisplayContext transform);
Builder scale(Vector3f scale);
Builder translation(Vector3f translation);
Builder position(Vector3f position);
Builder rotation(Quaternionf rotation);
Builder applyDyedColor(boolean applyDyedColor);
Builder shadowStrength(float shadowStrength);
Builder shadowRadius(float shadowRadius);
FurnitureElement build();
}
}

View File

@@ -25,11 +25,11 @@ public interface FurnitureManager extends Manageable {
Collection<Suggestion> cachedSuggestions();
Furniture place(WorldPosition position, CustomFurniture furniture, FurnitureExtraData extraData, boolean playSound);
Furniture place(WorldPosition position, FurnitureConfig furniture, FurnitureDataAccessor extraData, boolean playSound);
Optional<CustomFurniture> furnitureById(Key id);
Optional<FurnitureConfig> furnitureById(Key id);
Map<Key, CustomFurniture> loadedFurniture();
Map<Key, FurnitureConfig> loadedFurniture();
boolean isFurnitureRealEntity(int entityId);

View File

@@ -1,11 +1,13 @@
package net.momirealms.craftengine.core.entity.furniture;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.util.CustomDataType;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
public class FurnitureSettings {
@@ -13,6 +15,7 @@ public class FurnitureSettings {
FurnitureSounds sounds = FurnitureSounds.EMPTY;
@Nullable
Key itemId;
Map<CustomDataType<?>, Object> customData = new IdentityHashMap<>(4);
private FurnitureSettings() {}
@@ -29,6 +32,7 @@ public class FurnitureSettings {
newSettings.sounds = settings.sounds;
newSettings.itemId = settings.itemId;
newSettings.minimized = settings.minimized;
newSettings.customData = new IdentityHashMap<>(settings.customData);
return newSettings;
}
@@ -45,6 +49,25 @@ public class FurnitureSettings {
return settings;
}
@SuppressWarnings("unchecked")
public <T> T getCustomData(CustomDataType<T> type) {
return (T) this.customData.get(type);
}
public void clearCustomData() {
this.customData.clear();
}
@Nullable
@SuppressWarnings("unchecked")
public <T> T removeCustomData(CustomDataType<?> type) {
return (T) this.customData.remove(type);
}
public <T> void addCustomData(CustomDataType<T> key, T value) {
this.customData.put(key, value);
}
public FurnitureSounds sounds() {
return sounds;
}
@@ -103,7 +126,7 @@ public class FurnitureSettings {
}));
}
private static void registerFactory(String id, FurnitureSettings.Modifier.Factory factory) {
public static void registerFactory(String id, FurnitureSettings.Modifier.Factory factory) {
FACTORIES.put(id, factory);
}
}

View File

@@ -0,0 +1,13 @@
package net.momirealms.craftengine.core.entity.furniture;
import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig;
import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfig;
import org.joml.Vector3f;
import java.util.Optional;
public record FurnitureVariant(FurnitureElementConfig<?>[] elements,
HitBoxConfig[] hitBoxConfigs,
Optional<ExternalModel> externalModel,
Optional<Vector3f> dropOffset) {
}

View File

@@ -1,34 +0,0 @@
package net.momirealms.craftengine.core.entity.furniture;
import net.momirealms.craftengine.core.entity.seat.SeatConfig;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.craftengine.core.world.collision.AABB;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
public interface HitBoxConfig {
Key type();
void initPacketsAndColliders(int[] entityId, WorldPosition position, Quaternionf conjugated,
BiConsumer<Object, Boolean> packets, Consumer<Collider> collider, Consumer<HitBoxPart> aabb);
void initShapeForPlacement(double x, double y, double z, float yaw, Quaternionf conjugated, Consumer<AABB> aabbs);
int[] acquireEntityIds(Supplier<Integer> entityIdSupplier);
SeatConfig[] seats();
Vector3f position();
boolean blocksBuilding();
boolean canBeHitByProjectile();
boolean canUseItemOn();
}

View File

@@ -0,0 +1,7 @@
package net.momirealms.craftengine.core.entity.furniture.behavior;
public final class EmptyFurnitureBehavior implements FurnitureBehavior {
private EmptyFurnitureBehavior() {}
public static final EmptyFurnitureBehavior INSTANCE = new EmptyFurnitureBehavior();
}

View File

@@ -0,0 +1,15 @@
package net.momirealms.craftengine.core.entity.furniture.behavior;
import net.momirealms.craftengine.core.entity.furniture.Furniture;
import net.momirealms.craftengine.core.entity.furniture.tick.FurnitureTicker;
public interface FurnitureBehavior {
default <T extends Furniture> FurnitureTicker<T> createSyncFurnitureTicker(T furniture) {
return null;
}
default <T extends Furniture> FurnitureTicker<T> createAsyncBlockEntityTicker(T furniture) {
return null;
}
}

View File

@@ -0,0 +1,14 @@
package net.momirealms.craftengine.core.entity.furniture.element;
import net.momirealms.craftengine.core.entity.player.Player;
public interface FurnitureElement {
void show(Player player);
void hide(Player player);
default void deactivate() {}
default void activate() {}
}

View File

@@ -0,0 +1,9 @@
package net.momirealms.craftengine.core.entity.furniture.element;
import net.momirealms.craftengine.core.world.WorldPosition;
import org.jetbrains.annotations.NotNull;
public interface FurnitureElementConfig<E extends FurnitureElement> {
E create(@NotNull WorldPosition position);
}

View File

@@ -0,0 +1,8 @@
package net.momirealms.craftengine.core.entity.furniture.element;
import java.util.Map;
public interface FurnitureElementConfigFactory {
<E extends FurnitureElement> FurnitureElementConfig<E> create(Map<String, Object> args);
}

View File

@@ -0,0 +1,31 @@
package net.momirealms.craftengine.core.entity.furniture.element;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.registry.Registries;
import net.momirealms.craftengine.core.registry.WritableRegistry;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.ResourceKey;
import java.util.Map;
import java.util.Optional;
public class FurnitureElementConfigs {
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, FurnitureElementConfigFactory type) {
((WritableRegistry<FurnitureElementConfigFactory>) BuiltInRegistries.FURNITURE_ELEMENT_TYPE)
.register(ResourceKey.create(Registries.FURNITURE_ELEMENT_TYPE.location(), key), type);
}
public static <E extends FurnitureElement> FurnitureElementConfig<E> fromMap(Map<String, Object> arguments) {
Key type = Optional.ofNullable(arguments.get("type")).map(String::valueOf).map(it -> Key.withDefaultNamespace(it, "craftengine")).orElse(ITEM_DISPLAY);
FurnitureElementConfigFactory factory = BuiltInRegistries.FURNITURE_ELEMENT_TYPE.getValue(type);
if (factory == null) {
throw new LocalizedResourceConfigException("warning.config.furniture.element.invalid_type", type.toString());
}
return factory.create(arguments);
}
}

View File

@@ -0,0 +1,56 @@
package net.momirealms.craftengine.core.entity.furniture.hitbox;
import net.momirealms.craftengine.core.entity.furniture.Furniture;
import net.momirealms.craftengine.core.entity.seat.Seat;
import net.momirealms.craftengine.core.world.EntityHitResult;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.sparrow.nbt.CompoundTag;
import java.util.Optional;
public abstract class AbstractHitBox implements HitBox {
private final Furniture furniture;
private final HitBoxConfig config;
private final HitBoxPart[] parts;
private Seat<HitBox>[] seats;
public AbstractHitBox(Furniture furniture, HitBoxConfig config, HitBoxPart[] parts) {
this.parts = parts;
this.config = config;
this.furniture = furniture;
}
protected abstract void createSeats(HitBoxConfig config);
@Override
public HitBoxPart[] parts() {
return this.parts;
}
@Override
public HitBoxConfig config() {
return this.config;
}
@Override
public Seat<HitBox>[] seats() {
return this.seats;
}
@Override
public Optional<EntityHitResult> clip(Vec3d min, Vec3d max) {
for (HitBoxPart hbe : this.parts) {
Optional<EntityHitResult> result = hbe.aabb().clip(min, max);
if (result.isPresent()) {
return result;
}
}
return Optional.empty();
}
@Override
public void saveCustomData(CompoundTag data) {
data.putString("type", "furniture");
data.putInt("entity_id", this.furniture.entityId());
}
}

View File

@@ -1,4 +1,4 @@
package net.momirealms.craftengine.core.entity.furniture;
package net.momirealms.craftengine.core.entity.furniture.hitbox;
import net.momirealms.craftengine.core.entity.seat.SeatConfig;
import org.joml.Vector3f;
@@ -30,16 +30,16 @@ public abstract class AbstractHitBoxConfig implements HitBoxConfig {
@Override
public boolean blocksBuilding() {
return blocksBuilding;
return this.blocksBuilding;
}
@Override
public boolean canBeHitByProjectile() {
return canBeHitByProjectile;
return this.canBeHitByProjectile;
}
@Override
public boolean canUseItemOn() {
return canUseItemOn;
return this.canUseItemOn;
}
}

View File

@@ -1,4 +1,4 @@
package net.momirealms.craftengine.core.entity.furniture;
package net.momirealms.craftengine.core.entity.furniture.hitbox;
import net.momirealms.craftengine.core.entity.seat.Seat;
import net.momirealms.craftengine.core.entity.seat.SeatOwner;

View File

@@ -0,0 +1,20 @@
package net.momirealms.craftengine.core.entity.furniture.hitbox;
import net.momirealms.craftengine.core.entity.seat.SeatConfig;
import net.momirealms.craftengine.core.util.Key;
import org.joml.Vector3f;
public interface HitBoxConfig {
Key type();
SeatConfig[] seats();
Vector3f position();
boolean blocksBuilding();
boolean canBeHitByProjectile();
boolean canUseItemOn();
}

View File

@@ -1,4 +1,4 @@
package net.momirealms.craftengine.core.entity.furniture;
package net.momirealms.craftengine.core.entity.furniture.hitbox;
import java.util.Map;

View File

@@ -1,4 +1,4 @@
package net.momirealms.craftengine.core.entity.furniture;
package net.momirealms.craftengine.core.entity.furniture.hitbox;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.collision.AABB;

View File

@@ -1,4 +1,4 @@
package net.momirealms.craftengine.core.entity.furniture;
package net.momirealms.craftengine.core.entity.furniture.hitbox;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
@@ -14,6 +14,7 @@ public class HitBoxTypes {
public static final Key INTERACTION = Key.of("minecraft:interaction");
public static final Key SHULKER = Key.of("minecraft:shulker");
public static final Key HAPPY_GHAST = Key.of("minecraft:happy_ghast");
public static final Key VIRTUAL = Key.of("minecraft:virtual");
public static final Key CUSTOM = Key.of("minecraft:custom");
public static void register(Key key, HitBoxConfigFactory factory) {

View File

@@ -0,0 +1,8 @@
package net.momirealms.craftengine.core.entity.furniture.tick;
import net.momirealms.craftengine.core.entity.furniture.Furniture;
public interface FurnitureTicker<T extends Furniture> {
void tick(T furniture);
}

View File

@@ -1,4 +1,4 @@
package net.momirealms.craftengine.core.entity;
package net.momirealms.craftengine.core.entity.item;
import net.momirealms.craftengine.core.item.Item;

View File

@@ -1,7 +1,7 @@
package net.momirealms.craftengine.core.entity.projectile;
import net.momirealms.craftengine.core.entity.Billboard;
import net.momirealms.craftengine.core.entity.ItemDisplayContext;
import net.momirealms.craftengine.core.entity.display.Billboard;
import net.momirealms.craftengine.core.entity.display.ItemDisplayContext;
import net.momirealms.craftengine.core.util.Key;
import org.joml.Quaternionf;
import org.joml.Vector3f;

View File

@@ -28,7 +28,7 @@ import java.util.Optional;
* This interface provides methods for managing item properties such as custom model data,
* damage, display name, lore, enchantments, and tags.
*
* @param <I> the type of the item implementation
* @param <I> the id of the item implementation
*/
public interface Item<I> {

View File

@@ -1,7 +1,7 @@
package net.momirealms.craftengine.core.item;
import net.momirealms.craftengine.core.entity.Billboard;
import net.momirealms.craftengine.core.entity.ItemDisplayContext;
import net.momirealms.craftengine.core.entity.display.Billboard;
import net.momirealms.craftengine.core.entity.display.ItemDisplayContext;
import net.momirealms.craftengine.core.entity.projectile.ProjectileMeta;
import net.momirealms.craftengine.core.item.equipment.ComponentBasedEquipment;
import net.momirealms.craftengine.core.item.equipment.Equipment;
@@ -134,6 +134,12 @@ public class ItemSettings {
this.customData.clear();
}
@Nullable
@SuppressWarnings("unchecked")
public <T> T removeCustomData(CustomDataType<?> type) {
return (T) this.customData.remove(type);
}
public <T> void addCustomData(CustomDataType<T> key, T value) {
this.customData.put(key, value);
}

View File

@@ -190,7 +190,7 @@ public class CustomSmithingTransformRecipe<T> extends AbstractedFixedResultRecip
@Override
public CustomSmithingTransformRecipe<A> readMap(Key id, Map<String, Object> arguments) {
List<String> base = MiscUtils.getAsStringList(arguments.get("base"));
List<String> template = MiscUtils.getAsStringList(arguments.get("template-type"));
List<String> template = MiscUtils.getAsStringList(arguments.get("template-id"));
List<String> addition = MiscUtils.getAsStringList(arguments.get("addition"));
boolean mergeComponents = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("merge-components", true), "merge-components");
boolean mergeEnchantments = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("merge-enchantments", false), "merge-enchantments");

View File

@@ -141,7 +141,7 @@ public class CustomSmithingTrimRecipe<T> extends AbstractRecipe<T>
@Override
public CustomSmithingTrimRecipe<A> readMap(Key id, Map<String, Object> arguments) {
List<String> base = MiscUtils.getAsStringList(arguments.get("base"));
List<String> template = MiscUtils.getAsStringList(arguments.get("template-type"));
List<String> template = MiscUtils.getAsStringList(arguments.get("template-id"));
List<String> addition = MiscUtils.getAsStringList(arguments.get("addition"));
Key pattern = VersionHelper.isOrAbove1_21_5() ? Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("pattern"), "warning.config.recipe.smithing_trim.missing_pattern")) : null;
return new CustomSmithingTrimRecipe<>(id,

View File

@@ -87,12 +87,12 @@ public class ApplyBonusCountFunction<T> extends AbstractLootConditionalFunction<
public static Formula fromMap(Map<String, Object> map) {
String type = (String) map.get("type");
if (type == null) {
throw new NullPointerException("number type cannot be null");
throw new NullPointerException("number id cannot be null");
}
Key key = Key.withDefaultNamespace(type, Key.DEFAULT_NAMESPACE);
FormulaFactory factory = BuiltInRegistries.FORMULA_FACTORY.getValue(key);
if (factory == null) {
throw new IllegalArgumentException("Unknown formula type: " + type);
throw new IllegalArgumentException("Unknown formula id: " + type);
}
return factory.create(map);
}

View File

@@ -75,7 +75,7 @@ public class ItemModels {
Key key = Key.withDefaultNamespace(type, "minecraft");
ItemModelReader reader = BuiltInRegistries.ITEM_MODEL_READER.getValue(key);
if (reader == null) {
throw new IllegalArgumentException("Invalid item model type: " + key);
throw new IllegalArgumentException("Invalid item model id: " + key);
}
return reader.read(json);
}

View File

@@ -80,7 +80,7 @@ public class ConditionProperties {
Key key = Key.withDefaultNamespace(type, "minecraft");
ConditionPropertyReader reader = BuiltInRegistries.CONDITION_PROPERTY_READER.getValue(key);
if (reader == null) {
throw new IllegalArgumentException("Invalid condition property type: " + key);
throw new IllegalArgumentException("Invalid condition property id: " + key);
}
return reader.read(json);
}

View File

@@ -71,7 +71,7 @@ public class RangeDispatchProperties {
Key key = Key.withDefaultNamespace(type, "minecraft");
RangeDispatchPropertyReader reader = BuiltInRegistries.RANGE_DISPATCH_PROPERTY_READER.getValue(key);
if (reader == null) {
throw new IllegalArgumentException("Invalid range dispatch property type: " + key);
throw new IllegalArgumentException("Invalid range dispatch property id: " + key);
}
return reader.read(json);
}

View File

@@ -71,7 +71,7 @@ public class SelectProperties {
Key key = Key.withDefaultNamespace(type, "minecraft");
SelectPropertyReader reader = BuiltInRegistries.SELECT_PROPERTY_READER.getValue(key);
if (reader == null) {
throw new IllegalArgumentException("Invalid select property type: " + key);
throw new IllegalArgumentException("Invalid select property id: " + key);
}
return reader.read(json);
}

View File

@@ -45,7 +45,7 @@ public class SignSpecialModel implements SpecialModel {
@Override
public SpecialModel create(Map<String, Object> arguments) {
Key type = Key.of(arguments.get("type").toString());
String woodType = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("wood-type"), "warning.config.item.model.special.sign.missing_wood_type");
String woodType = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("wood-id"), "warning.config.item.model.special.sign.missing_wood_type");
String texture = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("texture"), "warning.config.item.model.special.sign.missing_texture");
return new SignSpecialModel(type, woodType, texture);
}

View File

@@ -80,7 +80,7 @@ public class SpecialModels {
Key key = Key.withDefaultNamespace(type, "minecraft");
SpecialModelReader reader = BuiltInRegistries.SPECIAL_MODEL_READER.getValue(key);
if (reader == null) {
throw new IllegalArgumentException("Invalid special model type: " + key);
throw new IllegalArgumentException("Invalid special model id: " + key);
}
return reader.read(json);
}

View File

@@ -65,7 +65,7 @@ public class Tints {
Key key = Key.withDefaultNamespace(type, "minecraft");
TintReader reader = BuiltInRegistries.TINT_READER.getValue(key);
if (reader == null) {
throw new IllegalArgumentException("Invalid tint type: " + type);
throw new IllegalArgumentException("Invalid tint id: " + type);
}
return reader.read(json);
}

View File

@@ -36,7 +36,7 @@ public enum ObfA {
return type;
}
}
throw new IllegalArgumentException("Unknown resource type: " + xclf);
throw new IllegalArgumentException("Unknown resource id: " + xclf);
}
public static final byte[] VALUES = new byte[] {

View File

@@ -9,7 +9,7 @@ import java.util.UUID;
/**
* Simple implementation of {@link Sender} using a {@link SenderFactory}
*
* @param <T> the command sender type
* @param <T> the command sender id
*/
public final class AbstractSender<T> implements Sender {
private final Plugin plugin;

View File

@@ -448,10 +448,10 @@ public class Config {
// furniture
furniture$hide_base_entity = config.getBoolean("furniture.hide-base-entity", true);
furniture$collision_entity_type = ColliderType.valueOf(config.getString("furniture.collision-entity-type", "interaction").toUpperCase(Locale.ENGLISH));
furniture$collision_entity_type = ColliderType.valueOf(config.getString("furniture.collision-entity-id", "interaction").toUpperCase(Locale.ENGLISH));
// equipment
equipment$sacrificed_vanilla_armor$type = config.getString("equipment.sacrificed-vanilla-armor.type", "chainmail").toLowerCase(Locale.ENGLISH);
equipment$sacrificed_vanilla_armor$type = config.getString("equipment.sacrificed-vanilla-armor.id", "chainmail").toLowerCase(Locale.ENGLISH);
if (!AbstractPackManager.ALLOWED_VANILLA_EQUIPMENT.contains(equipment$sacrificed_vanilla_armor$type)) {
TranslationManager.instance().log("warning.config.equipment.invalid_sacrificed_armor", equipment$sacrificed_vanilla_armor$type);
equipment$sacrificed_vanilla_armor$type = "chainmail";

View File

@@ -259,7 +259,7 @@ public class StringKeyConstructor extends SafeConstructor {
if (value instanceof Number number) {
return number.byteValue();
}
throw new RuntimeException("Unexpected type: " + value.getClass().getName());
throw new RuntimeException("Unexpected id: " + value.getClass().getName());
}
}
@@ -271,7 +271,7 @@ public class StringKeyConstructor extends SafeConstructor {
if (value instanceof Number number) {
return number.shortValue();
}
throw new RuntimeException("Unexpected type: " + value.getClass().getName());
throw new RuntimeException("Unexpected id: " + value.getClass().getName());
}
}
@@ -283,7 +283,7 @@ public class StringKeyConstructor extends SafeConstructor {
if (value instanceof Number number) {
return number.longValue();
}
throw new RuntimeException("Unexpected type: " + value.getClass().getName());
throw new RuntimeException("Unexpected id: " + value.getClass().getName());
}
}
@@ -295,7 +295,7 @@ public class StringKeyConstructor extends SafeConstructor {
if (value instanceof Number number) {
return number.floatValue();
}
throw new RuntimeException("Unexpected type: " + value.getClass().getName());
throw new RuntimeException("Unexpected id: " + value.getClass().getName());
}
}
@@ -307,7 +307,7 @@ public class StringKeyConstructor extends SafeConstructor {
if (value instanceof Number number) {
return number.doubleValue();
}
throw new RuntimeException("Unexpected type: " + value.getClass().getName());
throw new RuntimeException("Unexpected id: " + value.getClass().getName());
}
}
}

View File

@@ -61,7 +61,7 @@ public class ExpressionTemplateArgument implements TemplateArgument {
public TemplateArgument create(Map<String, Object> arguments) {
return new ExpressionTemplateArgument(
arguments.getOrDefault("expression", "").toString(),
ValueType.valueOf(arguments.getOrDefault("value-type", "double").toString().toUpperCase(Locale.ROOT))
ValueType.valueOf(arguments.getOrDefault("value-id", "double").toString().toUpperCase(Locale.ROOT))
);
}
}

View File

@@ -58,7 +58,7 @@ public class TemplateArguments {
Key key = Key.withDefaultNamespace(type0, Key.DEFAULT_NAMESPACE);
TemplateArgumentFactory factory = BuiltInRegistries.TEMPLATE_ARGUMENT_FACTORY.getValue(key);
if (factory == null) {
throw new IllegalArgumentException("Unknown argument type: " + type);
throw new IllegalArgumentException("Unknown argument id: " + type);
}
return factory.create(map);
}

View File

@@ -43,7 +43,7 @@ public class DamageFunction<CTX extends Context> extends AbstractConditionalFunc
@Override
public Function<CTX> create(Map<String, Object> arguments) {
PlayerSelector<CTX> selector = PlayerSelectors.fromObject(arguments.getOrDefault("target", "self"), conditionFactory());
Key damageType = Key.of(ResourceConfigUtils.getAsStringOrNull(arguments.getOrDefault("damage-type", "generic")));
Key damageType = Key.of(ResourceConfigUtils.getAsStringOrNull(arguments.getOrDefault("damage-id", "generic")));
NumberProvider amount = NumberProviders.fromObject(arguments.getOrDefault("amount", 1f));
return new DamageFunction<>(selector, damageType, amount, getPredicates(arguments));
}

View File

@@ -65,7 +65,7 @@ public class OpenWindowFunction<CTX extends Context> extends AbstractConditional
@Override
public Function<CTX> create(Map<String, Object> arguments) {
String title = Optional.ofNullable(arguments.get("title")).map(String::valueOf).orElse(null);
String rawType = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("gui-type"), "warning.config.function.open_window.missing_gui_type");
String rawType = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("gui-id"), "warning.config.function.open_window.missing_gui_type");
try {
GuiType type = GuiType.valueOf(rawType.toUpperCase(Locale.ENGLISH));
return new OpenWindowFunction<>(getPredicates(arguments), PlayerSelectors.fromObject(arguments.get("target"), conditionFactory()), type, title);

View File

@@ -46,7 +46,7 @@ public class RemoveFurnitureFunction<CTX extends Context> extends AbstractCondit
ContextHolder.Builder builder = ContextHolder.builder()
.withParameter(DirectContextParameters.POSITION, position)
.withParameter(DirectContextParameters.FURNITURE, furniture)
.withOptionalParameter(DirectContextParameters.FURNITURE_ITEM, furniture.extraData().item().orElse(null));
.withOptionalParameter(DirectContextParameters.FURNITURE_ITEM, furniture.dataAccessor().item().orElse(null));
Optional<Player> optionalPlayer = ctx.getOptionalParameter(DirectContextParameters.PLAYER);
Player player = optionalPlayer.orElse(null);
if (player != null) {

View File

@@ -94,7 +94,7 @@ public class ReplaceFurnitureFunction<CTX extends Context> extends AbstractCondi
NumberProvider z = NumberProviders.fromObject(arguments.getOrDefault("z", "<arg:furniture.z>"));
NumberProvider pitch = NumberProviders.fromObject(arguments.getOrDefault("pitch", "<arg:furniture.pitch>"));
NumberProvider yaw = NumberProviders.fromObject(arguments.getOrDefault("yaw", "<arg:furniture.yaw>"));
AnchorType anchorType = ResourceConfigUtils.getAsEnum(arguments.get("anchor-type"), AnchorType.class, null);
AnchorType anchorType = ResourceConfigUtils.getAsEnum(arguments.get("anchor-id"), AnchorType.class, null);
boolean dropLoot = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("drop-loot", true), "drop-loot");
boolean playSound = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("play-sound", true), "play-sound");
return new ReplaceFurnitureFunction<>(furnitureId, x, y, z, pitch, yaw, anchorType, dropLoot, playSound, getPredicates(arguments));

View File

@@ -1,8 +1,5 @@
package net.momirealms.craftengine.core.plugin.context.function;
import net.momirealms.craftengine.core.entity.furniture.AnchorType;
import net.momirealms.craftengine.core.entity.furniture.FurnitureExtraData;
import net.momirealms.craftengine.core.plugin.CraftEngine;
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;
@@ -15,7 +12,6 @@ import net.momirealms.craftengine.core.world.WorldPosition;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class SpawnFurnitureFunction<CTX extends Context> extends AbstractConditionalFunction<CTX> {
private final Key furnitureId;
@@ -24,7 +20,7 @@ public class SpawnFurnitureFunction<CTX extends Context> extends AbstractConditi
private final NumberProvider z;
private final NumberProvider pitch;
private final NumberProvider yaw;
private final AnchorType anchorType;
private final String variant;
private final boolean playSound;
public SpawnFurnitureFunction(
@@ -34,7 +30,7 @@ public class SpawnFurnitureFunction<CTX extends Context> extends AbstractConditi
NumberProvider z,
NumberProvider pitch,
NumberProvider yaw,
AnchorType anchorType,
String variant,
boolean playSound,
List<Condition<CTX>> predicates
) {
@@ -45,7 +41,7 @@ public class SpawnFurnitureFunction<CTX extends Context> extends AbstractConditi
this.z = z;
this.pitch = pitch;
this.yaw = yaw;
this.anchorType = anchorType;
this.variant = variant;
this.playSound = playSound;
}
@@ -59,17 +55,18 @@ public class SpawnFurnitureFunction<CTX extends Context> extends AbstractConditi
float pitchValue = this.pitch.getFloat(ctx);
float yawValue = this.yaw.getFloat(ctx);
WorldPosition position = new WorldPosition(world, xPos, yPos, zPos, pitchValue, yawValue);
spawnFurniture(this.furnitureId, position, this.anchorType, this.playSound);
// fixme api
// spawnFurniture(this.furnitureId, position, this.anchorType, this.playSound);
});
}
public static void spawnFurniture(Key furnitureId, WorldPosition position, AnchorType anchorType, boolean playSound) {
CraftEngine.instance().furnitureManager().furnitureById(furnitureId).ifPresent(furniture -> {
AnchorType anchor = Optional.ofNullable(anchorType).orElse(furniture.getAnyAnchorType());
FurnitureExtraData extraData = FurnitureExtraData.builder().anchorType(anchor).build();
CraftEngine.instance().furnitureManager().place(position, furniture, extraData, playSound);
});
}
// public static void spawnFurniture(Key furnitureId, WorldPosition position, AnchorType anchorType, boolean playSound) {
// CraftEngine.instance().furnitureManager().furnitureById(furnitureId).ifPresent(furniture -> {
// AnchorType anchor = Optional.ofNullable(anchorType).orElse(furniture.getAnyAnchorType());
// FurnitureDataAccessor extraData = FurnitureDataAccessor.builder().anchorType(anchor).build();
// CraftEngine.instance().furnitureManager().place(position, furniture, extraData, playSound);
// });
// }
@Override
public Key type() {
@@ -90,9 +87,9 @@ public class SpawnFurnitureFunction<CTX extends Context> extends AbstractConditi
NumberProvider z = NumberProviders.fromObject(arguments.getOrDefault("z", "<arg:position.z>"));
NumberProvider pitch = NumberProviders.fromObject(arguments.getOrDefault("pitch", "<arg:position.pitch>"));
NumberProvider yaw = NumberProviders.fromObject(arguments.getOrDefault("yaw", "<arg:position.yaw>"));
AnchorType anchorType = ResourceConfigUtils.getAsEnum(arguments.get("anchor-type"), AnchorType.class, null);
String variant = ResourceConfigUtils.getAsStringOrNull(ResourceConfigUtils.get(arguments, "variant", "anchor-id"));
boolean playSound = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("play-sound", true), "play-sound");
return new SpawnFurnitureFunction<>(furnitureId, x, y, z, pitch, yaw, anchorType, playSound, getPredicates(arguments));
return new SpawnFurnitureFunction<>(furnitureId, x, y, z, pitch, yaw, variant, playSound, getPredicates(arguments));
}
}
}

View File

@@ -63,7 +63,7 @@ public class ToastFunction<CTX extends Context> extends AbstractConditionalFunct
@Override
public Function<CTX> create(Map<String, Object> arguments) {
AdvancementType advancementType;
String advancementName = arguments.getOrDefault("advancement-type", "goal").toString();
String advancementName = arguments.getOrDefault("advancement-id", "goal").toString();
try {
advancementType = AdvancementType.valueOf(advancementName.toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException e) {

View File

@@ -1,7 +1,7 @@
package net.momirealms.craftengine.core.plugin.context.parameter;
import net.momirealms.craftengine.core.entity.Entity;
import net.momirealms.craftengine.core.entity.ItemEntity;
import net.momirealms.craftengine.core.entity.item.ItemEntity;
import net.momirealms.craftengine.core.plugin.context.ChainParameterProvider;
import net.momirealms.craftengine.core.plugin.context.ContextKey;
import net.momirealms.craftengine.core.util.MiscUtils;

View File

@@ -53,7 +53,7 @@ public class LangData {
temp.put(result, entry.getValue());
}
},
() -> CraftEngine.instance().logger().warn("Unknown lang type: " + key)
() -> CraftEngine.instance().logger().warn("Unknown lang id: " + key)
);
} else {
temp.put(key, entry.getValue());

View File

@@ -5,7 +5,9 @@ import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.entity.BlockEntityType;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory;
import net.momirealms.craftengine.core.block.properties.PropertyFactory;
import net.momirealms.craftengine.core.entity.furniture.HitBoxConfigFactory;
import net.momirealms.craftengine.core.entity.CustomEntityType;
import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigFactory;
import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfigFactory;
import net.momirealms.craftengine.core.item.ItemDataModifierFactory;
import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory;
import net.momirealms.craftengine.core.item.equipment.EquipmentFactory;
@@ -88,9 +90,11 @@ public class BuiltInRegistries {
public static final Registry<PostProcessor.Type<?>> RECIPE_POST_PROCESSOR_TYPE = createConstantBoundRegistry(Registries.RECIPE_POST_PROCESSOR_TYPE, 16);
public static final Registry<ItemUpdaterType<?>> ITEM_UPDATER_TYPE = createConstantBoundRegistry(Registries.ITEM_UPDATER_TYPE, 16);
public static final Registry<NetworkCodec<FriendlyByteBuf, ? extends ModPacket>> MOD_PACKET = createConstantBoundRegistry(Registries.MOD_PACKET, 16);
public static final Registry<BlockEntityType<?>> BLOCK_ENTITY_TYPE = createConstantBoundRegistry(Registries.BLOCK_ENTITY_TYPE, 128);
public static final Registry<BlockEntityType<?>> BLOCK_ENTITY_TYPE = createConstantBoundRegistry(Registries.BLOCK_ENTITY_TYPE, 64);
public static final Registry<BlockEntityElementConfigFactory> BLOCK_ENTITY_ELEMENT_TYPE = createConstantBoundRegistry(Registries.BLOCK_ENTITY_ELEMENT_TYPE, 16);
public static final Registry<CraftRemainderFactory> CRAFT_REMAINDER_FACTORY = createConstantBoundRegistry(Registries.CRAFT_REMAINDER_FACTORY, 16);
public static final Registry<CustomEntityType<?>> ENTITY_TYPE = createConstantBoundRegistry(Registries.ENTITY_TYPE, 32);
public static final Registry<FurnitureElementConfigFactory> FURNITURE_ELEMENT_TYPE = createConstantBoundRegistry(Registries.FURNITURE_ELEMENT_TYPE, 16);
private static <T> Registry<T> createConstantBoundRegistry(ResourceKey<? extends Registry<T>> key, int expectedSize) {
return new ConstantBoundRegistry<>(key, expectedSize);

View File

@@ -5,7 +5,9 @@ import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.entity.BlockEntityType;
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory;
import net.momirealms.craftengine.core.block.properties.PropertyFactory;
import net.momirealms.craftengine.core.entity.furniture.HitBoxConfigFactory;
import net.momirealms.craftengine.core.entity.CustomEntityType;
import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigFactory;
import net.momirealms.craftengine.core.entity.furniture.hitbox.HitBoxConfigFactory;
import net.momirealms.craftengine.core.item.ItemDataModifierFactory;
import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory;
import net.momirealms.craftengine.core.item.equipment.EquipmentFactory;
@@ -93,4 +95,6 @@ public class Registries {
public static final ResourceKey<Registry<BlockEntityType<?>>> BLOCK_ENTITY_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("block_entity_type"));
public static final ResourceKey<Registry<BlockEntityElementConfigFactory>> BLOCK_ENTITY_ELEMENT_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("block_entity_element_type"));
public static final ResourceKey<Registry<CraftRemainderFactory>> CRAFT_REMAINDER_FACTORY = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("craft_remainder_factory"));
public static final ResourceKey<Registry<CustomEntityType<?>>> ENTITY_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("entity_type"));
public static final ResourceKey<Registry<FurnitureElementConfigFactory>> FURNITURE_ELEMENT_TYPE = ResourceKey.create(ROOT_REGISTRY, Key.withDefaultNamespace("furniture_element_type"));
}

View File

@@ -22,7 +22,7 @@ public record SoundData(Key id, SoundValue volume, SoundValue pitch) {
SoundValue pitchValue = Optional.ofNullable(SoundValue.of(map.get("pitch"))).orElse(volume);
return new SoundData(id, volumeValue, pitchValue);
} else {
throw new IllegalArgumentException("Illegal object type for sound data: " + obj.getClass());
throw new IllegalArgumentException("Illegal object id for sound data: " + obj.getClass());
}
}

View File

@@ -6,8 +6,8 @@ import java.util.Objects;
* A generic class representing a pair of values.
* This class provides methods to create and access pairs of values.
*
* @param <L> the type of the left value
* @param <R> the type of the right value
* @param <L> the id of the left value
* @param <R> the id of the right value
*/
public record Pair<L, R>(L left, R right) {
@@ -16,8 +16,8 @@ public record Pair<L, R>(L left, R right) {
*
* @param left the left value
* @param right the right value
* @param <L> the type of the left value
* @param <R> the type of the right value
* @param <L> the id of the left value
* @param <R> the id of the right value
* @return a new {@link Pair} with the specified values
*/
public static <L, R> Pair<L, R> of(final L left, final R right) {

View File

@@ -439,7 +439,7 @@ public class ReflectionUtils {
public static List<Method> getMethods(@NotNull Class<?> clazz, @NotNull Class<?> returnType, @NotNull Class<?>... parameterTypes) {
List<Method> list = new ArrayList<>();
for (Method method : clazz.getMethods()) {
if (!returnType.isAssignableFrom(method.getReturnType()) // check type
if (!returnType.isAssignableFrom(method.getReturnType()) // check id
|| method.getParameterCount() != parameterTypes.length // check length
) continue;
Class<?>[] types = method.getParameterTypes();

View File

@@ -137,13 +137,13 @@ public final class ResourceConfigUtils {
try {
return Integer.parseInt(s.replace("_", ""));
} catch (NumberFormatException e) {
throw new LocalizedResourceConfigException("warning.config.type.int", e, s, option);
throw new LocalizedResourceConfigException("warning.config.id.int", e, s, option);
}
}
case Boolean b -> {
return b ? 1 : 0;
}
default -> throw new LocalizedResourceConfigException("warning.config.type.int", o.toString(), option);
default -> throw new LocalizedResourceConfigException("warning.config.id.int", o.toString(), option);
}
}
@@ -162,11 +162,11 @@ public final class ResourceConfigUtils {
try {
return Double.parseDouble(s);
} catch (NumberFormatException e) {
throw new LocalizedResourceConfigException("warning.config.type.double", e, s, option);
throw new LocalizedResourceConfigException("warning.config.id.double", e, s, option);
}
}
default -> {
throw new LocalizedResourceConfigException("warning.config.type.double", o.toString(), option);
throw new LocalizedResourceConfigException("warning.config.id.double", o.toString(), option);
}
}
}
@@ -183,14 +183,14 @@ public final class ResourceConfigUtils {
try {
return Float.parseFloat(s);
} catch (NumberFormatException e) {
throw new LocalizedResourceConfigException("warning.config.type.float", e, s, option);
throw new LocalizedResourceConfigException("warning.config.id.float", e, s, option);
}
}
case Number number -> {
return number.floatValue();
}
default -> {
throw new LocalizedResourceConfigException("warning.config.type.float", o.toString(), option);
throw new LocalizedResourceConfigException("warning.config.id.float", o.toString(), option);
}
}
}
@@ -206,15 +206,15 @@ public final class ResourceConfigUtils {
case Number n -> {
if (n.byteValue() == 0) return false;
if (n.byteValue() == 1) return true;
throw new LocalizedResourceConfigException("warning.config.type.boolean", String.valueOf(n), option);
throw new LocalizedResourceConfigException("warning.config.id.boolean", String.valueOf(n), option);
}
case String s -> {
if (s.equalsIgnoreCase("true")) return true;
if (s.equalsIgnoreCase("false")) return false;
throw new LocalizedResourceConfigException("warning.config.type.boolean", s, option);
throw new LocalizedResourceConfigException("warning.config.id.boolean", s, option);
}
default -> {
throw new LocalizedResourceConfigException("warning.config.type.boolean", o.toString(), option);
throw new LocalizedResourceConfigException("warning.config.id.boolean", o.toString(), option);
}
}
}
@@ -234,11 +234,11 @@ public final class ResourceConfigUtils {
try {
return Long.parseLong(s.replace("_", ""));
} catch (NumberFormatException e) {
throw new LocalizedResourceConfigException("warning.config.type.long", e, s, option);
throw new LocalizedResourceConfigException("warning.config.id.long", e, s, option);
}
}
default -> {
throw new LocalizedResourceConfigException("warning.config.type.long", o.toString(), option);
throw new LocalizedResourceConfigException("warning.config.id.long", o.toString(), option);
}
}
}
@@ -248,7 +248,7 @@ public final class ResourceConfigUtils {
if (obj instanceof Map<?, ?> map) {
return (Map<String, Object>) map;
}
throw new LocalizedResourceConfigException("warning.config.type.map", String.valueOf(obj), option);
throw new LocalizedResourceConfigException("warning.config.id.map", String.valueOf(obj), option);
}
@SuppressWarnings("unchecked")
@@ -259,7 +259,7 @@ public final class ResourceConfigUtils {
if (obj instanceof Map<?, ?> map) {
return (Map<String, Object>) map;
}
throw new LocalizedResourceConfigException("warning.config.type.map", String.valueOf(obj), option);
throw new LocalizedResourceConfigException("warning.config.id.map", String.valueOf(obj), option);
}
public static Vector3f getAsVector3f(Object o, String option) {
@@ -274,7 +274,7 @@ public final class ResourceConfigUtils {
} else if (split.length == 1) {
return new Vector3f(Float.parseFloat(split[0]));
} else {
throw new LocalizedResourceConfigException("warning.config.type.vector3f", stringFormat, option);
throw new LocalizedResourceConfigException("warning.config.id.vector3f", stringFormat, option);
}
}
}
@@ -293,7 +293,7 @@ public final class ResourceConfigUtils {
} else if (split.length == 1) {
return QuaternionUtils.toQuaternionf(0, (float) -Math.toRadians(Float.parseFloat(split[0])), 0);
} else {
throw new LocalizedResourceConfigException("warning.config.type.quaternionf", stringFormat, option);
throw new LocalizedResourceConfigException("warning.config.id.quaternionf", stringFormat, option);
}
}
}
@@ -311,7 +311,7 @@ public final class ResourceConfigUtils {
double d = Double.parseDouble(split[0]);
return new Vec3d(d, d, d);
} else {
throw new LocalizedResourceConfigException("warning.config.type.vec3d", stringFormat, option);
throw new LocalizedResourceConfigException("warning.config.id.vec3d", stringFormat, option);
}
}
}
@@ -344,50 +344,54 @@ public final class ResourceConfigUtils {
}
public static AABB getAsAABB(Object o, String option) {
if (o == null) {
throw new LocalizedResourceConfigException("warning.config.type.aabb", "null", option);
}
if (o instanceof Number number) {
double min = -(number.doubleValue() / 2);
double max = number.doubleValue() / 2;
return new AABB(min, min, min, max, max, max);
} else {
double[] args;
if (o instanceof List<?> list) {
args = new double[list.size()];
for (int i = 0; i < args.length; i++) {
if (list.get(i) instanceof Number number) {
args[i] = number.doubleValue();
} else {
switch (o) {
case null -> throw new LocalizedResourceConfigException("warning.config.id.aabb", "null", option);
case AABB aabb -> {
return aabb;
}
case Number number -> {
double min = -(number.doubleValue() / 2);
double max = number.doubleValue() / 2;
return new AABB(min, min, min, max, max, max);
}
default -> {
double[] args;
if (o instanceof List<?> list) {
args = new double[list.size()];
for (int i = 0; i < args.length; i++) {
if (list.get(i) instanceof Number number) {
args[i] = number.doubleValue();
} else {
try {
args[i] = Double.parseDouble(list.get(i).toString());
} catch (NumberFormatException e) {
throw new LocalizedResourceConfigException("warning.config.id.aabb", o.toString(), option);
}
}
}
} else {
String[] split = o.toString().split(",");
args = new double[split.length];
for (int i = 0; i < args.length; i++) {
try {
args[i] = Double.parseDouble(list.get(i).toString());
args[i] = Double.parseDouble(split[i]);
} catch (NumberFormatException e) {
throw new LocalizedResourceConfigException("warning.config.type.aabb", o.toString(), option);
throw new LocalizedResourceConfigException("warning.config.id.aabb", o.toString(), option);
}
}
}
} else {
String[] split = o.toString().split(",");
args = new double[split.length];
for (int i = 0; i < args.length; i++) {
try {
args[i] = Double.parseDouble(split[i]);
} catch (NumberFormatException e) {
throw new LocalizedResourceConfigException("warning.config.type.aabb", o.toString(), option);
}
if (args.length == 1) {
return new AABB(-args[0] / 2, -args[0] / 2, -args[0] / 2, args[0] / 2, args[0] / 2, args[0] / 2);
} else if (args.length == 2) {
return new AABB(-args[0] / 2, -args[1] / 2, -args[0] / 2, args[0] / 2, args[1] / 2, args[0] / 2);
} else if (args.length == 3) {
return new AABB(-args[0] / 2, -args[1] / 2, -args[2] / 2, args[0] / 2, args[1] / 2, args[2] / 2);
} else if (args.length == 6) {
return new AABB(args[0], args[1], args[2], args[3], args[4], args[5]);
} else {
throw new LocalizedResourceConfigException("warning.config.id.aabb", o.toString(), option);
}
}
if (args.length == 1) {
return new AABB(-args[0]/2, -args[0]/2, -args[0]/2, args[0]/2, args[0]/2, args[0]/2);
} else if (args.length == 2) {
return new AABB(-args[0]/2, -args[1]/2, -args[0]/2, args[0]/2, args[1]/2, args[0]/2);
} else if (args.length == 3) {
return new AABB(-args[0]/2, -args[1]/2, -args[2]/2, args[0]/2, args[1]/2, args[2]/2);
} else if (args.length == 6) {
return new AABB(args[0], args[1], args[2], args[3], args[4], args[5]);
} else {
throw new LocalizedResourceConfigException("warning.config.type.aabb", o.toString(), option);
}
}
}
}

View File

@@ -156,7 +156,7 @@ public final class SNBTReader extends DefaultStringReader {
// 1.21.6的SNBT原版是支持 {key:[B;1,2b,0xFF]} 这种奇葩写法的, 越界部分会被自动舍弃, 如0xff的byte值为-1.
// 如果需要和原版对齐, 那么只需要判断是否是数字就行了.
// if (!(element instanceof Number number))
// throw new IllegalArgumentException("Error element type at pos " + getCursor());
// throw new IllegalArgumentException("Error element id at pos " + getCursor());
if (!(element instanceof Number number))
throw new IllegalArgumentException("Error parsing number at pos " + getCursor());

View File

@@ -6,9 +6,9 @@ import java.util.Objects;
* A generic class representing a tuple with three values.
* This class provides methods for creating and accessing tuples with three values.
*
* @param <L> the type of the left value
* @param <M> the type of the middle value
* @param <R> the type of the right value
* @param <L> the id of the left value
* @param <M> the id of the middle value
* @param <R> the id of the right value
*/
public record Tuple<L, M, R>(L left, M mid, R right) {
@@ -18,9 +18,9 @@ public record Tuple<L, M, R>(L left, M mid, R right) {
* @param left the left value
* @param mid the middle value
* @param right the right value
* @param <L> the type of the left value
* @param <M> the type of the middle value
* @param <R> the type of the right value
* @param <L> the id of the left value
* @param <M> the id of the middle value
* @param <R> the id of the right value
* @return a new {@link Tuple} with the specified values
*/
public static <L, M, R> Tuple<L, M, R> of(final L left, final M mid, final R right) {

View File

@@ -8,18 +8,18 @@ public class TypeUtils {
private TypeUtils() {}
/**
* Checks if the provided object is of the specified type.
* Checks if the provided object is of the specified id.
* If not, throws an IllegalArgumentException with a detailed message.
*
* @param object The object to check.
* @param expectedType The expected class type.
* @param <T> The type parameter for expectedType.
* @return The object cast to the expected type if it matches.
* @throws IllegalArgumentException if the object's type does not match the expected type.
* @param expectedType The expected class id.
* @param <T> The id parameter for expectedType.
* @return The object cast to the expected id if it matches.
* @throws IllegalArgumentException if the object's id does not match the expected id.
*/
public static <T> T checkType(Object object, Class<T> expectedType) {
if (!expectedType.isInstance(object)) {
throw new IllegalArgumentException("Expected type: " + expectedType.getName() +
throw new IllegalArgumentException("Expected id: " + expectedType.getName() +
", but got: " + (object == null ? "null" : object.getClass().getName()));
}
return expectedType.cast(object);
@@ -48,7 +48,7 @@ public class TypeUtils {
}
yield bytes;
}
default -> throw new IllegalStateException("Unsupported type: " + type.toLowerCase(Locale.ENGLISH));
default -> throw new IllegalStateException("Unsupported id: " + type.toLowerCase(Locale.ENGLISH));
};
}

View File

@@ -10,6 +10,7 @@ import net.momirealms.craftengine.core.block.entity.render.DynamicBlockEntityRen
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.tick.*;
import net.momirealms.craftengine.core.entity.CustomEntity;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.entityculling.CullingData;
@@ -18,6 +19,7 @@ import net.momirealms.craftengine.core.world.*;
import net.momirealms.craftengine.core.world.chunk.client.VirtualCullableObject;
import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntityRendererSerializer;
import net.momirealms.craftengine.core.world.chunk.serialization.DefaultBlockEntitySerializer;
import net.momirealms.craftengine.core.world.chunk.serialization.DefaultEntitySerializer;
import net.momirealms.sparrow.nbt.ListTag;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -30,11 +32,12 @@ public class CEChunk {
public final ChunkPos chunkPos;
public final CESection[] sections;
public final WorldHeight worldHeightAccessor;
public final Map<BlockPos, BlockEntity> blockEntities; // 从区域线程上访问,安全
public final Map<BlockPos, ReplaceableTickingBlockEntity> tickingSyncBlockEntitiesByPos; // 从区域线程上访问,安全
public final Map<BlockPos, ReplaceableTickingBlockEntity> tickingAsyncBlockEntitiesByPos; // 从区域线程上访问,安全
public final Map<BlockPos, ConstantBlockEntityRenderer> constantBlockEntityRenderers; // 从区域线程上读写netty线程上读取
public final Map<BlockPos, DynamicBlockEntityRenderer> dynamicBlockEntityRenderers; // 会从区域线程上读写netty线程上读取
private final Map<UUID, CustomEntity> entities; // 从区域线程上访问,安全
private final Map<BlockPos, BlockEntity> blockEntities; // 从区域线程上访问,安全
private final Map<BlockPos, ReplaceableTickingBlockEntity> tickingSyncBlockEntitiesByPos; // 从区域线程上访问,安全
private final Map<BlockPos, ReplaceableTickingBlockEntity> tickingAsyncBlockEntitiesByPos; // 从区域线程上访问,安全
private final Map<BlockPos, ConstantBlockEntityRenderer> constantBlockEntityRenderers; // 会从区域线程上读写netty线程上读取
private final Map<BlockPos, DynamicBlockEntityRenderer> dynamicBlockEntityRenderers; // 会从区域线程上读写netty线程上读取
private final ReentrantReadWriteLock renderLock = new ReentrantReadWriteLock();
private volatile boolean dirty;
private volatile boolean loaded;
@@ -50,10 +53,11 @@ public class CEChunk {
this.dynamicBlockEntityRenderers = new Object2ObjectOpenHashMap<>(10, 0.5f);
this.tickingSyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(10, 0.5f);
this.tickingAsyncBlockEntitiesByPos = new Object2ObjectOpenHashMap<>(10, 0.5f);
this.entities = new Object2ObjectOpenHashMap<>(10, 0.5f);
this.fillEmptySection();
}
public CEChunk(CEWorld world, ChunkPos chunkPos, CESection[] sections, @Nullable ListTag blockEntitiesTag, @Nullable ListTag itemDisplayBlockRenders) {
public CEChunk(CEWorld world, ChunkPos chunkPos, CESection[] sections, @Nullable ListTag blockEntitiesTag, @Nullable ListTag blockEntityRenders, @Nullable ListTag entities) {
this.world = world;
this.chunkPos = chunkPos;
this.worldHeightAccessor = world.worldHeight();
@@ -80,15 +84,27 @@ public class CEChunk {
} else {
this.blockEntities = new Object2ObjectOpenHashMap<>(10, 0.5f);
}
if (itemDisplayBlockRenders != null) {
this.constantBlockEntityRenderers = new Object2ObjectOpenHashMap<>(Math.max(itemDisplayBlockRenders.size(), 10), 0.5f);
List<BlockPos> blockEntityRendererPoses = DefaultBlockEntityRendererSerializer.deserialize(this.chunkPos, itemDisplayBlockRenders);
if (blockEntityRenders != null) {
this.constantBlockEntityRenderers = new Object2ObjectOpenHashMap<>(Math.max(blockEntityRenders.size(), 10), 0.5f);
List<BlockPos> blockEntityRendererPoses = DefaultBlockEntityRendererSerializer.deserialize(this.chunkPos, blockEntityRenders);
for (BlockPos pos : blockEntityRendererPoses) {
this.addConstantBlockEntityRenderer(pos);
}
} else {
this.constantBlockEntityRenderers = new Object2ObjectOpenHashMap<>(10, 0.5f);
}
if (entities != null) {
this.entities = new Object2ObjectOpenHashMap<>(Math.max(entities.size(), 10), 0.5f);
for (CustomEntity entity : DefaultEntitySerializer.deserialize(world, entities)) {
this.entities.put(entity.uuid(), entity);
}
} else {
this.entities = new Object2ObjectOpenHashMap<>(10, 0.5f);
}
}
public Map<UUID, CustomEntity> entities() {
return Collections.unmodifiableMap(this.entities);
}
public void spawnBlockEntities(Player player) {

View File

@@ -4,6 +4,7 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.behavior.EntityBlockBehavior;
import net.momirealms.craftengine.core.block.entity.BlockEntity;
import net.momirealms.craftengine.core.block.entity.BlockEntityType;
import net.momirealms.craftengine.core.block.entity.InactiveBlockEntity;
import net.momirealms.craftengine.core.plugin.logger.Debugger;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.util.Key;
@@ -37,11 +38,13 @@ public final class DefaultBlockEntitySerializer {
CompoundTag data = tag.getCompound(i);
Key id = Key.of(data.getString("id"));
BlockEntityType<?> type = BuiltInRegistries.BLOCK_ENTITY_TYPE.getValue(id);
BlockPos pos = BlockEntity.readPosAndVerify(data, chunk.chunkPos());
ImmutableBlockState blockState = chunk.getBlockState(pos);
if (type == null) {
Debugger.BLOCK.debug(() -> "Unknown block entity type: " + id);
BlockEntity blockEntity = new InactiveBlockEntity(pos, blockState, data);
blockEntities.add(blockEntity);
} else {
BlockPos pos = BlockEntity.readPosAndVerify(data, chunk.chunkPos());
ImmutableBlockState blockState = chunk.getBlockState(pos);
if (blockState.blockEntityType() == type) {
Optional<EntityBlockBehavior> entityBlockBehavior = blockState.behavior().getAs(EntityBlockBehavior.class);
if (entityBlockBehavior.isPresent()) {

View File

@@ -1,5 +1,6 @@
package net.momirealms.craftengine.core.world.chunk.serialization;
import net.momirealms.craftengine.core.entity.CustomEntity;
import net.momirealms.craftengine.core.world.CEWorld;
import net.momirealms.craftengine.core.world.ChunkPos;
import net.momirealms.craftengine.core.world.chunk.CEChunk;
@@ -9,6 +10,9 @@ import net.momirealms.sparrow.nbt.ListTag;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.UUID;
public final class DefaultChunkSerializer {
private DefaultChunkSerializer() {}
@@ -36,6 +40,10 @@ public final class DefaultChunkSerializer {
if (!blockEntityRenders.isEmpty()) {
chunkNbt.put("block_entity_renderers", blockEntityRenders);
}
ListTag listTag = new ListTag();
Map<UUID, CustomEntity> entities = chunk.entities();
return chunkNbt;
}
@@ -54,7 +62,8 @@ public final class DefaultChunkSerializer {
}
}
ListTag blockEntities = chunkNbt.getList("block_entities");
ListTag itemDisplayBlockRenders = chunkNbt.getList("block_entity_renderers");
return new CEChunk(world, pos, sectionArray, blockEntities, itemDisplayBlockRenders);
ListTag blockEntityRenders = chunkNbt.getList("block_entity_renderers");
ListTag entities = chunkNbt.getList("entities");
return new CEChunk(world, pos, sectionArray, blockEntities, blockEntityRenders, entities);
}
}

View File

@@ -0,0 +1,55 @@
package net.momirealms.craftengine.core.world.chunk.serialization;
import net.momirealms.craftengine.core.entity.CustomEntity;
import net.momirealms.craftengine.core.entity.CustomEntityType;
import net.momirealms.craftengine.core.entity.InactiveCustomEntity;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.world.CEWorld;
import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.sparrow.nbt.CompoundTag;
import net.momirealms.sparrow.nbt.ListTag;
import net.momirealms.sparrow.nbt.Tag;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
public class DefaultEntitySerializer {
public static ListTag serialize(@NotNull Collection<CustomEntity> entity) {
ListTag entities = new ListTag();
for (CustomEntity customEntity : entity) {
if (customEntity.isValid()) {
entities.add(customEntity.saveAsTag());
}
}
return entities;
}
public static List<CustomEntity> deserialize(CEWorld world, ListTag entitiesTag) {
List<CustomEntity> entities = new ArrayList<>(entitiesTag.size());
for (Tag tag : entitiesTag) {
if (tag instanceof CompoundTag entityTag) {
WorldPosition worldPosition = CustomEntity.readPos(world, entityTag);
UUID uuid = CustomEntity.readUUID(entityTag);
Key type = Key.of(entityTag.getString("type"));
CustomEntityType<?> entityType = BuiltInRegistries.ENTITY_TYPE.getValue(type);
if (entityType == null) {
InactiveCustomEntity entity = new InactiveCustomEntity(uuid, worldPosition, entityTag);
entities.add(entity);
} else {
CustomEntity entity = entityType.factory().create(uuid, worldPosition);
entity.loadCustomData(entityTag);
// 加载时无效则直接放弃
if (entity.isValid()) {
entities.add(entity);
}
}
}
}
return entities;
}
}