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

refactor furniture

This commit is contained in:
XiaoMoMi
2025-04-04 04:25:45 +08:00
parent 74d5320251
commit 72f49eccc0
10 changed files with 280 additions and 197 deletions

View File

@@ -34,7 +34,7 @@ public class CraftEngineFurniture {
* @return the custom furniture
*/
public static CustomFurniture byId(@NotNull Key id) {
return BukkitFurnitureManager.instance().getFurniture(id).orElse(null);
return BukkitFurnitureManager.instance().furnitureById(id).orElse(null);
}
/**
@@ -139,7 +139,7 @@ public class CraftEngineFurniture {
*/
@Nullable
public static LoadedFurniture getLoadedFurnitureByBaseEntity(@NotNull Entity baseEntity) {
return BukkitFurnitureManager.instance().getLoadedFurnitureByRealEntityId(baseEntity.getEntityId());
return BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(baseEntity.getEntityId());
}
/**
@@ -152,7 +152,7 @@ public class CraftEngineFurniture {
public static LoadedFurniture getLoadedFurnitureBySeat(@NotNull Entity seat) {
Integer baseEntityId = seat.getPersistentDataContainer().get(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER);
if (baseEntityId == null) return null;
return BukkitFurnitureManager.instance().getLoadedFurnitureByRealEntityId(baseEntityId);
return BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(baseEntityId);
}
/**
@@ -163,7 +163,7 @@ public class CraftEngineFurniture {
*/
public static boolean remove(@NotNull Entity furniture) {
if (!isFurniture(furniture)) return false;
LoadedFurniture loadedFurniture = BukkitFurnitureManager.instance().getLoadedFurnitureByRealEntityId(furniture.getEntityId());
LoadedFurniture loadedFurniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(furniture.getEntityId());
if (loadedFurniture == null) return false;
loadedFurniture.destroy();
return true;
@@ -181,7 +181,7 @@ public class CraftEngineFurniture {
boolean dropLoot,
boolean playSound) {
if (!isFurniture(furniture)) return false;
LoadedFurniture loadedFurniture = BukkitFurnitureManager.instance().getLoadedFurnitureByRealEntityId(furniture.getEntityId());
LoadedFurniture loadedFurniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(furniture.getEntityId());
if (loadedFurniture == null) return false;
remove(loadedFurniture, (net.momirealms.craftengine.core.entity.player.Player) null, dropLoot, playSound);
return true;
@@ -201,7 +201,7 @@ public class CraftEngineFurniture {
boolean dropLoot,
boolean playSound) {
if (!isFurniture(furniture)) return false;
LoadedFurniture loadedFurniture = BukkitFurnitureManager.instance().getLoadedFurnitureByRealEntityId(furniture.getEntityId());
LoadedFurniture loadedFurniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(furniture.getEntityId());
if (loadedFurniture == null) return false;
remove(loadedFurniture, player, dropLoot, playSound);
return true;

View File

@@ -10,13 +10,15 @@ import net.momirealms.craftengine.bukkit.util.EntityUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.entity.furniture.*;
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.plugin.config.ConfigManager;
import net.momirealms.craftengine.core.plugin.scheduler.SchedulerTask;
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
import net.momirealms.craftengine.core.sound.SoundData;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.Vec3d;
import org.bukkit.*;
import org.bukkit.entity.*;
import org.bukkit.event.HandlerList;
@@ -31,35 +33,35 @@ import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class BukkitFurnitureManager implements FurnitureManager {
public class BukkitFurnitureManager extends AbstractFurnitureManager {
public static final NamespacedKey FURNITURE_KEY = Objects.requireNonNull(NamespacedKey.fromString("craftengine:furniture_id"));
public static final NamespacedKey FURNITURE_ANCHOR_KEY = Objects.requireNonNull(NamespacedKey.fromString("craftengine:anchor_type"));
public static final NamespacedKey FURNITURE_SEAT_BASE_ENTITY_KEY = Objects.requireNonNull(NamespacedKey.fromString("craftengine:seat_to_base_entity"));
public static final NamespacedKey FURNITURE_SEAT_VECTOR_3F_KEY = Objects.requireNonNull(NamespacedKey.fromString("craftengine:seat_vector"));
private static BukkitFurnitureManager instance;
private final BukkitCraftEngine plugin;
private final Map<Key, CustomFurniture> byId = new HashMap<>();
private final FurnitureParser furnitureParser;
private final Map<Integer, LoadedFurniture> furnitureByRealEntityId = new ConcurrentHashMap<>(256, 0.5f);
private final Map<Integer, LoadedFurniture> furnitureByEntityId = new ConcurrentHashMap<>(512, 0.5f);
// Event listeners
private final Listener dismountListener;
private final FurnitureEventListener furnitureEventListener;
// tick task
private SchedulerTask tickTask;
// Cached command suggestions
private final List<Suggestion> cachedSuggestions = new ArrayList<>();
public static BukkitFurnitureManager instance() {
return instance;
}
public BukkitFurnitureManager(BukkitCraftEngine plugin) {
instance = this;
this.plugin = plugin;
this.furnitureParser = new FurnitureParser();
this.furnitureEventListener = new FurnitureEventListener(this);
this.dismountListener = VersionHelper.isVersionNewerThan1_20_3() ? new DismountListener1_20_3(this) : new DismountListener1_20(this::handleDismount);
instance = this;
}
@Override
public Furniture place(CustomFurniture furniture, Vec3d vec3d, net.momirealms.craftengine.core.world.World world, AnchorType anchorType, boolean playSound) {
return this.place(furniture, new Location((World) world.platformWorld(), vec3d.x(), vec3d.y(), vec3d.z()), anchorType, playSound);
}
public LoadedFurniture place(CustomFurniture furniture, Location location, AnchorType anchorType, boolean playSound) {
@@ -77,151 +79,131 @@ public class BukkitFurnitureManager implements FurnitureManager {
SoundData data = furniture.settings().sounds().placeSound();
location.getWorld().playSound(location, data.id().toString(), SoundCategory.BLOCKS, data.volume(), data.pitch());
}
return getLoadedFurnitureByRealEntityId(furnitureEntity.getEntityId());
return loadedFurnitureByRealEntityId(furnitureEntity.getEntityId());
}
@Override
public void delayedLoad() {
this.initSuggestions();
public ConfigSectionParser parser() {
return this.furnitureParser;
}
@Override
public void initSuggestions() {
this.cachedSuggestions.clear();
for (Key key : this.byId.keySet()) {
this.cachedSuggestions.add(Suggestion.suggestion(key.toString()));
}
}
public class FurnitureParser implements ConfigSectionParser {
public static final String[] CONFIG_SECTION_NAME = new String[] { "furniture" };
@Override
public Collection<Suggestion> cachedSuggestions() {
return Collections.unmodifiableCollection(this.cachedSuggestions);
}
@SuppressWarnings("unchecked")
@Override
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) {
Map<String, Object> lootMap = MiscUtils.castToMap(section.get("loot"), true);
Map<String, Object> settingsMap = MiscUtils.castToMap(section.get("settings"), true);
Map<String, Object> placementMap = MiscUtils.castToMap(section.get("placement"), true);
EnumMap<AnchorType, CustomFurniture.Placement> placements = new EnumMap<>(AnchorType.class);
if (placementMap == null) {
throw new IllegalArgumentException("Missing required parameter 'placement' for furniture " + id);
@Override
public String[] sectionId() {
return CONFIG_SECTION_NAME;
}
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(), true);
@Override
public int loadingSequence() {
return LoadingSequence.FURNITURE;
}
// 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) {
String key = (String) element.get("item");
if (key == null) {
throw new IllegalArgumentException("Missing required parameter 'item' for furniture " + id);
@SuppressWarnings("unchecked")
@Override
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) {
Map<String, Object> lootMap = MiscUtils.castToMap(section.get("loot"), true);
Map<String, Object> settingsMap = MiscUtils.castToMap(section.get("settings"), true);
Map<String, Object> placementMap = MiscUtils.castToMap(section.get("placement"), true);
EnumMap<AnchorType, CustomFurniture.Placement> placements = new EnumMap<>(AnchorType.class);
if (placementMap == null) {
throw new IllegalArgumentException("Missing required parameter 'placement' for furniture " + id);
}
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(), true);
// 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) {
String key = (String) element.get("item");
if (key == null) {
throw new IllegalArgumentException("Missing required parameter 'item' for furniture " + id);
}
ItemDisplayContext transform = ItemDisplayContext.valueOf(element.getOrDefault("transform", "NONE").toString().toUpperCase(Locale.ENGLISH));
Billboard billboard = Billboard.valueOf(element.getOrDefault("billboard", "FIXED").toString().toUpperCase(Locale.ENGLISH));
FurnitureElement furnitureElement = new BukkitFurnitureElement(Key.of(key), billboard, transform,
MiscUtils.getVector3f(element.getOrDefault("scale", "1")),
MiscUtils.getVector3f(element.getOrDefault("translation", "0")),
MiscUtils.getVector3f(element.getOrDefault("position", "0")),
MiscUtils.getQuaternionf(element.getOrDefault("rotation", "0"))
);
elements.add(furnitureElement);
}
// add colliders
List<Collider> colliders = new ArrayList<>();
// external model providers
Optional<ExternalModel> externalModel;
if (placementArguments.containsKey("model-engine")) {
externalModel = Optional.of(new ModelEngineModel(placementArguments.get("model-engine").toString()));
} else if (placementArguments.containsKey("better-model")) {
externalModel = Optional.of(new BetterModelModel(placementArguments.get("better-model").toString()));
} else {
externalModel = Optional.empty();
}
// add hitboxes
List<Map<String, Object>> hitboxConfigs = (List<Map<String, Object>>) placementArguments.getOrDefault("hitboxes", List.of());
List<HitBox> hitboxes = new ArrayList<>();
for (Map<String, Object> config : hitboxConfigs) {
HitBox hitBox = HitBoxTypes.fromMap(config);
hitboxes.add(hitBox);
hitBox.optionalCollider().ifPresent(colliders::add);
}
if (hitboxes.isEmpty() && externalModel.isEmpty()) {
hitboxes.add(InteractionHitBox.DEFAULT);
}
// rules
Map<String, Object> ruleSection = MiscUtils.castToMap(placementArguments.get("rules"), true);
if (ruleSection != null) {
RotationRule rotationRule = Optional.ofNullable((String) ruleSection.get("rotation"))
.map(it -> RotationRule.valueOf(it.toUpperCase(Locale.ENGLISH)))
.orElse(RotationRule.ANY);
AlignmentRule alignmentRule = Optional.ofNullable((String) ruleSection.get("alignment"))
.map(it -> AlignmentRule.valueOf(it.toUpperCase(Locale.ENGLISH)))
.orElse(AlignmentRule.CENTER);
placements.put(anchorType, new CustomFurniture.Placement(
elements.toArray(new FurnitureElement[0]),
hitboxes.toArray(new HitBox[0]),
colliders.toArray(new Collider[0]),
rotationRule,
alignmentRule,
externalModel
));
} else {
placements.put(anchorType, new CustomFurniture.Placement(
elements.toArray(new FurnitureElement[0]),
hitboxes.toArray(new HitBox[0]),
colliders.toArray(new Collider[0]),
RotationRule.ANY,
AlignmentRule.CENTER,
externalModel
));
}
ItemDisplayContext transform = ItemDisplayContext.valueOf(element.getOrDefault("transform", "NONE").toString().toUpperCase(Locale.ENGLISH));
Billboard billboard = Billboard.valueOf(element.getOrDefault("billboard", "FIXED").toString().toUpperCase(Locale.ENGLISH));
FurnitureElement furnitureElement = new BukkitFurnitureElement(Key.of(key), billboard, transform,
MiscUtils.getVector3f(element.getOrDefault("scale", "1")),
MiscUtils.getVector3f(element.getOrDefault("translation", "0")),
MiscUtils.getVector3f(element.getOrDefault("position", "0")),
MiscUtils.getQuaternionf(element.getOrDefault("rotation", "0"))
);
elements.add(furnitureElement);
}
// add colliders
List<Collider> colliders = new ArrayList<>();
// List<Map<String, Object>> colliderConfigs = (List<Map<String, Object>>) placementArguments.getOrDefault("colliders", List.of());
// for (Map<String, Object> config : colliderConfigs) {
// if (!config.containsKey("position")) {
// colliders.add(new Collider(
// (boolean) config.getOrDefault("can-be-hit-by-projectile", false),
// MiscUtils.getVector3d(config.getOrDefault("point-1", "0")),
// MiscUtils.getVector3d(config.getOrDefault("point-2", "0"))
// ));
// } else {
// colliders.add(new Collider(
// (boolean) config.getOrDefault("can-be-hit-by-projectile", false),
// MiscUtils.getVector3f(config.getOrDefault("position", "0")),
// MiscUtils.getAsFloat(config.getOrDefault("width", "1")),
// MiscUtils.getAsFloat(config.getOrDefault("height", "1"))
// ));
// }
// }
CustomFurniture furniture = new CustomFurniture(
id,
FurnitureSettings.fromMap(settingsMap),
placements,
lootMap == null ? null : LootTable.fromMap(lootMap)
);
// external model providers
Optional<ExternalModel> externalModel;
if (placementArguments.containsKey("model-engine")) {
externalModel = Optional.of(new ModelEngineModel(placementArguments.get("model-engine").toString()));
} else if (placementArguments.containsKey("better-model")) {
externalModel = Optional.of(new BetterModelModel(placementArguments.get("better-model").toString()));
} else {
externalModel = Optional.empty();
}
// add hitboxes
List<Map<String, Object>> hitboxConfigs = (List<Map<String, Object>>) placementArguments.getOrDefault("hitboxes", List.of());
List<HitBox> hitboxes = new ArrayList<>();
for (Map<String, Object> config : hitboxConfigs) {
HitBox hitBox = HitBoxTypes.fromMap(config);
hitboxes.add(hitBox);
hitBox.optionalCollider().ifPresent(colliders::add);
}
if (hitboxes.isEmpty() && externalModel.isEmpty()) {
hitboxes.add(InteractionHitBox.DEFAULT);
}
// rules
Map<String, Object> ruleSection = MiscUtils.castToMap(placementArguments.get("rules"), true);
if (ruleSection != null) {
RotationRule rotationRule = Optional.ofNullable((String) ruleSection.get("rotation"))
.map(it -> RotationRule.valueOf(it.toUpperCase(Locale.ENGLISH)))
.orElse(RotationRule.ANY);
AlignmentRule alignmentRule = Optional.ofNullable((String) ruleSection.get("alignment"))
.map(it -> AlignmentRule.valueOf(it.toUpperCase(Locale.ENGLISH)))
.orElse(AlignmentRule.CENTER);
placements.put(anchorType, new CustomFurniture.Placement(
elements.toArray(new FurnitureElement[0]),
hitboxes.toArray(new HitBox[0]),
colliders.toArray(new Collider[0]),
rotationRule,
alignmentRule,
externalModel
));
} else {
placements.put(anchorType, new CustomFurniture.Placement(
elements.toArray(new FurnitureElement[0]),
hitboxes.toArray(new HitBox[0]),
colliders.toArray(new Collider[0]),
RotationRule.ANY,
AlignmentRule.CENTER,
externalModel
));
}
byId.put(id, furniture);
}
CustomFurniture furniture = new CustomFurniture(
id,
FurnitureSettings.fromMap(settingsMap),
placements,
lootMap == null ? null : LootTable.fromMap(lootMap)
);
this.byId.put(id, furniture);
}
public void tick() {
}
@Override
public void delayedInit() {
Bukkit.getPluginManager().registerEvents(this.dismountListener, this.plugin.bootstrap());
Bukkit.getPluginManager().registerEvents(this.furnitureEventListener, this.plugin.bootstrap());
this.tickTask = plugin.scheduler().sync().runRepeating(this::tick, 1, 1);
for (World world : Bukkit.getWorlds()) {
List<Entity> entities = world.getEntities();
for (Entity entity : entities) {
@@ -230,18 +212,10 @@ public class BukkitFurnitureManager implements FurnitureManager {
}
}
@Override
public void unload() {
this.byId.clear();
}
@Override
public void disable() {
HandlerList.unregisterAll(this.dismountListener);
HandlerList.unregisterAll(this.furnitureEventListener);
if (tickTask != null && !tickTask.cancelled()) {
tickTask.cancel();
}
unload();
for (Player player : Bukkit.getOnlinePlayers()) {
Entity vehicle = player.getVehicle();
@@ -251,23 +225,20 @@ public class BukkitFurnitureManager implements FurnitureManager {
}
}
@Override
public Optional<CustomFurniture> getFurniture(Key id) {
return Optional.ofNullable(this.byId.get(id));
}
@Override
public boolean isFurnitureRealEntity(int entityId) {
return this.furnitureByRealEntityId.containsKey(entityId);
}
@Nullable
public LoadedFurniture getLoadedFurnitureByRealEntityId(int entityId) {
@Override
public LoadedFurniture loadedFurnitureByRealEntityId(int entityId) {
return this.furnitureByRealEntityId.get(entityId);
}
@Override
@Nullable
public LoadedFurniture getLoadedFurnitureByEntityId(int entityId) {
public LoadedFurniture loadedFurnitureByEntityId(int entityId) {
return this.furnitureByEntityId.get(entityId);
}
@@ -296,7 +267,7 @@ public class BukkitFurnitureManager implements FurnitureManager {
String id = entity.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING);
if (id == null) return;
Key key = Key.of(id);
Optional<CustomFurniture> optionalFurniture = getFurniture(key);
Optional<CustomFurniture> optionalFurniture = furnitureById(key);
if (optionalFurniture.isEmpty()) return;
CustomFurniture customFurniture = optionalFurniture.get();
LoadedFurniture previous = this.furnitureByRealEntityId.get(display.getEntityId());
@@ -318,7 +289,7 @@ public class BukkitFurnitureManager implements FurnitureManager {
String id = entity.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING);
if (id == null) return;
Key key = Key.of(id);
Optional<CustomFurniture> optionalFurniture = getFurniture(key);
Optional<CustomFurniture> optionalFurniture = furnitureById(key);
if (optionalFurniture.isPresent()) {
CustomFurniture customFurniture = optionalFurniture.get();
LoadedFurniture previous = this.furnitureByRealEntityId.get(display.getEntityId());
@@ -375,7 +346,7 @@ public class BukkitFurnitureManager implements FurnitureManager {
Integer baseFurniture = vehicle.getPersistentDataContainer().get(FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER);
if (baseFurniture == null) return;
vehicle.remove();
LoadedFurniture furniture = getLoadedFurnitureByRealEntityId(baseFurniture);
LoadedFurniture furniture = loadedFurnitureByRealEntityId(baseFurniture);
if (furniture == null) {
return;
}

View File

@@ -6,12 +6,15 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.util.EntityUtils;
import net.momirealms.craftengine.bukkit.util.LegacyAttributeUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
import net.momirealms.craftengine.core.entity.furniture.*;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.ArrayUtils;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.QuaternionUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.World;
import org.bukkit.Location;
import org.bukkit.attribute.Attribute;
import org.bukkit.entity.*;
@@ -24,7 +27,7 @@ import org.joml.Vector3f;
import java.lang.ref.WeakReference;
import java.util.*;
public class LoadedFurniture {
public class LoadedFurniture implements Furniture {
private final Key id;
private final CustomFurniture furniture;
private final AnchorType anchorType;
@@ -144,6 +147,7 @@ public class LoadedFurniture {
}
}
@Override
public void initializeColliders() {
Object world = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(this.location.getWorld());
for (CollisionEntity entity : this.collisionEntities) {
@@ -161,6 +165,16 @@ public class LoadedFurniture {
}
}
@Override
public Vec3d position() {
return new Vec3d(location.getX(), location.getY(), location.getZ());
}
@Override
public World world() {
return new BukkitWorld(this.location.getWorld());
}
@NotNull
public Location location() {
return this.location.clone();
@@ -175,10 +189,12 @@ public class LoadedFurniture {
return entity;
}
@Override
public boolean isValid() {
return baseEntity().isValid();
}
@Override
public void destroy() {
if (!isValid()) {
return;
@@ -197,6 +213,7 @@ public class LoadedFurniture {
this.seats.clear();
}
@Override
public void destroySeats() {
for (Entity entity : this.seats) {
entity.remove();
@@ -204,6 +221,7 @@ public class LoadedFurniture {
this.seats.clear();
}
@Override
public Optional<Seat> findFirstAvailableSeat(int targetEntityId) {
HitBox hitbox = hitBoxes.get(targetEntityId);
if (hitbox == null) return Optional.empty();
@@ -216,14 +234,12 @@ public class LoadedFurniture {
.findFirst();
}
@Override
public boolean removeOccupiedSeat(Vector3f seat) {
return this.occupiedSeats.remove(seat);
}
public boolean removeOccupiedSeat(Seat seat) {
return this.removeOccupiedSeat(seat.offset());
}
@Override
public boolean tryOccupySeat(Seat seat) {
if (this.occupiedSeats.contains(seat.offset())) {
return false;
@@ -232,10 +248,12 @@ public class LoadedFurniture {
return true;
}
@Override
public UUID uuid() {
return this.baseEntity().getUniqueId();
}
@Override
public int baseEntityId() {
return this.baseEntityId;
}
@@ -254,31 +272,29 @@ public class LoadedFurniture {
return this.collisionEntities;
}
@NotNull
public AnchorType anchorType() {
@Override
public @NotNull AnchorType anchorType() {
return this.anchorType;
}
@NotNull
public Key id() {
@Override
public @NotNull Key id() {
return this.id;
}
@NotNull
public CustomFurniture config() {
@Override
public @NotNull CustomFurniture config() {
return this.furniture;
}
@Override
public boolean hasExternalModel() {
return hasExternalModel;
}
public CustomFurniture.Placement placement() {
return this.placement;
}
public Map<Integer, HitBox> hitBoxes() {
return this.hitBoxes;
@Override
public void spawnSeatEntityForPlayer(net.momirealms.craftengine.core.entity.player.Player player, Seat seat) {
spawnSeatEntityForPlayer((Player) player.platformPlayer(), seat);
}
public void spawnSeatEntityForPlayer(org.bukkit.entity.Player player, Seat seat) {

View File

@@ -47,7 +47,7 @@ public class FurnitureItemBehavior extends ItemBehavior {
}
public InteractionResult place(UseOnContext context) {
Optional<CustomFurniture> optionalCustomFurniture = BukkitFurnitureManager.instance().getFurniture(this.id);
Optional<CustomFurniture> optionalCustomFurniture = BukkitFurnitureManager.instance().furnitureById(this.id);
if (optionalCustomFurniture.isEmpty()) {
CraftEngine.instance().logger().warn("Furniture " + this.id + " not found");
return InteractionResult.FAIL;
@@ -139,7 +139,7 @@ public class FurnitureItemBehavior extends ItemBehavior {
throw new IllegalArgumentException("Missing required parameter 'furniture' for furniture_item behavior");
}
if (id instanceof Map<?,?> map) {
BukkitFurnitureManager.instance().parseSection(pack, path, key, MiscUtils.castToMap(map, false));
BukkitFurnitureManager.instance().parser().parseSection(pack, path, key, MiscUtils.castToMap(map, false));
return new FurnitureItemBehavior(key);
} else {
return new FurnitureItemBehavior(Key.of(id.toString()));

View File

@@ -47,7 +47,7 @@ public class DebugSpawnFurnitureCommand extends BukkitCommandFeature<CommandSend
NamespacedKey namespacedKey = context.get("id");
Key id = KeyUtils.namespacedKey2Key(namespacedKey);
BukkitFurnitureManager furnitureManager = BukkitFurnitureManager.instance();
Optional<CustomFurniture> optionalCustomFurniture = furnitureManager.getFurniture(id);
Optional<CustomFurniture> optionalCustomFurniture = furnitureManager.furnitureById(id);
if (optionalCustomFurniture.isEmpty()) {
return;
}

View File

@@ -563,7 +563,7 @@ public class PacketConsumers {
public static final TriConsumer<NetWorkUser, NMSPacketEvent, Object> PICK_ITEM_FROM_ENTITY = (user, event, packet) -> {
try {
int entityId = (int) Reflections.field$ServerboundPickItemFromEntityPacket$id.get(packet);
LoadedFurniture furniture = BukkitFurnitureManager.instance().getLoadedFurnitureByEntityId(entityId);
LoadedFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByEntityId(entityId);
if (furniture == null) return;
Player player = (Player) user.platformPlayer();
if (player == null) return;
@@ -622,7 +622,7 @@ public class PacketConsumers {
} else if (entityType == Reflections.instance$EntityType$ITEM_DISPLAY) {
// Furniture
int entityId = (int) Reflections.field$ClientboundAddEntityPacket$entityId.get(packet);
LoadedFurniture furniture = BukkitFurnitureManager.instance().getLoadedFurnitureByRealEntityId(entityId);
LoadedFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entityId);
if (furniture != null) {
user.furnitureView().computeIfAbsent(furniture.baseEntityId(), k -> new ArrayList<>()).addAll(furniture.fakeEntityIds());
user.sendPacket(furniture.spawnPacket((Player) user.platformPlayer()), false);
@@ -633,7 +633,7 @@ public class PacketConsumers {
} else if (entityType == Reflections.instance$EntityType$SHULKER) {
// Cancel collider entity packet
int entityId = (int) Reflections.field$ClientboundAddEntityPacket$entityId.get(packet);
LoadedFurniture furniture = BukkitFurnitureManager.instance().getLoadedFurnitureByRealEntityId(entityId);
LoadedFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entityId);
if (furniture != null) {
event.setCancelled(true);
}
@@ -694,7 +694,7 @@ public class PacketConsumers {
Object action = Reflections.field$ServerboundInteractPacket$action.get(packet);
Object actionType = Reflections.method$ServerboundInteractPacket$Action$getType.invoke(action);
if (actionType == null) return;
LoadedFurniture furniture = BukkitFurnitureManager.instance().getLoadedFurnitureByEntityId(entityId);
LoadedFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByEntityId(entityId);
if (furniture == null) return;
Location location = furniture.baseEntity().getLocation();
BukkitServerPlayer serverPlayer = (BukkitServerPlayer) user;

View File

@@ -0,0 +1,40 @@
package net.momirealms.craftengine.core.entity.furniture;
import net.momirealms.craftengine.core.util.Key;
import org.incendo.cloud.suggestion.Suggestion;
import java.util.*;
public abstract class AbstractFurnitureManager implements FurnitureManager {
protected final Map<Key, CustomFurniture> byId = new HashMap<>();
// Cached command suggestions
private final List<Suggestion> cachedSuggestions = new ArrayList<>();
@Override
public void delayedLoad() {
this.initSuggestions();
}
@Override
public void initSuggestions() {
this.cachedSuggestions.clear();
for (Key key : this.byId.keySet()) {
this.cachedSuggestions.add(Suggestion.suggestion(key.toString()));
}
}
@Override
public Collection<Suggestion> cachedSuggestions() {
return Collections.unmodifiableCollection(this.cachedSuggestions);
}
@Override
public Optional<CustomFurniture> furnitureById(Key id) {
return Optional.ofNullable(this.byId.get(id));
}
@Override
public void unload() {
this.byId.clear();
}
}

View File

@@ -0,0 +1,49 @@
package net.momirealms.craftengine.core.entity.furniture;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.World;
import org.jetbrains.annotations.NotNull;
import org.joml.Vector3f;
import java.util.Optional;
import java.util.UUID;
public interface Furniture {
void initializeColliders();
Vec3d position();
World world();
boolean isValid();
void destroy();
void destroySeats();
Optional<Seat> findFirstAvailableSeat(int targetEntityId);
boolean removeOccupiedSeat(Vector3f seat);
default boolean removeOccupiedSeat(Seat seat) {
return this.removeOccupiedSeat(seat.offset());
}
boolean tryOccupySeat(Seat seat);
UUID uuid();
int baseEntityId();
@NotNull AnchorType anchorType();
@NotNull Key id();
@NotNull CustomFurniture config();
boolean hasExternalModel();
void spawnSeatEntityForPlayer(Player player, Seat seat);
}

View File

@@ -1,33 +1,40 @@
package net.momirealms.craftengine.core.entity.furniture;
import net.momirealms.craftengine.core.pack.LoadingSequence;
import net.momirealms.craftengine.core.entity.Entity;
import net.momirealms.craftengine.core.plugin.Reloadable;
import net.momirealms.craftengine.core.plugin.config.ConfigSectionParser;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.craftengine.core.world.World;
import org.incendo.cloud.suggestion.Suggestion;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Optional;
public interface FurnitureManager extends Reloadable, ConfigSectionParser {
String[] CONFIG_SECTION_NAME = new String[] { "furniture" };
public interface FurnitureManager extends Reloadable {
String FURNITURE_ADMIN_NODE = "craftengine.furniture.admin";
ConfigSectionParser parser();
void initSuggestions();
Collection<Suggestion> cachedSuggestions();
@Override
default String[] sectionId() {
return CONFIG_SECTION_NAME;
}
Furniture place(CustomFurniture furniture, Vec3d vec3d, World world, AnchorType anchorType, boolean playSound);
@Override
default int loadingSequence() {
return LoadingSequence.FURNITURE;
}
Optional<CustomFurniture> getFurniture(Key id);
Optional<CustomFurniture> furnitureById(Key id);
boolean isFurnitureRealEntity(int entityId);
@Nullable
Furniture loadedFurnitureByRealEntityId(int entityId);
@Nullable
default Furniture loadedFurnitureByRealEntity(Entity entity) {
return loadedFurnitureByRealEntityId(entity.entityID());
}
@Nullable
Furniture loadedFurnitureByEntityId(int entityId);
}

View File

@@ -192,7 +192,7 @@ public abstract class CraftEngine implements Plugin {
// register item parser
this.packManager.registerConfigSectionParser(this.itemManager);
// register furniture parser
this.packManager.registerConfigSectionParser(this.furnitureManager);
this.packManager.registerConfigSectionParser(this.furnitureManager.parser());
// register block parser
this.packManager.registerConfigSectionParser(this.blockManager);
// register recipe parser