diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java index 436f677b3..fca21a6cc 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/api/CraftEngineFurniture.java @@ -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; diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java index c499b31c8..4e1878aeb 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/BukkitFurnitureManager.java @@ -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 byId = new HashMap<>(); - + private final FurnitureParser furnitureParser; private final Map furnitureByRealEntityId = new ConcurrentHashMap<>(256, 0.5f); private final Map 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 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 cachedSuggestions() { - return Collections.unmodifiableCollection(this.cachedSuggestions); - } - - @SuppressWarnings("unchecked") - @Override - public void parseSection(Pack pack, Path path, Key id, Map section) { - Map lootMap = MiscUtils.castToMap(section.get("loot"), true); - Map settingsMap = MiscUtils.castToMap(section.get("settings"), true); - Map placementMap = MiscUtils.castToMap(section.get("placement"), true); - EnumMap 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 entry : placementMap.entrySet()) { - // anchor type - AnchorType anchorType = AnchorType.valueOf(entry.getKey().toUpperCase(Locale.ENGLISH)); - Map placementArguments = MiscUtils.castToMap(entry.getValue(), true); + @Override + public int loadingSequence() { + return LoadingSequence.FURNITURE; + } - // furniture display elements - List elements = new ArrayList<>(); - List> elementConfigs = (List>) placementArguments.getOrDefault("elements", List.of()); - for (Map 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 section) { + Map lootMap = MiscUtils.castToMap(section.get("loot"), true); + Map settingsMap = MiscUtils.castToMap(section.get("settings"), true); + Map placementMap = MiscUtils.castToMap(section.get("placement"), true); + EnumMap placements = new EnumMap<>(AnchorType.class); + if (placementMap == null) { + throw new IllegalArgumentException("Missing required parameter 'placement' for furniture " + id); + } + + for (Map.Entry entry : placementMap.entrySet()) { + // anchor type + AnchorType anchorType = AnchorType.valueOf(entry.getKey().toUpperCase(Locale.ENGLISH)); + Map placementArguments = MiscUtils.castToMap(entry.getValue(), true); + + // furniture display elements + List elements = new ArrayList<>(); + List> elementConfigs = (List>) placementArguments.getOrDefault("elements", List.of()); + for (Map 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 colliders = new ArrayList<>(); + + // external model providers + Optional 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> hitboxConfigs = (List>) placementArguments.getOrDefault("hitboxes", List.of()); + List hitboxes = new ArrayList<>(); + for (Map 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 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 colliders = new ArrayList<>(); -// List> colliderConfigs = (List>) placementArguments.getOrDefault("colliders", List.of()); -// for (Map 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; - 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> hitboxConfigs = (List>) placementArguments.getOrDefault("hitboxes", List.of()); - List hitboxes = new ArrayList<>(); - for (Map 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 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 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 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 optionalFurniture = getFurniture(key); + Optional 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 optionalFurniture = getFurniture(key); + Optional 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; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/LoadedFurniture.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/LoadedFurniture.java index 668c35060..74862a595 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/LoadedFurniture.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/entity/furniture/LoadedFurniture.java @@ -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 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 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) { diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java index f0dec7311..76f777fbd 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/item/behavior/FurnitureItemBehavior.java @@ -47,7 +47,7 @@ public class FurnitureItemBehavior extends ItemBehavior { } public InteractionResult place(UseOnContext context) { - Optional optionalCustomFurniture = BukkitFurnitureManager.instance().getFurniture(this.id); + Optional 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())); diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java index f5bbb5a8f..df390ccf2 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/command/feature/DebugSpawnFurnitureCommand.java @@ -47,7 +47,7 @@ public class DebugSpawnFurnitureCommand extends BukkitCommandFeature optionalCustomFurniture = furnitureManager.getFurniture(id); + Optional optionalCustomFurniture = furnitureManager.furnitureById(id); if (optionalCustomFurniture.isEmpty()) { return; } diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java index f7df2d8ab..42372cd01 100644 --- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java +++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/network/PacketConsumers.java @@ -563,7 +563,7 @@ public class PacketConsumers { public static final TriConsumer 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; diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java new file mode 100644 index 000000000..c9839cb60 --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/AbstractFurnitureManager.java @@ -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 byId = new HashMap<>(); + // Cached command suggestions + private final List 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 cachedSuggestions() { + return Collections.unmodifiableCollection(this.cachedSuggestions); + } + + @Override + public Optional furnitureById(Key id) { + return Optional.ofNullable(this.byId.get(id)); + } + + @Override + public void unload() { + this.byId.clear(); + } +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java new file mode 100644 index 000000000..42998e48b --- /dev/null +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/Furniture.java @@ -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 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); +} diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureManager.java b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureManager.java index 8f053198c..0985250f6 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureManager.java +++ b/core/src/main/java/net/momirealms/craftengine/core/entity/furniture/FurnitureManager.java @@ -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 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 getFurniture(Key id); + Optional 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); } diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java index db7667305..bce65b429 100644 --- a/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java +++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/CraftEngine.java @@ -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