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

Merge remote-tracking branch 'upstream/dev' into dev

# Conflicts:
#	gradle.properties
This commit is contained in:
jhqwqmc
2025-05-22 16:09:17 +08:00
139 changed files with 2399 additions and 1106 deletions

View File

@@ -46,7 +46,6 @@ dependencies {
compileOnly("com.saicone.rtag:rtag-item:${rootProject.properties["rtag_version"]}")
// Adventure
compileOnly("net.kyori:adventure-api:${rootProject.properties["adventure_bundle_version"]}")
compileOnly("net.kyori:adventure-platform-bukkit:${rootProject.properties["adventure_platform_version"]}")
compileOnly("net.kyori:adventure-text-minimessage:${rootProject.properties["adventure_bundle_version"]}")
compileOnly("net.kyori:adventure-text-serializer-gson:${rootProject.properties["adventure_bundle_version"]}") {
exclude("com.google.code.gson", "gson")

View File

@@ -6,7 +6,7 @@ import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.SkriptParser;
import ch.njol.util.Kleenean;
import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture;
import net.momirealms.craftengine.bukkit.entity.furniture.LoadedFurniture;
import net.momirealms.craftengine.core.entity.furniture.Furniture;
import org.bukkit.entity.Entity;
import org.bukkit.event.Event;
import org.jetbrains.annotations.Nullable;
@@ -23,9 +23,9 @@ public class EffRemoveFurniture extends Effect {
protected void execute(Event e) {
for (Entity entity : entities.getArray(e)) {
if (CraftEngineFurniture.isFurniture(entity)) {
LoadedFurniture loadedFurniture = CraftEngineFurniture.getLoadedFurnitureByBaseEntity(entity);
if (loadedFurniture != null) {
loadedFurniture.destroy();
Furniture bukkitFurniture = CraftEngineFurniture.getLoadedFurnitureByBaseEntity(entity);
if (bukkitFurniture != null) {
bukkitFurniture.destroy();
}
}
}

View File

@@ -21,7 +21,6 @@ dependencies {
implementation(project(":bukkit:compatibility"))
implementation(project(":bukkit:compatibility:legacy"))
implementation("net.kyori:adventure-platform-bukkit:${rootProject.properties["adventure_platform_version"]}")
implementation("com.saicone.rtag:rtag-item:${rootProject.properties["rtag_version"]}")
implementation("net.momirealms:sparrow-util:${rootProject.properties["sparrow_util_version"]}")
implementation("net.momirealms:antigrieflib:${rootProject.properties["anti_grief_version"]}")

View File

@@ -23,10 +23,9 @@ commons-io=${commons_io_version}
commons-imaging=${commons_imaging_version}
byte-buddy=${byte_buddy_version}
snake-yaml=${snake_yaml_version}
adventure-text-minimessage=${adventure_bundle_version}
adventure-text-serializer-gson=${adventure_bundle_version}
adventure-text-serializer-json=${adventure_bundle_version}
adventure-text-serializer-json-legacy-impl=${adventure_bundle_version}
examination-api=1.3.0
option=1.1.0
adventure-api=${adventure_bundle_version}
netty-codec-http=${netty_version}
ahocorasick=${ahocorasick_version}
lz4=${lz4_version}

View File

@@ -64,14 +64,12 @@ items#misc:
events:
- on: right_click
functions:
- type: run
functions:
- type: open_window
gui-type: anvil
- type: cancel_event
conditions:
- type: expression
expression: "!<arg:player.is_sneaking>"
- type: open_window
gui-type: anvil
- type: cancel_event
conditions:
- type: expression
expression: "!<arg:player.is_sneaking>"
settings:
template:
- default:pickaxe_power/level_4

View File

@@ -94,6 +94,9 @@ warning.config.condition.permission.missing_permission: "<yellow>Issue found in
warning.config.condition.equals.missing_value1: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'value1' argument for 'equals' condition.</yellow>"
warning.config.condition.equals.missing_value2: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'value2' argument for 'equals' condition.</yellow>"
warning.config.condition.expression.missing_expression: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'expression' argument for 'expression' condition.</yellow>"
warning.config.condition.is_null.missing_argument: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'argument' argument for 'is_null' condition.</yellow>"
warning.config.condition.hand.missing_hand: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'hand' argument for 'hand' condition.</yellow>"
warning.config.condition.hand.invalid_hand: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is using an invalid 'hand' argument '<arg:2>' for 'hand' condition. Allowed hand types: [<arg:3>]</yellow>"
warning.config.structure.not_section: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is expected to be a config section while it's actually a(n) '<arg:2>'.</yellow>"
warning.config.image.duplicate: "<yellow>Issue found in file <arg:0> - Duplicated image '<arg:1>'. Please check if there is the same configuration in other files.</yellow>"
warning.config.image.missing_height: "<yellow>Issue found in file <arg:0> - The image '<arg:1>' is missing the required 'height' argument.</yellow>"
@@ -317,6 +320,10 @@ warning.config.function.message.missing_message: "<yellow>Issue found in file <a
warning.config.function.open_window.missing_gui_type: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'gui-type' argument for 'open_window' function.</yellow>"
warning.config.function.open_window.invalid_gui_type: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is using an invalid gui type <arg:2> for 'open_window' function. Allowed types: [<arg:3>].</yellow>"
warning.config.function.run.missing_functions: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'functions' argument for 'run' function.</yellow>"
warning.config.function.place_block.missing_block_state: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'block-state' argument for 'place_block' function.</yellow>"
warning.config.function.set_food.missing_food: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'food' argument for 'set_food' function.</yellow>"
warning.config.function.set_saturation.missing_saturation: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'saturation' argument for 'set_saturation' function.</yellow>"
warning.config.function.play_sound.missing_sound: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'sound' argument for 'play_sound' function.</yellow>"
warning.config.selector.missing_type: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'type' argument for selector.</yellow>"
warning.config.selector.invalid_type: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is using an invalid selector type '<arg:2>'.</yellow>"
warning.config.selector.invalid_target: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is using an invalid selector target '<arg:2>'.</yellow>"

View File

@@ -94,7 +94,10 @@ warning.config.condition.permission.missing_permission: "<yellow>在文件 <arg:
warning.config.condition.equals.missing_value1: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 缺少 'equals' 条件必需的 'value1' 参数</yellow>"
warning.config.condition.equals.missing_value2: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 缺少 'equals' 条件必需的 'value2' 参数</yellow>"
warning.config.condition.expression.missing_expression: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 缺少 'expression' 条件必需的 'expression' 参数</yellow>"
warning.config.condition.is_null.missing_argument: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 缺少 'is_null' 条件的必需的 'argument' 参数.</yellow>"
warning.config.structure.not_section: "<yellow>在文件 <arg:0> 发现问题 - 配置项 '<arg:1>' 应为配置段落 但实际类型为 '<arg:2>'</yellow>"
warning.config.condition.hand.missing_hand: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'hand' argument for 'hand' condition.</yellow>"
warning.config.condition.hand.invalid_hand: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is using an invalid 'hand' argument '<arg:2>' for 'hand' condition. Allowed hand types: [<arg:3>]</yellow>"
warning.config.image.duplicate: "<yellow>在文件 <arg:0> 发现问题 - 重复的图片配置 '<arg:1>' 请检查其他文件中是否存在相同配置</yellow>"
warning.config.image.missing_height: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 缺少必需的 'height' 参数</yellow>"
warning.config.image.height_ascent_conflict: "<yellow>在文件 <arg:0> 发现问题 - 图片 '<arg:1>' 违反位图规则: 'height' 参数 '<arg:2>' 必须不小于 'ascent' 参数 '<arg:3>'</yellow>"
@@ -317,6 +320,10 @@ warning.config.function.message.missing_message: "<yellow>在文件 <arg:0> 中
warning.config.function.open_window.missing_gui_type: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 缺少 'open_window' 函数必需的 'gui-type' 参数</yellow>"
warning.config.function.open_window.invalid_gui_type: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 为 'open_window' 函数使用了无效的 GUI 类型 <arg:2>. 允许的类型: [<arg:3>]。</yellow>"
warning.config.function.run.missing_functions: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 缺少 'run' 函数必需的 'functions' 参数</yellow>"
warning.config.function.place_block.missing_block_state: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 缺少 'place_block' 函数必需的 'block-state' 参数.</yellow>"
warning.config.function.set_food.missing_food: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'food' argument for 'set_food' function.</yellow>"
warning.config.function.set_saturation.missing_saturation: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'saturation' argument for 'set_saturation' function.</yellow>"
warning.config.function.play_sound.missing_sound: "<yellow>Issue found in file <arg:0> - The config '<arg:1>' is missing the required 'sound' argument for 'play_sound' function.</yellow>"
warning.config.selector.missing_type: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 缺少选择器必需的 'type' 参数</yellow>"
warning.config.selector.invalid_type: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 使用了无效的选择器类型 '<arg:2>'</yellow>"
warning.config.selector.invalid_target: "<yellow>在文件 <arg:0> 中发现问题 - 配置项 '<arg:1>' 使用了无效的选择器目标 '<arg:2>'</yellow>"

View File

@@ -3,8 +3,11 @@ package net.momirealms.craftengine.bukkit.api;
import net.momirealms.craftengine.bukkit.entity.BukkitEntity;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.world.BukkitBlockInWorld;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
import net.momirealms.craftengine.core.world.WorldPosition;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
@@ -29,4 +32,8 @@ public final class BukkitAdaptors {
public static BukkitBlockInWorld adapt(final Block block) {
return new BukkitBlockInWorld(block);
}
public static Location toLocation(WorldPosition position) {
return LocationUtils.toLocation(position);
}
}

View File

@@ -1,13 +1,14 @@
package net.momirealms.craftengine.bukkit.api;
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture;
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager;
import net.momirealms.craftengine.bukkit.entity.furniture.LoadedFurniture;
import net.momirealms.craftengine.bukkit.nms.CollisionEntity;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
import net.momirealms.craftengine.core.entity.furniture.AnchorType;
import net.momirealms.craftengine.core.entity.furniture.CustomFurniture;
import net.momirealms.craftengine.core.entity.furniture.Furniture;
import net.momirealms.craftengine.core.entity.furniture.FurnitureExtraData;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.loot.LootTable;
@@ -48,7 +49,7 @@ public final class CraftEngineFurniture {
* @return the loaded furniture
*/
@Nullable
public static LoadedFurniture place(Location location, Key furnitureId) {
public static Furniture place(Location location, Key furnitureId) {
CustomFurniture furniture = byId(furnitureId);
if (furniture == null) return null;
return place(location, furnitureId, furniture.getAnyPlacement());
@@ -63,7 +64,7 @@ public final class CraftEngineFurniture {
* @return the loaded furniture
*/
@Nullable
public static LoadedFurniture place(Location location, Key furnitureId, AnchorType anchorType) {
public static Furniture place(Location location, Key furnitureId, AnchorType anchorType) {
CustomFurniture furniture = byId(furnitureId);
if (furniture == null) return null;
return BukkitFurnitureManager.instance().place(location, furniture, FurnitureExtraData.builder().anchorType(anchorType).build(), true);
@@ -78,7 +79,7 @@ public final class CraftEngineFurniture {
* @return the loaded furniture
*/
@NotNull
public static LoadedFurniture place(Location location, CustomFurniture furniture, AnchorType anchorType) {
public static Furniture place(Location location, CustomFurniture furniture, AnchorType anchorType) {
return BukkitFurnitureManager.instance().place(location, furniture, FurnitureExtraData.builder().anchorType(anchorType).build(), true);
}
@@ -92,7 +93,7 @@ public final class CraftEngineFurniture {
* @return the loaded furniture
*/
@Nullable
public static LoadedFurniture place(Location location, Key furnitureId, AnchorType anchorType, boolean playSound) {
public static Furniture place(Location location, Key furnitureId, AnchorType anchorType, boolean playSound) {
CustomFurniture furniture = byId(furnitureId);
if (furniture == null) return null;
return BukkitFurnitureManager.instance().place(location, furniture, FurnitureExtraData.builder().anchorType(anchorType).build(), playSound);
@@ -108,7 +109,7 @@ public final class CraftEngineFurniture {
* @return the loaded furniture
*/
@NotNull
public static LoadedFurniture place(Location location, CustomFurniture furniture, AnchorType anchorType, boolean playSound) {
public static Furniture place(Location location, CustomFurniture furniture, AnchorType anchorType, boolean playSound) {
return BukkitFurnitureManager.instance().place(location, furniture, FurnitureExtraData.builder().anchorType(anchorType).build(), playSound);
}
@@ -152,7 +153,7 @@ public final class CraftEngineFurniture {
* @return the loaded furniture
*/
@Nullable
public static LoadedFurniture getLoadedFurnitureByBaseEntity(@NotNull Entity baseEntity) {
public static Furniture getLoadedFurnitureByBaseEntity(@NotNull Entity baseEntity) {
return BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(baseEntity.getEntityId());
}
@@ -163,7 +164,7 @@ public final class CraftEngineFurniture {
* @return the loaded furniture
*/
@Nullable
public static LoadedFurniture getLoadedFurnitureBySeat(@NotNull Entity seat) {
public static Furniture 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().loadedFurnitureByRealEntityId(baseEntityId);
@@ -172,107 +173,108 @@ public final class CraftEngineFurniture {
/**
* Removes furniture
*
* @param furniture furniture base entity
* @param entity furniture base entity
* @return success or not
*/
public static boolean remove(@NotNull Entity furniture) {
if (!isFurniture(furniture)) return false;
LoadedFurniture loadedFurniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(furniture.getEntityId());
if (loadedFurniture == null) return false;
loadedFurniture.destroy();
public static boolean remove(@NotNull Entity entity) {
if (!isFurniture(entity)) return false;
BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entity.getEntityId());
if (furniture == null) return false;
furniture.destroy();
return true;
}
/**
* Removes furniture, with more options
*
* @param furniture furniture base entity
* @param entity furniture base entity
* @param dropLoot whether to drop loots
* @param playSound whether to play break sound
* @return success or not
*/
public static boolean remove(@NotNull Entity furniture,
public static boolean remove(@NotNull Entity entity,
boolean dropLoot,
boolean playSound) {
if (!isFurniture(furniture)) return false;
LoadedFurniture loadedFurniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(furniture.getEntityId());
if (loadedFurniture == null) return false;
remove(loadedFurniture, (net.momirealms.craftengine.core.entity.player.Player) null, dropLoot, playSound);
if (!isFurniture(entity)) return false;
BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entity.getEntityId());
if (furniture == null) return false;
remove(furniture, (net.momirealms.craftengine.core.entity.player.Player) null, dropLoot, playSound);
return true;
}
/**
* Removes furniture, with more options
*
* @param furniture furniture base entity
* @param entity furniture base entity
* @param player the player who removes the furniture
* @param dropLoot whether to drop loots
* @param playSound whether to play break sound
* @return success or not
*/
public static boolean remove(@NotNull Entity furniture,
public static boolean remove(@NotNull Entity entity,
@Nullable Player player,
boolean dropLoot,
boolean playSound) {
if (!isFurniture(furniture)) return false;
LoadedFurniture loadedFurniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(furniture.getEntityId());
if (loadedFurniture == null) return false;
remove(loadedFurniture, player, dropLoot, playSound);
if (!isFurniture(entity)) return false;
Furniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entity.getEntityId());
if (furniture == null) return false;
remove(furniture, player, dropLoot, playSound);
return true;
}
/**
* Removes furniture by providing furniture instance
*
* @param loadedFurniture loaded furniture
* @param furniture loaded furniture
* @param dropLoot whether to drop loots
* @param playSound whether to play break sound
*/
public static void remove(@NotNull LoadedFurniture loadedFurniture,
public static void remove(@NotNull Furniture furniture,
boolean dropLoot,
boolean playSound) {
remove(loadedFurniture, (net.momirealms.craftengine.core.entity.player.Player) null, dropLoot, playSound);
remove(furniture, (net.momirealms.craftengine.core.entity.player.Player) null, dropLoot, playSound);
}
/**
* Removes furniture by providing furniture instance
*
* @param loadedFurniture loaded furniture
* @param furniture loaded furniture
* @param player the player who removes the furniture
* @param dropLoot whether to drop loots
* @param playSound whether to play break sound
*/
public static void remove(@NotNull LoadedFurniture loadedFurniture,
public static void remove(@NotNull Furniture furniture,
@Nullable Player player,
boolean dropLoot,
boolean playSound) {
remove(loadedFurniture, player == null ? null : BukkitCraftEngine.instance().adapt(player), dropLoot, playSound);
remove(furniture, player == null ? null : BukkitCraftEngine.instance().adapt(player), dropLoot, playSound);
}
/**
* Removes furniture by providing furniture instance
*
* @param loadedFurniture loaded furniture
* @param furniture loaded furniture
* @param player the player who removes the furniture
* @param dropLoot whether to drop loots
* @param playSound whether to play break sound
*/
@SuppressWarnings("unchecked")
public static void remove(@NotNull LoadedFurniture loadedFurniture,
public static void remove(@NotNull Furniture furniture,
@Nullable net.momirealms.craftengine.core.entity.player.Player player,
boolean dropLoot,
boolean playSound) {
Location location = loadedFurniture.dropLocation();
loadedFurniture.destroy();
LootTable<ItemStack> lootTable = (LootTable<ItemStack>) loadedFurniture.config().lootTable();
if (!furniture.isValid()) return;
Location location = ((BukkitFurniture) furniture).dropLocation();
furniture.destroy();
LootTable<ItemStack> lootTable = (LootTable<ItemStack>) furniture.config().lootTable();
World world = new BukkitWorld(location.getWorld());
WorldPosition position = new WorldPosition(world, location.getX(), location.getY(), location.getZ());
if (dropLoot && lootTable != null) {
ContextHolder.Builder builder = ContextHolder.builder()
.withParameter(DirectContextParameters.POSITION, position)
.withParameter(DirectContextParameters.FURNITURE, loadedFurniture)
.withOptionalParameter(DirectContextParameters.FURNITURE_ITEM, loadedFurniture.extraData().item().orElse(null));
.withParameter(DirectContextParameters.FURNITURE, furniture)
.withOptionalParameter(DirectContextParameters.FURNITURE_ITEM, furniture.extraData().item().orElse(null));
if (player != null) {
builder.withParameter(DirectContextParameters.PLAYER, player);
}
@@ -282,7 +284,7 @@ public final class CraftEngineFurniture {
}
}
if (playSound) {
world.playBlockSound(position, loadedFurniture.config().settings().sounds().breakSound());
world.playBlockSound(position, furniture.config().settings().sounds().breakSound());
}
}
}

View File

@@ -1,6 +1,7 @@
package net.momirealms.craftengine.bukkit.api.event;
import net.momirealms.craftengine.bukkit.entity.furniture.LoadedFurniture;
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture;
import net.momirealms.craftengine.core.entity.furniture.Furniture;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
@@ -11,10 +12,10 @@ import org.jetbrains.annotations.NotNull;
public class FurnitureBreakEvent extends PlayerEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList();
private boolean cancelled;
private final LoadedFurniture furniture;
private final BukkitFurniture furniture;
public FurnitureBreakEvent(@NotNull Player player,
@NotNull LoadedFurniture furniture) {
@NotNull BukkitFurniture furniture) {
super(player);
this.furniture = furniture;
}
@@ -25,7 +26,7 @@ public class FurnitureBreakEvent extends PlayerEvent implements Cancellable {
}
@NotNull
public LoadedFurniture furniture() {
public Furniture furniture() {
return this.furniture;
}

View File

@@ -1,6 +1,7 @@
package net.momirealms.craftengine.bukkit.api.event;
import net.momirealms.craftengine.bukkit.entity.furniture.LoadedFurniture;
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture;
import net.momirealms.craftengine.core.entity.furniture.Furniture;
import net.momirealms.craftengine.core.entity.player.InteractionHand;
import org.bukkit.Location;
import org.bukkit.entity.Player;
@@ -12,12 +13,12 @@ import org.jetbrains.annotations.NotNull;
public class FurnitureInteractEvent extends PlayerEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList();
private boolean cancelled;
private final LoadedFurniture furniture;
private final BukkitFurniture furniture;
private final InteractionHand hand;
private final Location interactionPoint;
public FurnitureInteractEvent(@NotNull Player player,
@NotNull LoadedFurniture furniture,
@NotNull BukkitFurniture furniture,
@NotNull InteractionHand hand,
@NotNull Location interactionPoint) {
super(player);
@@ -42,7 +43,7 @@ public class FurnitureInteractEvent extends PlayerEvent implements Cancellable {
}
@NotNull
public LoadedFurniture furniture() {
public Furniture furniture() {
return this.furniture;
}

View File

@@ -1,6 +1,7 @@
package net.momirealms.craftengine.bukkit.api.event;
import net.momirealms.craftengine.bukkit.entity.furniture.LoadedFurniture;
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture;
import net.momirealms.craftengine.core.entity.furniture.Furniture;
import net.momirealms.craftengine.core.entity.player.InteractionHand;
import org.bukkit.Location;
import org.bukkit.entity.Player;
@@ -12,12 +13,12 @@ import org.jetbrains.annotations.NotNull;
public class FurniturePlaceEvent extends PlayerEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList();
private final Location location;
private final LoadedFurniture furniture;
private final BukkitFurniture furniture;
private final InteractionHand hand;
private boolean cancelled;
public FurniturePlaceEvent(@NotNull Player player,
@NotNull LoadedFurniture furniture,
@NotNull BukkitFurniture furniture,
@NotNull Location location,
@NotNull InteractionHand hand) {
super(player);
@@ -32,7 +33,7 @@ public class FurniturePlaceEvent extends PlayerEvent implements Cancellable {
}
@NotNull
public LoadedFurniture furniture() {
public Furniture furniture() {
return this.furniture;
}

View File

@@ -17,8 +17,8 @@ import net.momirealms.craftengine.core.loot.LootTable;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.context.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.plugin.event.EventTrigger;
import net.momirealms.craftengine.core.util.Cancellable;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.BlockPos;

View File

@@ -3,12 +3,10 @@ package net.momirealms.craftengine.bukkit.block;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import dev.dejvokep.boostedyaml.YamlDocument;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
@@ -28,12 +26,8 @@ import net.momirealms.craftengine.core.pack.model.generation.ModelGeneration;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.config.ConfigParser;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.context.function.Function;
import net.momirealms.craftengine.core.plugin.event.EventFunctions;
import net.momirealms.craftengine.core.plugin.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.context.event.EventFunctions;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.registry.Holder;
import net.momirealms.craftengine.core.registry.WritableRegistry;
@@ -45,10 +39,9 @@ import org.bukkit.Registry;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nullable;
import java.io.File;
import java.lang.reflect.Field;
import java.nio.file.Path;
@@ -58,50 +51,34 @@ public class BukkitBlockManager extends AbstractBlockManager {
private static BukkitBlockManager instance;
private final BukkitCraftEngine plugin;
private final BlockParser blockParser;
// A temporary map used to detect whether the same block state corresponds to multiple models.
private final Map<Integer, Key> tempRegistryIdConflictMap = new Int2ObjectOpenHashMap<>();
// A temporary map that converts the custom block registered on the server to the vanilla block ID.
private final Map<Integer, Integer> tempBlockAppearanceConvertor = new Int2IntOpenHashMap();
// A temporary map that stores the model path of a certain vanilla block state
private final Map<Integer, JsonElement> tempVanillaBlockStateModels = new Int2ObjectOpenHashMap<>();
// The total amount of blocks registered
private int customBlockCount;
protected final ImmutableBlockState[] stateId2ImmutableBlockStates;
// Minecraft objects
// Cached new blocks $ holders
private ImmutableMap<Key, Integer> internalId2StateId;
private ImmutableMap<Integer, Object> stateId2BlockHolder;
private Map<Key, Integer> internalId2StateId;
private Map<Integer, Object> stateId2BlockHolder;
// This map is used to change the block states that are not necessarily needed into a certain block state
private ImmutableMap<Integer, Integer> blockAppearanceMapper;
private Map<Integer, Integer> blockAppearanceMapper;
// Used to automatically arrange block states for strings such as minecraft:note_block:0
private ImmutableMap<Key, List<Integer>> blockAppearanceArranger;
private ImmutableMap<Key, List<Integer>> realBlockArranger;
private Map<Key, List<Integer>> blockAppearanceArranger;
private Map<Key, List<Integer>> realBlockArranger;
// Record the amount of real blocks by block type
private LinkedHashMap<Key, Integer> registeredRealBlockSlots;
private Map<Key, Integer> registeredRealBlockSlots;
// A set of blocks that sounds have been removed
private ImmutableSet<Object> affectedSoundBlocks;
private ImmutableMap<Key, Key> soundMapper;
private Set<Object> affectedSoundBlocks;
private Map<Key, Key> soundMapper;
// A list to record the order of registration
private List<Key> blockRegisterOrder = new ObjectArrayList<>();
// a reverted mapper
private final Map<Integer, List<Integer>> appearanceToRealState = new Int2ObjectOpenHashMap<>();
// Used to store override information of json files
private final Map<Key, Map<String, JsonElement>> blockStateOverrides = new HashMap<>();
// for mod, real block id -> state models
private final Map<Key, JsonElement> modBlockStates = new HashMap<>();
// Event listeners
private final BlockEventListener blockEventListener;
private final FallingBlockRemoveListener fallingBlockRemoveListener;
private Map<Integer, List<String>> clientBoundTags = Map.of();
private Map<Integer, List<String>> previousTags = Map.of();
// cached tag packet
protected Object cachedUpdateTagsPacket;
public BukkitBlockManager(BukkitCraftEngine plugin) {
super(plugin);
instance = this;
this.plugin = plugin;
this.blockParser = new BlockParser();
this.initVanillaRegistry();
@@ -120,42 +97,33 @@ public class BukkitBlockManager extends AbstractBlockManager {
if (enableNoteBlocks) {
this.recordVanillaNoteBlocks();
}
if (VersionHelper.isOrAbove1_20_3()) {
this.fallingBlockRemoveListener = new FallingBlockRemoveListener();
} else this.fallingBlockRemoveListener = null;
this.stateId2ImmutableBlockStates = new ImmutableBlockState[customBlockCount];
this.fallingBlockRemoveListener = VersionHelper.isOrAbove1_20_3() ? new FallingBlockRemoveListener() : null;
this.stateId2ImmutableBlockStates = new ImmutableBlockState[this.customBlockCount];
Arrays.fill(this.stateId2ImmutableBlockStates, EmptyBlock.INSTANCE.defaultState());
instance = this;
this.resetPacketConsumers();
}
public List<Key> blockRegisterOrder() {
return Collections.unmodifiableList(this.blockRegisterOrder);
}
public static BukkitBlockManager instance() {
return instance;
}
public List<Key> blockRegisterOrder() {
return Collections.unmodifiableList(this.blockRegisterOrder);
}
@Override
public void delayedInit() {
Bukkit.getPluginManager().registerEvents(this.blockEventListener, plugin.bootstrap());
Bukkit.getPluginManager().registerEvents(this.blockEventListener, this.plugin.bootstrap());
if (this.fallingBlockRemoveListener != null) {
Bukkit.getPluginManager().registerEvents(this.fallingBlockRemoveListener, plugin.bootstrap());
Bukkit.getPluginManager().registerEvents(this.fallingBlockRemoveListener, this.plugin.bootstrap());
}
}
@Override
public void unload() {
super.unload();
this.clearCache();
this.appearanceToRealState.clear();
this.blockStateOverrides.clear();
this.modBlockStates.clear();
if (EmptyBlock.STATE != null)
Arrays.fill(this.stateId2ImmutableBlockStates, EmptyBlock.STATE);
this.previousTags = this.clientBoundTags;
this.clientBoundTags = new HashMap<>();
}
@Override
@@ -172,15 +140,14 @@ public class BukkitBlockManager extends AbstractBlockManager {
@Override
public void delayedLoad() {
initSuggestions();
resetPacketConsumers();
clearCache();
resendTags();
this.resetPacketConsumers();
super.delayedLoad();
}
private void resendTags() {
@Override
protected void resendTags() {
// if there's no change
if (this.clientBoundTags.equals(this.previousTags)) return;
if (this.clientBoundTags.equals(this.previousClientBoundTags)) return;
List<TagUtils.TagEntry> list = new ArrayList<>();
for (Map.Entry<Integer, List<String>> entry : this.clientBoundTags.entrySet()) {
list.add(new TagUtils.TagEntry(entry.getKey(), entry.getValue()));
@@ -197,10 +164,19 @@ public class BukkitBlockManager extends AbstractBlockManager {
}
}
private void clearCache() {
this.tempRegistryIdConflictMap.clear();
this.tempBlockAppearanceConvertor.clear();
this.tempVanillaBlockStateModels.clear();
@Nullable
@Override
public BlockStateWrapper createPackedBlockState(String blockState) {
ImmutableBlockState state = BlockStateParser.deserialize(blockState);
if (state != null) {
return state.customBlockState();
}
try {
BlockData blockData = Bukkit.createBlockData(blockState);
return BlockStateUtils.toPackedBlockState(blockData);
} catch (IllegalArgumentException e) {
return null;
}
}
@Nullable
@@ -209,11 +185,13 @@ public class BukkitBlockManager extends AbstractBlockManager {
}
@NotNull
@Override
public ImmutableBlockState getImmutableBlockStateUnsafe(int stateId) {
return this.stateId2ImmutableBlockStates[stateId - BlockStateUtils.vanillaStateSize()];
}
@Nullable
@Override
public ImmutableBlockState getImmutableBlockState(int stateId) {
if (!BlockStateUtils.isVanillaBlock(stateId)) {
return this.stateId2ImmutableBlockStates[stateId - BlockStateUtils.vanillaStateSize()];
@@ -221,41 +199,53 @@ public class BukkitBlockManager extends AbstractBlockManager {
return null;
}
@Override
public Map<Key, JsonElement> modBlockStates() {
return Collections.unmodifiableMap(this.modBlockStates);
}
@Override
public ConfigParser parser() {
return this.blockParser;
}
@Override
public Map<Key, Map<String, JsonElement>> blockOverrides() {
return Collections.unmodifiableMap(this.blockStateOverrides);
public void addBlock(Key id, CustomBlock customBlock) {
// bind appearance and real state
for (ImmutableBlockState state : customBlock.variantProvider().states()) {
ImmutableBlockState previous = this.stateId2ImmutableBlockStates[state.customBlockState().registryId() - BlockStateUtils.vanillaStateSize()];
if (previous != null && !previous.isEmpty()) {
throw new LocalizedResourceConfigException("warning.config.block.state.bind_failed", state.toString(), previous.toString());
}
this.stateId2ImmutableBlockStates[state.customBlockState().registryId() - BlockStateUtils.vanillaStateSize()] = state;
this.tempBlockAppearanceConvertor.put(state.customBlockState().registryId(), state.vanillaBlockState().registryId());
this.appearanceToRealState.computeIfAbsent(state.vanillaBlockState().registryId(), k -> new IntArrayList()).add(state.customBlockState().registryId());
}
super.addBlock(id, customBlock);
}
public ImmutableMap<Key, List<Integer>> blockAppearanceArranger() {
@Override
public Key getBlockOwnerId(BlockStateWrapper state) {
return BlockStateUtils.getBlockOwnerIdFromState(state.handle());
}
@Override
public int availableAppearances(Key blockType) {
return Optional.ofNullable(this.registeredRealBlockSlots.get(blockType)).orElse(0);
}
@NotNull
public Map<Key, List<Integer>> blockAppearanceArranger() {
return this.blockAppearanceArranger;
}
public ImmutableMap<Key, List<Integer>> realBlockArranger() {
@NotNull
public Map<Key, List<Integer>> realBlockArranger() {
return this.realBlockArranger;
}
@Nullable
public List<Integer> appearanceToRealStates(int appearanceStateId) {
return this.appearanceToRealState.get(appearanceStateId);
}
private void initMirrorRegistry() {
int size = RegistryUtils.currentBlockRegistrySize();
PackedBlockState[] states = new PackedBlockState[size];
BlockStateWrapper[] states = new BlockStateWrapper[size];
for (int i = 0; i < size; i++) {
states[i] = new PackedBlockState(BlockStateUtils.idToBlockState(i), i);
states[i] = BlockStateWrapper.create(BlockStateUtils.idToBlockState(i), i, BlockStateUtils.isVanillaBlock(i));
}
BlockRegistryMirror.init(states, new PackedBlockState(Reflections.instance$Blocks$STONE$defaultState, BlockStateUtils.blockStateToId(Reflections.instance$Blocks$STONE$defaultState)));
BlockRegistryMirror.init(states, BlockStateWrapper.vanilla(Reflections.instance$Blocks$STONE$defaultState, BlockStateUtils.blockStateToId(Reflections.instance$Blocks$STONE$defaultState)));
}
private void registerEmptyBlock() {
@@ -276,25 +266,25 @@ public class BukkitBlockManager extends AbstractBlockManager {
private void initVanillaRegistry() {
int vanillaStateCount;
if (plugin.hasMod()) {
if (this.plugin.hasMod()) {
try {
Class<?> modClass = ReflectionUtils.getClazz(CraftEngine.MOD_CLASS);
Field amountField = ReflectionUtils.getDeclaredField(modClass, "vanillaRegistrySize");
vanillaStateCount = amountField.getInt(null);
} catch (Exception e) {
vanillaStateCount = RegistryUtils.currentBlockRegistrySize();
plugin.logger().severe("Fatal error", e);
this.plugin.logger().severe("Fatal error", e);
}
} else {
vanillaStateCount = RegistryUtils.currentBlockRegistrySize();
}
plugin.logger().info("Vanilla block count: " + vanillaStateCount);
this.plugin.logger().info("Vanilla block count: " + vanillaStateCount);
BlockStateUtils.init(vanillaStateCount);
}
@SuppressWarnings("unchecked")
private void registerBlocks() {
plugin.logger().info("Registering blocks. Please wait...");
this.plugin.logger().info("Registering blocks. Please wait...");
try {
ImmutableMap.Builder<Key, Integer> builder1 = ImmutableMap.builder();
ImmutableMap.Builder<Integer, Object> builder2 = ImmutableMap.builder();
@@ -367,7 +357,7 @@ public class BukkitBlockManager extends AbstractBlockManager {
parseVanillaBlock(pack, path, id, section);
} else {
// check duplicated config
if (byId.containsKey(id)) {
if (BukkitBlockManager.this.byId.containsKey(id)) {
throw new LocalizedResourceConfigException("warning.config.block.duplicate");
}
parseCustomBlock(pack, path, id, section);
@@ -376,128 +366,73 @@ public class BukkitBlockManager extends AbstractBlockManager {
private void parseCustomBlock(Pack pack, Path path, Key id, Map<String, Object> section) {
// read block settings
BlockSettings settings = BlockSettings.fromMap(MiscUtils.castToMap(section.getOrDefault("settings", Map.of()), false));
// read loot table
LootTable<ItemStack> lootTable = LootTable.fromMap(MiscUtils.castToMap(section.getOrDefault("loot", Map.of()), false));
BlockSettings settings = BlockSettings.fromMap(MiscUtils.castToMap(section.get("settings"), true));
// read states
Map<String, Property<?>> properties;
Map<String, Integer> appearances;
Map<String, VariantState> variants;
Object stateObj = ResourceConfigUtils.requireNonNullOrThrow(ResourceConfigUtils.get(section, "state", "states"), "warning.config.block.missing_state");
Map<String, Object> stateSection = MiscUtils.castToMap(stateObj, true);
Map<String, Object> stateSection = MiscUtils.castToMap(ResourceConfigUtils.requireNonNullOrThrow(ResourceConfigUtils.get(section, "state", "states"), "warning.config.block.missing_state"), true);
boolean singleState = !stateSection.containsKey("properties");
// single state
if (!stateSection.containsKey("properties")) {
if (singleState) {
properties = Map.of();
int internalId = ResourceConfigUtils.getAsInt(stateSection.getOrDefault("id", -1), "id");
if (internalId < 0) {
throw new LocalizedResourceConfigException("warning.config.block.state.missing_real_id");
}
Pair<Key, Integer> pair = parseAppearanceSection(id, stateSection);
if (pair == null) return;
appearances = Map.of("default", pair.right());
String internalBlock = pair.left().value() + "_" + internalId;
Key internalBlockId = Key.of(Key.DEFAULT_NAMESPACE, internalBlock);
int internalId = ResourceConfigUtils.getAsInt(ResourceConfigUtils.requireNonNullOrThrow(stateSection.get("id"), "warning.config.block.state.missing_real_id"), "id");
VanillaBlockState vanillaBlock = getVanillaBlock(id, stateSection);
appearances = Map.of("", vanillaBlock.registryId());
Key internalBlockId = Key.of(Key.DEFAULT_NAMESPACE, vanillaBlock.type().value() + "_" + internalId);
int internalBlockRegistryId = Optional.ofNullable(internalId2StateId.get(internalBlockId)).orElse(-1);
if (internalBlockRegistryId == -1) {
throw new LocalizedResourceConfigException("warning.config.block.state.invalid_real_id",
internalBlock,
String.valueOf(registeredRealBlockSlots.get(pair.left()) - 1));
throw new LocalizedResourceConfigException("warning.config.block.state.invalid_real_id", internalBlockId.toString(), String.valueOf(availableAppearances(vanillaBlock.type()) - 1));
}
variants = Map.of("", new VariantState("default", settings, internalBlockRegistryId));
variants = Map.of("", new VariantState("", settings, internalBlockRegistryId));
} else {
// properties
Map<String, Object> propertySection = MiscUtils.castToMap(stateSection.get("properties"), true);
if (propertySection == null) {
throw new LocalizedResourceConfigException("warning.config.block.state.missing_properties");
}
properties = parseProperties(propertySection);
properties = getProperties(MiscUtils.castToMap(ResourceConfigUtils.requireNonNullOrThrow(stateSection.get("properties"), "warning.config.block.state.missing_properties"), true));
// appearance
Map<String, Object> appearancesSection = MiscUtils.castToMap(stateSection.get("appearances"), true);
if (appearancesSection == null) {
throw new LocalizedResourceConfigException("warning.config.block.state.missing_appearances");
}
appearances = new HashMap<>();
Map<String, Key> tempTypeMap = new HashMap<>();
for (Map.Entry<String, Object> appearanceEntry : appearancesSection.entrySet()) {
if (appearanceEntry.getValue() instanceof Map<?, ?> appearanceSection) {
Pair<Key, Integer> pair = parseAppearanceSection(id, MiscUtils.castToMap(appearanceSection, false));
if (pair == null) return;
appearances.put(appearanceEntry.getKey(), pair.right());
tempTypeMap.put(appearanceEntry.getKey(), pair.left());
Map<String, Key> appearance2BlockType = new HashMap<>();
for (Map.Entry<String, Object> appearanceEntry : MiscUtils.castToMap(ResourceConfigUtils.requireNonNullOrThrow(stateSection.get("appearances"), "warning.config.block.state.missing_appearances"), false).entrySet()) {
if (appearanceEntry.getValue() instanceof Map<?, ?>) {
VanillaBlockState vanillaBlock = getVanillaBlock(id, MiscUtils.castToMap(appearanceEntry.getValue(), false));
appearances.put(appearanceEntry.getKey(), vanillaBlock.registryId());
appearance2BlockType.put(appearanceEntry.getKey(), vanillaBlock.type());
}
}
// variants
Map<String, Object> variantsSection = MiscUtils.castToMap(stateSection.get("variants"), true);
if (variantsSection == null) {
throw new LocalizedResourceConfigException("warning.config.block.state.missing_variants");
}
variants = new HashMap<>();
for (Map.Entry<String, Object> variantEntry : variantsSection.entrySet()) {
if (variantEntry.getValue() instanceof Map<?, ?> variantSection0) {
Map<String, Object> variantSection = MiscUtils.castToMap(variantSection0, false);
String variantName = variantEntry.getKey();
String appearance = (String) variantSection.get("appearance");
if (appearance == null) {
throw new LocalizedResourceConfigException("warning.config.block.state.variant.missing_appearance", variantName);
}
for (Map.Entry<String, Object> variantEntry : MiscUtils.castToMap(ResourceConfigUtils.requireNonNullOrThrow(stateSection.get("variants"), "warning.config.block.state.missing_variants"), false).entrySet()) {
if (variantEntry.getValue() instanceof Map<?, ?>) {
Map<String, Object> variantSection = MiscUtils.castToMap(variantEntry.getValue(), false);
String variantNBT = variantEntry.getKey();
String appearance = ResourceConfigUtils.requireNonEmptyStringOrThrow(variantSection.get("appearance"), "warning.config.block.state.variant.missing_appearance");
if (!appearances.containsKey(appearance)) {
throw new LocalizedResourceConfigException("warning.config.block.state.variant.invalid_appearance", variantName, appearance);
throw new LocalizedResourceConfigException("warning.config.block.state.variant.invalid_appearance", variantNBT, appearance);
}
int internalId = ResourceConfigUtils.getAsInt(variantSection.getOrDefault("id", -1), "id");
Key baseBlock = tempTypeMap.get(appearance);
int internalId = ResourceConfigUtils.getAsInt(ResourceConfigUtils.requireNonNullOrThrow(variantSection.get("id"), "warning.config.block.state.missing_real_id"), "id");
Key baseBlock = appearance2BlockType.get(appearance);
Key internalBlockId = Key.of(Key.DEFAULT_NAMESPACE, baseBlock.value() + "_" + internalId);
int internalBlockRegistryId = Optional.ofNullable(internalId2StateId.get(internalBlockId)).orElse(-1);
if (internalBlockRegistryId == -1) {
throw new LocalizedResourceConfigException("warning.config.block.state.invalid_real_id",
internalBlockId.toString(),
String.valueOf(registeredRealBlockSlots.getOrDefault(baseBlock, 1) - 1));
throw new LocalizedResourceConfigException("warning.config.block.state.invalid_real_id", internalBlockId.toString(), String.valueOf(availableAppearances(baseBlock) - 1));
}
Map<String, Object> anotherSetting = MiscUtils.castToMap(variantSection.get("settings"), true);
variants.put(variantName, new VariantState(appearance, anotherSetting == null ? settings : BlockSettings.ofFullCopy(settings, anotherSetting), internalBlockRegistryId));
variants.put(variantNBT, new VariantState(appearance, anotherSetting == null ? settings : BlockSettings.ofFullCopy(settings, anotherSetting), internalBlockRegistryId));
}
}
}
Object eventsObj = ResourceConfigUtils.get(section, "events", "event");
EnumMap<EventTrigger, List<Function<PlayerOptionalContext>>> events = EventFunctions.parseEvents(eventsObj);
Map<String, Object> behaviors = MiscUtils.castToMap(section.getOrDefault("behavior", Map.of()), false);
CustomBlock block = BukkitCustomBlock.builder(id)
.appearances(appearances)
.variantMapper(variants)
.lootTable(lootTable)
.properties(properties)
.settings(settings)
.behavior(behaviors)
.events(events)
.lootTable(LootTable.fromMap(MiscUtils.castToMap(section.get("loot"), true)))
.behavior(MiscUtils.castToMap(section.get("behavior"), true))
.events(EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event")))
.build();
// bind appearance and real state
for (ImmutableBlockState state : block.variantProvider().states()) {
ImmutableBlockState previous = stateId2ImmutableBlockStates[state.customBlockState().registryId() - BlockStateUtils.vanillaStateSize()];
if (previous != null && !previous.isEmpty()) {
TranslationManager.instance().log("warning.config.block.state.bind_failed", path.toString(), id.toString(), state.toString(), previous.toString());
continue;
}
stateId2ImmutableBlockStates[state.customBlockState().registryId() - BlockStateUtils.vanillaStateSize()] = state;
tempBlockAppearanceConvertor.put(state.customBlockState().registryId(), state.vanillaBlockState().registryId());
appearanceToRealState.computeIfAbsent(state.vanillaBlockState().registryId(), k -> new IntArrayList()).add(state.customBlockState().registryId());
}
BukkitBlockManager.this.byId.put(id, block);
// generate mod assets
if (Config.generateModAssets()) {
for (ImmutableBlockState state : block.variantProvider().states()) {
Key realBlockId = BlockStateUtils.getBlockOwnerIdFromState(state.customBlockState());
modBlockStates.put(realBlockId, tempVanillaBlockStateModels.get(state.vanillaBlockState().registryId()));
}
}
addBlock(id, block);
}
private void parseVanillaBlock(Pack pack, Path path, Key id, Map<String, Object> section) {
@@ -518,7 +453,8 @@ public class BukkitBlockManager extends AbstractBlockManager {
}
}
private Map<String, Property<?>> parseProperties(Map<String, Object> propertiesSection) {
@NotNull
private Map<String, Property<?>> getProperties(Map<String, Object> propertiesSection) {
Map<String, Property<?>> properties = new HashMap<>();
for (Map.Entry<String, Object> entry : propertiesSection.entrySet()) {
Property<?> property = Properties.fromMap(entry.getKey(), MiscUtils.castToMap(entry.getValue(), false));
@@ -527,69 +463,40 @@ public class BukkitBlockManager extends AbstractBlockManager {
return properties;
}
@Nullable
private Pair<Key, Integer> parseAppearanceSection(Key id, Map<String, Object> section) {
@NotNull
private VanillaBlockState getVanillaBlock(Key id, Map<String, Object> section) {
// require state non null
Object vanillaStateString = section.get("state");
if (vanillaStateString == null) {
throw new LocalizedResourceConfigException("warning.config.block.state.missing_state");
}
String vanillaBlockStateTag = ResourceConfigUtils.requireNonEmptyStringOrThrow(section.get("state"), "warning.config.block.state.missing_state");
// get its registry id
int vanillaStateRegistryId = parseVanillaStateRegistryId(vanillaStateString.toString());
// check conflict
Key ifAny = this.tempRegistryIdConflictMap.get(vanillaStateRegistryId);
int vanillaBlockStateRegistryId = getVanillaBlockStateRegistryId(vanillaBlockStateTag);
// check if another block has occupied the appearance
// TODO blocks share the same look
Key ifAny = this.tempRegistryIdConflictMap.get(vanillaBlockStateRegistryId);
if (ifAny != null && !ifAny.equals(id)) {
throw new LocalizedResourceConfigException("warning.config.block.state.conflict", BlockStateUtils.fromBlockData(BlockStateUtils.idToBlockState(vanillaStateRegistryId)).getAsString(), ifAny.toString());
throw new LocalizedResourceConfigException("warning.config.block.state.conflict", BlockStateUtils.fromBlockData(BlockStateUtils.idToBlockState(vanillaBlockStateRegistryId)).getAsString(), ifAny.toString());
}
// require models not to be null
Object models = section.get("models");
if (models == null) {
models = section.get("model");
}
if (models == null) {
Object models = ResourceConfigUtils.requireNonNullOrThrow(ResourceConfigUtils.get(section, "models", "model"), "warning.config.block.state.missing_model");
List<JsonObject> variants = ResourceConfigUtils.parseConfigAsList(models, this::getVariantModel);
if (variants.isEmpty()) {
throw new LocalizedResourceConfigException("warning.config.block.state.missing_model");
}
List<JsonObject> variants = new ArrayList<>();
if (models instanceof Map<?, ?> singleModelSection) {
loadVariantModel(variants, MiscUtils.castToMap(singleModelSection, false));
} else if (models instanceof List<?> modelList) {
for (Object model : modelList) {
if (model instanceof Map<?,?> singleModelMap) {
loadVariantModel(variants, MiscUtils.castToMap(singleModelMap, false));
}
}
}
if (variants.isEmpty()) return null;
this.tempRegistryIdConflictMap.put(vanillaStateRegistryId, id);
String blockState = BlockStateUtils.idToBlockState(vanillaStateRegistryId).toString();
Key block = Key.of(blockState.substring(blockState.indexOf('{') + 1, blockState.lastIndexOf('}')));
String propertyData = blockState.substring(blockState.indexOf('[') + 1, blockState.lastIndexOf(']'));
Map<String, JsonElement> paths = this.blockStateOverrides.computeIfAbsent(block, k -> new HashMap<>());
if (variants.size() == 1) {
paths.put(propertyData, variants.get(0));
this.tempVanillaBlockStateModels.put(vanillaStateRegistryId, variants.get(0));
} else {
JsonArray array = new JsonArray();
for (JsonObject object : variants) {
array.add(object);
}
paths.put(propertyData, array);
this.tempVanillaBlockStateModels.put(vanillaStateRegistryId, array);
}
return Pair.of(block, vanillaStateRegistryId);
// TODO blocks share the same look
this.tempRegistryIdConflictMap.put(vanillaBlockStateRegistryId, id);
// gets the full block state
String blockState = BlockStateUtils.idToBlockState(vanillaBlockStateRegistryId).toString();
Key blockId = Key.of(blockState.substring(blockState.indexOf('{') + 1, blockState.lastIndexOf('}')));
String propertyNBT = blockState.substring(blockState.indexOf('[') + 1, blockState.lastIndexOf(']'));
// for generating assets
JsonElement combinedVariant = GsonHelper.combine(variants);
this.blockStateOverrides.computeIfAbsent(blockId, k -> new HashMap<>()).put(propertyNBT, combinedVariant);
this.tempVanillaBlockStateModels.put(vanillaBlockStateRegistryId, combinedVariant);
return new VanillaBlockState(blockId, propertyNBT, vanillaBlockStateRegistryId);
}
private void loadVariantModel(List<JsonObject> variants, Map<String, Object> singleModelMap) {
private JsonObject getVariantModel(Map<String, Object> singleModelMap) {
JsonObject json = new JsonObject();
String modelPath = (String) singleModelMap.get("path");
if (modelPath == null) {
throw new LocalizedResourceConfigException("warning.config.block.state.model.missing_path");
}
String modelPath = ResourceConfigUtils.requireNonEmptyStringOrThrow(singleModelMap.get("path"), "warning.config.block.state.model.missing_path");
if (!ResourceLocation.isValid(modelPath)) {
throw new LocalizedResourceConfigException("warning.config.block.state.model.invalid_path", modelPath);
}
@@ -602,10 +509,10 @@ public class BukkitBlockManager extends AbstractBlockManager {
if (generationMap != null) {
prepareModelGeneration(ModelGeneration.of(Key.of(modelPath), generationMap));
}
variants.add(json);
return json;
}
private int parseVanillaStateRegistryId(String blockState) {
private int getVanillaBlockStateRegistryId(String blockState) {
String[] split = blockState.split(":", 3);
if (split.length >= 4) {
throw new LocalizedResourceConfigException("warning.config.block.state.invalid_vanilla", blockState);

View File

@@ -10,8 +10,8 @@ import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.loot.LootTable;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.context.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.context.function.Function;
import net.momirealms.craftengine.core.plugin.event.EventTrigger;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.registry.Holder;
import net.momirealms.craftengine.core.registry.WritableRegistry;
@@ -26,18 +26,21 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.*;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class BukkitCustomBlock extends CustomBlock {
public class BukkitCustomBlock extends AbstractCustomBlock {
protected BukkitCustomBlock(
Key id,
Holder.Reference<CustomBlock> holder,
Map<String, Property<?>> properties,
Map<String, Integer> appearances,
Map<String, VariantState> variantMapper,
BlockSettings settings,
@NotNull EnumMap<EventTrigger, List<Function<PlayerOptionalContext>>> events,
@NotNull Key id,
@NotNull Holder.Reference<CustomBlock> holder,
@NotNull Map<String, Property<?>> properties,
@NotNull Map<String, Integer> appearances,
@NotNull Map<String, VariantState> variantMapper,
@NotNull BlockSettings settings,
@NotNull Map<EventTrigger, List<Function<PlayerOptionalContext>>> events,
@Nullable Map<String, Object> behavior,
@Nullable LootTable<?> lootTable
) {
@@ -147,17 +150,67 @@ public class BukkitCustomBlock extends CustomBlock {
}
public static Builder builder(Key id) {
return new Builder(id);
return new BuilderImpl(id);
}
public static class Builder extends CustomBlock.Builder {
public static class BuilderImpl implements Builder {
protected final Key id;
protected Map<String, Property<?>> properties;
protected Map<String, Integer> appearances;
protected Map<String, VariantState> variantMapper;
protected BlockSettings settings;
protected Map<String, Object> behavior;
protected LootTable<?> lootTable;
protected Map<EventTrigger, List<Function<PlayerOptionalContext>>> events;
protected Builder(Key id) {
super(id);
public BuilderImpl(Key id) {
this.id = id;
}
@Override
public CustomBlock build() {
public Builder events(Map<EventTrigger, List<Function<PlayerOptionalContext>>> events) {
this.events = events;
return this;
}
@Override
public Builder appearances(Map<String, Integer> appearances) {
this.appearances = appearances;
return this;
}
@Override
public Builder behavior(Map<String, Object> behavior) {
this.behavior = behavior;
return this;
}
@Override
public Builder lootTable(LootTable<?> lootTable) {
this.lootTable = lootTable;
return this;
}
@Override
public Builder properties(Map<String, Property<?>> properties) {
this.properties = properties;
return this;
}
@Override
public Builder settings(BlockSettings settings) {
this.settings = settings;
return this;
}
@Override
public Builder variantMapper(Map<String, VariantState> variantMapper) {
this.variantMapper = variantMapper;
return this;
}
@Override
public @NotNull CustomBlock build() {
// create or get block holder
Holder.Reference<CustomBlock> holder = BuiltInRegistries.BLOCK.get(id).orElseGet(() ->
((WritableRegistry<CustomBlock>) BuiltInRegistries.BLOCK).registerForHolder(new ResourceKey<>(BuiltInRegistries.BLOCK.key().location(), id)));

View File

@@ -42,12 +42,12 @@ public class BukkitEntity extends AbstractEntity {
}
@Override
public float getXRot() {
public float xRot() {
return literalObject().getYaw();
}
@Override
public float getYRot() {
public float yRot() {
return literalObject().getPitch();
}

View File

@@ -0,0 +1,74 @@
package net.momirealms.craftengine.bukkit.entity.furniture;
import net.momirealms.craftengine.core.entity.furniture.AbstractCustomFurniture;
import net.momirealms.craftengine.core.entity.furniture.AnchorType;
import net.momirealms.craftengine.core.entity.furniture.CustomFurniture;
import net.momirealms.craftengine.core.entity.furniture.FurnitureSettings;
import net.momirealms.craftengine.core.loot.LootTable;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
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.List;
import java.util.Map;
public class BukkitCustomFurniture extends AbstractCustomFurniture {
protected BukkitCustomFurniture(@NotNull Key id,
@NotNull FurnitureSettings settings,
@NotNull Map<AnchorType, Placement> placements,
@NotNull Map<EventTrigger, List<Function<PlayerOptionalContext>>> events,
@Nullable LootTable<?> lootTable) {
super(id, settings, placements, events, lootTable);
}
public static Builder builder() {
return new BuilderImpl();
}
public static class BuilderImpl implements Builder {
private Key id;
private Map<AnchorType, Placement> placements;
private FurnitureSettings settings;
private Map<EventTrigger, List<Function<PlayerOptionalContext>>> events;
private LootTable<?> lootTable;
@Override
public CustomFurniture build() {
return new BukkitCustomFurniture(id, settings, placements, events, lootTable);
}
@Override
public Builder id(Key id) {
this.id = id;
return this;
}
@Override
public Builder placement(Map<AnchorType, Placement> placements) {
this.placements = placements;
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<PlayerOptionalContext>>> events) {
this.events = events;
return this;
}
}
}

View File

@@ -1,19 +1,19 @@
package net.momirealms.craftengine.bukkit.entity.furniture;
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import net.momirealms.craftengine.bukkit.entity.BukkitEntity;
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.LocationUtils;
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 net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.craftengine.core.world.collision.AABB;
import org.bukkit.Location;
@@ -29,7 +29,7 @@ import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.*;
public class LoadedFurniture implements Furniture {
public class BukkitFurniture implements Furniture {
private final Key id;
private final CustomFurniture furniture;
private final AnchorType anchorType;
@@ -44,8 +44,8 @@ public class LoadedFurniture implements Furniture {
// cache
private final List<Integer> fakeEntityIds;
private final List<Integer> entityIds;
private final Map<Integer, HitBox> hitBoxes = new HashMap<>();
private final Map<Integer, AABB> aabb = new HashMap<>();
private final Map<Integer, HitBox> hitBoxes = new Int2ObjectArrayMap<>();
private final Map<Integer, AABB> aabb = new Int2ObjectArrayMap<>();
private final boolean minimized;
private final boolean hasExternalModel;
// seats
@@ -55,7 +55,7 @@ public class LoadedFurniture implements Furniture {
private Object cachedSpawnPacket;
private Object cachedMinimizedSpawnPacket;
public LoadedFurniture(Entity baseEntity,
public BukkitFurniture(Entity baseEntity,
CustomFurniture furniture,
FurnitureExtraData extraData) {
this.id = furniture.id();
@@ -66,8 +66,8 @@ public class LoadedFurniture implements Furniture {
this.baseEntity = new WeakReference<>(baseEntity);
this.furniture = furniture;
this.minimized = furniture.settings().minimized();
List<Integer> fakeEntityIds = new ArrayList<>();
List<Integer> mainEntityIds = new ArrayList<>();
List<Integer> fakeEntityIds = new IntArrayList();
List<Integer> mainEntityIds = new IntArrayList();
mainEntityIds.add(this.baseEntityId);
CustomFurniture.Placement placement = furniture.getPlacement(anchorType);
@@ -84,19 +84,11 @@ public class LoadedFurniture implements Furniture {
this.hasExternalModel = false;
}
float yaw = this.location.getYaw();
Quaternionf conjugated = QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - yaw), 0).conjugate();
double x = location.getX();
double y = location.getY();
double z = location.getZ();
Quaternionf conjugated = QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - this.location.getYaw()), 0).conjugate();
List<Object> packets = new ArrayList<>();
List<Object> minimizedPackets = new ArrayList<>();
List<Collider> colliders = new ArrayList<>();
World world = world();
WorldPosition position = new WorldPosition(world, x, y, z, yaw, 0);
WorldPosition position = position();
Integer dyedColor = this.extraData.dyedColor().orElse(null);
for (FurnitureElement element : placement.elements()) {
int entityId = Reflections.instance$Entity$ENTITY_COUNTER.incrementAndGet();
@@ -154,13 +146,8 @@ public class LoadedFurniture implements Furniture {
}
@Override
public Vec3d position() {
return new Vec3d(location.getX(), location.getY(), location.getZ());
}
@Override
public World world() {
return new BukkitWorld(this.location.getWorld());
public WorldPosition position() {
return LocationUtils.toWorldPosition(this.location);
}
@NotNull
@@ -170,7 +157,7 @@ public class LoadedFurniture implements Furniture {
@NotNull
public Entity baseEntity() {
Entity entity = baseEntity.get();
Entity entity = this.baseEntity.get();
if (entity == null) {
throw new RuntimeException("Base entity not found. It might be unloaded.");
}

View File

@@ -7,6 +7,7 @@ import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.entity.Billboard;
import net.momirealms.craftengine.core.entity.ItemDisplayContext;
import net.momirealms.craftengine.core.entity.furniture.AbstractFurnitureElement;
import net.momirealms.craftengine.core.entity.furniture.FurnitureElement;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.Key;
@@ -30,10 +31,10 @@ public class BukkitFurnitureElement extends AbstractFurnitureElement {
ItemDisplayContext transform,
Vector3f scale,
Vector3f translation,
Vector3f offset,
Vector3f position,
Quaternionf rotation,
boolean applyDyedColor) {
super(item, billboard, transform, scale, translation, offset, rotation, applyDyedColor);
super(item, billboard, transform, scale, translation, position, rotation, applyDyedColor);
this.commonValues = new ArrayList<>();
ItemDisplayEntityData.Scale.addEntityDataIfNotDefaultValue(scale(), this.commonValues);
ItemDisplayEntityData.RotationLeft.addEntityDataIfNotDefaultValue(rotation(), this.commonValues);
@@ -67,4 +68,72 @@ public class BukkitFurnitureElement extends AbstractFurnitureElement {
ItemDisplayEntityData.DisplayedItem.addEntityDataIfNotDefaultValue(item.getLiteralObject(), cachedValues);
return cachedValues;
}
public static Builder builder() {
return new BuilderImpl();
}
public static class BuilderImpl implements Builder {
private boolean applyDyedColor;
private Key item;
private Billboard billboard;
private ItemDisplayContext transform;
private Vector3f scale;
private Vector3f translation;
private Vector3f position;
private Quaternionf rotation;
@Override
public Builder applyDyedColor(boolean applyDyedColor) {
this.applyDyedColor = applyDyedColor;
return this;
}
@Override
public Builder item(Key item) {
this.item = item;
return this;
}
@Override
public Builder billboard(Billboard billboard) {
this.billboard = billboard;
return this;
}
@Override
public Builder transform(ItemDisplayContext transform) {
this.transform = transform;
return this;
}
@Override
public Builder scale(Vector3f scale) {
this.scale = scale;
return this;
}
@Override
public Builder translation(Vector3f translation) {
this.translation = translation;
return this;
}
@Override
public Builder position(Vector3f position) {
this.position = position;
return this;
}
@Override
public Builder rotation(Quaternionf rotation) {
this.rotation = rotation;
return this;
}
@Override
public FurnitureElement build() {
return new BukkitFurnitureElement(item, billboard, transform, scale, translation, position, rotation, applyDyedColor);
}
}
}

View File

@@ -6,58 +6,45 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.network.handler.FurniturePacketHandler;
import net.momirealms.craftengine.bukkit.util.EntityUtils;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.entity.Billboard;
import net.momirealms.craftengine.core.entity.ItemDisplayContext;
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.Config;
import net.momirealms.craftengine.core.plugin.config.ConfigParser;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.context.function.Function;
import net.momirealms.craftengine.core.plugin.event.EventFunctions;
import net.momirealms.craftengine.core.plugin.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
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.ResourceConfigUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.WorldPosition;
import org.bukkit.*;
import org.bukkit.entity.*;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector3f;
import javax.annotation.Nullable;
import java.io.IOException;
import java.nio.file.Path;
import java.util.*;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
public class BukkitFurnitureManager extends AbstractFurnitureManager {
public static final NamespacedKey FURNITURE_KEY = Objects.requireNonNull(NamespacedKey.fromString("craftengine:furniture_id"));
// DEPRECATED
// public static final NamespacedKey FURNITURE_ANCHOR_KEY = Objects.requireNonNull(NamespacedKey.fromString("craftengine:anchor_type"));
public static final NamespacedKey FURNITURE_EXTRA_DATA_KEY = Objects.requireNonNull(NamespacedKey.fromString("craftengine:furniture_extra_data"));
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"));
public static final NamespacedKey FURNITURE_COLLISION = Objects.requireNonNull(NamespacedKey.fromString("craftengine:collision"));
public static final NamespacedKey FURNITURE_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_KEY);
public static final NamespacedKey FURNITURE_EXTRA_DATA_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_EXTRA_DATA_KEY);
public static final NamespacedKey FURNITURE_SEAT_BASE_ENTITY_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY);
public static final NamespacedKey FURNITURE_SEAT_VECTOR_3F_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY);
public static final NamespacedKey FURNITURE_COLLISION = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_COLLISION);
public static Class<?> COLLISION_ENTITY_CLASS = Interaction.class;
public static Object NMS_COLLISION_ENTITY_TYPE = Reflections.instance$EntityType$INTERACTION;
public static ColliderType COLLISION_ENTITY_TYPE = ColliderType.INTERACTION;
private static BukkitFurnitureManager instance;
private final BukkitCraftEngine plugin;
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);
private final Map<Integer, BukkitFurniture> furnitureByRealEntityId = new ConcurrentHashMap<>(256, 0.5f);
private final Map<Integer, BukkitFurniture> furnitureByEntityId = new ConcurrentHashMap<>(512, 0.5f);
// Event listeners
private final Listener dismountListener;
private final FurnitureEventListener furnitureEventListener;
@@ -67,9 +54,9 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
}
public BukkitFurnitureManager(BukkitCraftEngine plugin) {
super(plugin);
instance = this;
this.plugin = plugin;
this.furnitureParser = new FurnitureParser();
this.furnitureEventListener = new FurnitureEventListener(this);
this.dismountListener = VersionHelper.isOrAbove1_20_3() ? new DismountListener1_20_3(this) : new DismountListener1_20(this::handleDismount);
}
@@ -79,7 +66,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
return this.place(LocationUtils.toLocation(position), furniture, extraData, playSound);
}
public LoadedFurniture place(Location location, CustomFurniture furniture, FurnitureExtraData extraData, boolean playSound) {
public BukkitFurniture place(Location location, CustomFurniture furniture, FurnitureExtraData extraData, boolean playSound) {
Optional<AnchorType> optionalAnchorType = extraData.anchorType();
if (optionalAnchorType.isEmpty() || !furniture.isAllowedPlacement(optionalAnchorType.get())) {
extraData.anchorType(furniture.getAnyPlacement());
@@ -101,128 +88,6 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
return loadedFurnitureByRealEntityId(furnitureEntity.getEntityId());
}
@Override
public ConfigParser parser() {
return this.furnitureParser;
}
public class FurnitureParser implements ConfigParser {
public static final String[] CONFIG_SECTION_NAME = new String[] { "furniture" };
@Override
public String[] sectionId() {
return CONFIG_SECTION_NAME;
}
@Override
public int loadingSequence() {
return LoadingSequence.FURNITURE;
}
@SuppressWarnings("unchecked")
@Override
public void parseSection(Pack pack, Path path, Key id, Map<String, Object> section) {
if (byId.containsKey(id)) {
throw new LocalizedResourceConfigException("warning.config.furniture.duplicate", path, id);
}
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);
if (placementMap == null) {
throw new LocalizedResourceConfigException("warning.config.furniture.missing_placement", path, id);
}
EnumMap<AnchorType, CustomFurniture.Placement> placements = new EnumMap<>(AnchorType.class);
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);
Optional<Vector3f> optionalLootSpawnOffset = Optional.ofNullable(placementArguments.get("loot-spawn-offset")).map(it -> MiscUtils.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) {
String key = (String) element.get("item");
if (key == null) {
throw new LocalizedResourceConfigException("warning.config.furniture.element.missing_item", path, 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.getAsVector3f(element.getOrDefault("scale", "1"), "scale"),
MiscUtils.getAsVector3f(element.getOrDefault("translation", "0"), "translation"),
MiscUtils.getAsVector3f(element.getOrDefault("position", "0"), "position"),
MiscUtils.getAsQuaternionf(element.getOrDefault("rotation", "0"), "rotation"),
(boolean) element.getOrDefault("apply-dyed-color", true)
);
elements.add(furnitureElement);
}
// external model providers
Optional<ExternalModel> externalModel;
if (placementArguments.containsKey("model-engine")) {
externalModel = Optional.of(plugin.compatibilityManager().createModelEngineModel(placementArguments.get("model-engine").toString()));
} else if (placementArguments.containsKey("better-model")) {
externalModel = Optional.of(plugin.compatibilityManager().createBetterModelModel(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);
}
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]),
rotationRule,
alignmentRule,
externalModel,
optionalLootSpawnOffset
));
} else {
placements.put(anchorType, new CustomFurniture.Placement(
elements.toArray(new FurnitureElement[0]),
hitboxes.toArray(new HitBox[0]),
RotationRule.ANY,
AlignmentRule.CENTER,
externalModel,
optionalLootSpawnOffset
));
}
}
// get furniture settings
FurnitureSettings settings = FurnitureSettings.fromMap(settingsMap);
// get loot table
LootTable<ItemStack> lootTable = lootMap == null ? null : LootTable.fromMap(lootMap);
EnumMap<EventTrigger, List<Function<PlayerOptionalContext>>> events = EventFunctions.parseEvents(ResourceConfigUtils.get(section, "events", "event"));
CustomFurniture furniture = new CustomFurniture(id, settings, placements, events, lootTable);
byId.put(id, furniture);
}
}
@Override
public void delayedInit() {
COLLISION_ENTITY_CLASS = Config.colliderType() == ColliderType.INTERACTION ? Interaction.class : Boat.class;
@@ -264,19 +129,29 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
@Nullable
@Override
public LoadedFurniture loadedFurnitureByRealEntityId(int entityId) {
public BukkitFurniture loadedFurnitureByRealEntityId(int entityId) {
return this.furnitureByRealEntityId.get(entityId);
}
@Override
@Nullable
public LoadedFurniture loadedFurnitureByEntityId(int entityId) {
public BukkitFurniture loadedFurnitureByEntityId(int entityId) {
return this.furnitureByEntityId.get(entityId);
}
@Override
protected CustomFurniture.Builder furnitureBuilder() {
return BukkitCustomFurniture.builder();
}
@Override
protected FurnitureElement.Builder furnitureElementBuilder() {
return BukkitFurnitureElement.builder();
}
protected void handleBaseEntityUnload(Entity entity) {
int id = entity.getEntityId();
LoadedFurniture furniture = this.furnitureByRealEntityId.remove(id);
BukkitFurniture furniture = this.furnitureByRealEntityId.remove(id);
if (furniture != null) {
Location location = entity.getLocation();
boolean isPreventing = FastNMS.INSTANCE.isPreventingStatusUpdates(location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4);
@@ -305,7 +180,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
if (optionalFurniture.isEmpty()) return;
CustomFurniture customFurniture = optionalFurniture.get();
LoadedFurniture previous = this.furnitureByRealEntityId.get(display.getEntityId());
BukkitFurniture previous = this.furnitureByRealEntityId.get(display.getEntityId());
if (previous != null) return;
Location location = display.getLocation();
@@ -313,7 +188,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
boolean preventChange = FastNMS.INSTANCE.isPreventingStatusUpdates(location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4);
if (above1_20_1) {
if (!preventChange) {
LoadedFurniture furniture = addNewFurniture(display, customFurniture);
BukkitFurniture furniture = addNewFurniture(display, customFurniture);
furniture.initializeColliders();
for (Player player : display.getTrackedPlayers()) {
this.plugin.adapt(player).entityPacketHandlers().computeIfAbsent(furniture.baseEntityId(), k -> new FurniturePacketHandler(furniture.fakeEntityIds()));
@@ -321,7 +196,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
}
}
} else {
LoadedFurniture furniture = addNewFurniture(display, customFurniture);
BukkitFurniture furniture = addNewFurniture(display, customFurniture);
for (Player player : display.getTrackedPlayers()) {
this.plugin.adapt(player).entityPacketHandlers().computeIfAbsent(furniture.baseEntityId(), k -> new FurniturePacketHandler(furniture.fakeEntityIds()));
this.plugin.networkManager().sendPacket(player, furniture.spawnPacket(player));
@@ -383,9 +258,9 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
Optional<CustomFurniture> optionalFurniture = furnitureById(key);
if (optionalFurniture.isPresent()) {
CustomFurniture customFurniture = optionalFurniture.get();
LoadedFurniture previous = this.furnitureByRealEntityId.get(display.getEntityId());
BukkitFurniture previous = this.furnitureByRealEntityId.get(display.getEntityId());
if (previous != null) return;
LoadedFurniture furniture = addNewFurniture(display, customFurniture);
BukkitFurniture furniture = addNewFurniture(display, customFurniture);
furniture.initializeColliders(); // safely do it here
}
}
@@ -412,23 +287,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
return FurnitureExtraData.fromBytes(extraData);
}
// private AnchorType getAnchorType(Entity baseEntity, CustomFurniture furniture) {
// String anchorType = baseEntity.getPersistentDataContainer().get(FURNITURE_ANCHOR_KEY, PersistentDataType.STRING);
// if (anchorType != null) {
// try {
// AnchorType unverified = AnchorType.valueOf(anchorType);
// if (furniture.isAllowedPlacement(unverified)) {
// return unverified;
// }
// } catch (IllegalArgumentException ignored) {
// }
// }
// AnchorType anchorTypeEnum = furniture.getAnyPlacement();
// baseEntity.getPersistentDataContainer().set(FURNITURE_ANCHOR_KEY, PersistentDataType.STRING, anchorTypeEnum.name());
// return anchorTypeEnum;
// }
private synchronized LoadedFurniture addNewFurniture(ItemDisplay display, CustomFurniture furniture) {
private synchronized BukkitFurniture addNewFurniture(ItemDisplay display, CustomFurniture furniture) {
FurnitureExtraData extraData;
try {
extraData = getFurnitureExtraData(display);
@@ -436,16 +295,21 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
extraData = FurnitureExtraData.builder().build();
plugin.logger().warn("Furniture extra data could not be loaded", e);
}
LoadedFurniture loadedFurniture = new LoadedFurniture(display, furniture, extraData);
this.furnitureByRealEntityId.put(loadedFurniture.baseEntityId(), loadedFurniture);
for (int entityId : loadedFurniture.entityIds()) {
this.furnitureByEntityId.put(entityId, loadedFurniture);
BukkitFurniture bukkitFurniture = new BukkitFurniture(display, furniture, extraData);
this.furnitureByRealEntityId.put(bukkitFurniture.baseEntityId(), bukkitFurniture);
for (int entityId : bukkitFurniture.entityIds()) {
this.furnitureByEntityId.put(entityId, bukkitFurniture);
}
for (Collider collisionEntity : loadedFurniture.collisionEntities()) {
for (Collider collisionEntity : bukkitFurniture.collisionEntities()) {
int collisionEntityId = FastNMS.INSTANCE.method$Entity$getId(collisionEntity.handle());
this.furnitureByRealEntityId.put(collisionEntityId, loadedFurniture);
this.furnitureByRealEntityId.put(collisionEntityId, bukkitFurniture);
}
return loadedFurniture;
return bukkitFurniture;
}
@Override
protected HitBox defaultHitBox() {
return InteractionHitBox.DEFAULT;
}
protected void handleDismount(Player player, Entity entity) {
@@ -458,7 +322,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
Integer baseFurniture = vehicle.getPersistentDataContainer().get(FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER);
if (baseFurniture == null) return;
vehicle.remove();
LoadedFurniture furniture = loadedFurnitureByRealEntityId(baseFurniture);
BukkitFurniture furniture = loadedFurnitureByRealEntityId(baseFurniture);
if (furniture == null) {
return;
}

View File

@@ -25,8 +25,8 @@ import org.bukkit.event.entity.ProjectileLaunchEvent;
import org.bukkit.event.player.PlayerItemConsumeEvent;
import org.bukkit.event.world.EntitiesLoadEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nullable;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

View File

@@ -6,14 +6,17 @@ import net.momirealms.craftengine.core.item.*;
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.modifier.ItemDataModifier;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.context.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.context.function.Function;
import net.momirealms.craftengine.core.plugin.event.EventTrigger;
import net.momirealms.craftengine.core.registry.Holder;
import net.momirealms.craftengine.core.util.Key;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import java.util.*;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
private final Material material;
@@ -22,7 +25,7 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
List<ItemBehavior> behaviors,
List<ItemDataModifier<ItemStack>> modifiers, List<ItemDataModifier<ItemStack>> clientBoundModifiers,
ItemSettings settings,
EnumMap<EventTrigger, List<Function<PlayerOptionalContext>>> events) {
Map<EventTrigger, List<Function<PlayerOptionalContext>>> events) {
super(id, materialKey, behaviors, modifiers, clientBoundModifiers, settings, events);
this.material = material;
}
@@ -56,7 +59,7 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
private Holder<Key> id;
private Key materialKey;
private final Material material;
private final EnumMap<EventTrigger, List<Function<PlayerOptionalContext>>> events = new EnumMap<>(EventTrigger.class);
private final Map<EventTrigger, List<Function<PlayerOptionalContext>>> events = new EnumMap<>(EventTrigger.class);
private final List<ItemBehavior> behaviors = new ArrayList<>(4);
private final List<ItemDataModifier<ItemStack>> modifiers = new ArrayList<>(4);
private final List<ItemDataModifier<ItemStack>> clientBoundModifiers = new ArrayList<>(4);
@@ -122,7 +125,7 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
}
@Override
public Builder<ItemStack> events(EnumMap<EventTrigger, List<Function<PlayerOptionalContext>>> events) {
public Builder<ItemStack> events(Map<EventTrigger, List<Function<PlayerOptionalContext>>> events) {
this.events.putAll(events);
return this;
}

View File

@@ -36,7 +36,6 @@ import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;

View File

@@ -98,6 +98,6 @@ public class ComponentItemWrapper implements ItemWrapper<ItemStack> {
@Override
public void count(int amount) {
this.item.setAmount(amount);
this.item.setAmount(Math.max(amount, 0));
}
}

View File

@@ -60,7 +60,7 @@ public class LegacyItemWrapper implements ItemWrapper<ItemStack> {
@Override
public ItemStack load() {
ItemStack itemStack = this.rtagItem.load();
itemStack.setAmount(this.count);
itemStack.setAmount(Math.max(this.count, 0));
return itemStack;
}

View File

@@ -25,8 +25,8 @@ import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.context.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.plugin.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.util.Cancellable;
import net.momirealms.craftengine.core.util.Direction;

View File

@@ -2,8 +2,8 @@ package net.momirealms.craftengine.bukkit.item.behavior;
import net.momirealms.craftengine.bukkit.api.event.FurnitureAttemptPlaceEvent;
import net.momirealms.craftengine.bukkit.api.event.FurniturePlaceEvent;
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture;
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager;
import net.momirealms.craftengine.bukkit.entity.furniture.LoadedFurniture;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
@@ -23,8 +23,8 @@ import net.momirealms.craftengine.core.pack.Pack;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.context.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.plugin.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
import net.momirealms.craftengine.core.util.*;
import net.momirealms.craftengine.core.world.Vec3d;
@@ -105,7 +105,7 @@ public class FurnitureItemBehavior extends ItemBehavior {
finalPlacePosition = new Vec3d(xz.left(), xz.right(), clickedPosition.z());
}
} else {
furnitureYaw = placement.rotationRule().apply(180 + player.getXRot());
furnitureYaw = placement.rotationRule().apply(180 + player.xRot());
Pair<Double, Double> xz = placement.alignmentRule().apply(Pair.of(clickedPosition.x(), clickedPosition.z()));
finalPlacePosition = new Vec3d(xz.left(), clickedPosition.y(), xz.right());
}
@@ -134,7 +134,7 @@ public class FurnitureItemBehavior extends ItemBehavior {
Item<?> item = context.getItem();
LoadedFurniture loadedFurniture = BukkitFurnitureManager.instance().place(
BukkitFurniture bukkitFurniture = BukkitFurnitureManager.instance().place(
furnitureLocation.clone(), customFurniture,
FurnitureExtraData.builder()
.item(item.copyWithCount(1))
@@ -142,15 +142,15 @@ public class FurnitureItemBehavior extends ItemBehavior {
.dyedColor(item.dyedColor().orElse(null))
.build(), false);
FurniturePlaceEvent placeEvent = new FurniturePlaceEvent(bukkitPlayer, loadedFurniture, furnitureLocation, context.getHand());
FurniturePlaceEvent placeEvent = new FurniturePlaceEvent(bukkitPlayer, bukkitFurniture, furnitureLocation, context.getHand());
if (EventUtils.fireAndCheckCancel(placeEvent)) {
loadedFurniture.destroy();
bukkitFurniture.destroy();
return InteractionResult.FAIL;
}
Cancellable dummy = Cancellable.dummy();
PlayerOptionalContext functionContext = PlayerOptionalContext.of(player, ContextHolder.builder()
.withParameter(DirectContextParameters.FURNITURE, loadedFurniture)
.withParameter(DirectContextParameters.FURNITURE, bukkitFurniture)
.withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(furnitureLocation))
.withParameter(DirectContextParameters.EVENT, dummy)
.withParameter(DirectContextParameters.HAND, context.getHand())

View File

@@ -25,8 +25,8 @@ import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

View File

@@ -16,8 +16,8 @@ import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.context.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.plugin.event.EventTrigger;
import net.momirealms.craftengine.core.util.Cancellable;
import net.momirealms.craftengine.core.util.Direction;
import net.momirealms.craftengine.core.world.BlockHitResult;
@@ -121,7 +121,14 @@ public class ItemEventListener implements Listener {
// interact block with items
if (hasItem && action == Action.RIGHT_CLICK_BLOCK) {
Location interactionPoint = Objects.requireNonNull(event.getInteractionPoint(), "interaction point should not be null");
Location interactionPoint = event.getInteractionPoint();
// some plugins would trigger this event without interaction point
if (interactionPoint == null) {
if (hasCustomItem) {
event.setCancelled(true);
}
return;
}
Direction direction = DirectionUtils.toDirection(event.getBlockFace());
BlockPos pos = LocationUtils.toBlockPos(block.getLocation());
Vec3d vec3d = new Vec3d(interactionPoint.getX(), interactionPoint.getY(), interactionPoint.getZ());

View File

@@ -1,8 +1,7 @@
package net.momirealms.craftengine.bukkit.plugin.command;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
@@ -18,11 +17,9 @@ import org.bukkit.entity.Player;
import java.util.UUID;
public class BukkitSenderFactory extends SenderFactory<BukkitCraftEngine, CommandSender> {
private final BukkitAudiences audiences;
public BukkitSenderFactory(BukkitCraftEngine plugin) {
super(plugin);
this.audiences = BukkitAudiences.create(plugin.bootstrap());
}
@Override
@@ -41,20 +38,18 @@ public class BukkitSenderFactory extends SenderFactory<BukkitCraftEngine, Comman
return Sender.CONSOLE_UUID;
}
@Override
public Audience audience(CommandSender sender) {
return this.audiences.sender(sender);
}
@Override
protected void sendMessage(CommandSender sender, Component message) {
// we can safely send async for players and the console - otherwise, send it sync
if (sender instanceof Player player) {
FastNMS.INSTANCE.sendPacket(FastNMS.INSTANCE.method$CraftPlayer$getHandle(player), FastNMS.INSTANCE.constructor$ClientboundSystemChatPacket(ComponentUtils.adventureToMinecraft(message), false));
} else if (sender instanceof ConsoleCommandSender || sender instanceof RemoteConsoleCommandSender) {
audience(sender).sendMessage(message);
} else if (sender instanceof ConsoleCommandSender commandSender) {
commandSender.sendMessage(LegacyComponentSerializer.legacySection().serialize(message));
} else if (sender instanceof RemoteConsoleCommandSender commandSender) {
commandSender.sendMessage(LegacyComponentSerializer.legacySection().serialize(message));
} else {
plugin().scheduler().executeSync(() -> audience(sender).sendMessage(message));
String legacy = LegacyComponentSerializer.legacySection().serialize(message);
plugin().scheduler().sync().run(() -> sender.sendMessage(legacy));
}
}
@@ -93,6 +88,5 @@ public class BukkitSenderFactory extends SenderFactory<BukkitCraftEngine, Comman
@Override
public void close() {
super.close();
this.audiences.close();
}
}

View File

@@ -51,7 +51,7 @@ public class DebugAppearanceStateUsageCommand extends BukkitCommandFeature<Comma
for (int appearance : appearances) {
Component text = Component.text("|");
List<Integer> reals = blockManager.appearanceToRealStates(appearance);
if (reals == null) {
if (reals == null || reals.isEmpty()) {
Component hover = Component.text(baseBlockId.value() + ":" + i).color(NamedTextColor.GREEN);
hover = hover.append(Component.newline()).append(Component.text(BlockStateUtils.fromBlockData(BlockStateUtils.idToBlockState(appearance)).getAsString()).color(NamedTextColor.GREEN));
text = text.color(NamedTextColor.GREEN).hoverEvent(HoverEvent.showText(hover));

View File

@@ -3,16 +3,17 @@ package net.momirealms.craftengine.bukkit.plugin.command.feature;
import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.item.ComponentTypes;
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
import net.momirealms.craftengine.bukkit.util.MaterialUtils;
import net.momirealms.craftengine.bukkit.util.PlayerUtils;
import net.momirealms.craftengine.core.item.*;
import net.momirealms.craftengine.core.item.CustomItem;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import net.momirealms.craftengine.core.item.ItemKeys;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
import net.momirealms.craftengine.core.plugin.command.FlagKeys;
import net.momirealms.craftengine.core.plugin.locale.MessageConstants;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.VersionHelper;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@@ -26,7 +27,6 @@ import org.incendo.cloud.bukkit.parser.selector.MultiplePlayerSelectorParser;
import org.incendo.cloud.context.CommandContext;
import org.incendo.cloud.context.CommandInput;
import org.incendo.cloud.parser.flag.CommandFlag;
import org.incendo.cloud.parser.standard.StringParser;
import org.incendo.cloud.suggestion.Suggestion;
import org.incendo.cloud.suggestion.SuggestionProvider;

View File

@@ -10,8 +10,8 @@ import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture;
import net.momirealms.craftengine.bukkit.api.event.FurnitureBreakEvent;
import net.momirealms.craftengine.bukkit.api.event.FurnitureInteractEvent;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture;
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager;
import net.momirealms.craftengine.bukkit.entity.furniture.LoadedFurniture;
import net.momirealms.craftengine.bukkit.entity.projectile.BukkitProjectileManager;
import net.momirealms.craftengine.bukkit.item.behavior.FurnitureItemBehavior;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
@@ -35,11 +35,14 @@ import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.context.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
import net.momirealms.craftengine.core.plugin.event.EventTrigger;
import net.momirealms.craftengine.core.plugin.network.*;
import net.momirealms.craftengine.core.util.*;
import net.momirealms.craftengine.core.world.*;
import net.momirealms.craftengine.core.world.BlockHitResult;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.EntityHitResult;
import net.momirealms.craftengine.core.world.WorldEvents;
import net.momirealms.craftengine.core.world.chunk.Palette;
import net.momirealms.craftengine.core.world.chunk.PalettedContainer;
import net.momirealms.craftengine.core.world.chunk.packet.BlockEntityData;
@@ -47,7 +50,6 @@ import net.momirealms.craftengine.core.world.chunk.packet.MCSection;
import net.momirealms.craftengine.core.world.collision.AABB;
import net.momirealms.sparrow.nbt.Tag;
import org.bukkit.*;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
@@ -1489,7 +1491,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().loadedFurnitureByEntityId(entityId);
BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByEntityId(entityId);
if (furniture == null) return;
Player player = (Player) user.platformPlayer();
if (player == null) return;
@@ -1518,7 +1520,7 @@ public class PacketConsumers {
}
};
private static void handlePickItemFromEntityOnMainThread(Player player, LoadedFurniture furniture) throws Exception {
private static void handlePickItemFromEntityOnMainThread(Player player, BukkitFurniture furniture) throws Exception {
Key itemId = furniture.config().settings().itemId();
if (itemId == null) return;
pickItem(player, itemId, null, FastNMS.INSTANCE.method$CraftEntity$getHandle(furniture.baseEntity()));
@@ -1596,7 +1598,7 @@ public class PacketConsumers {
int entityId = FastNMS.INSTANCE.field$ClientboundAddEntityPacket$entityId(packet);
if (entityType == Reflections.instance$EntityType$ITEM_DISPLAY) {
// Furniture
LoadedFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entityId);
BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entityId);
if (furniture != null) {
Player player = (Player) user.platformPlayer();
List<Integer> fakeEntityIds = furniture.fakeEntityIds();
@@ -1631,7 +1633,7 @@ public class PacketConsumers {
}
} else if (entityType == BukkitFurnitureManager.NMS_COLLISION_ENTITY_TYPE) {
// Cancel collider entity packet
LoadedFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entityId);
BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entityId);
if (furniture != null) {
event.setCancelled(true);
user.entityPacketHandlers().put(entityId, FurnitureCollisionPacketHandler.INSTANCE);
@@ -1709,7 +1711,7 @@ public class PacketConsumers {
} else {
entityId = FastNMS.INSTANCE.field$ServerboundInteractPacket$entityId(packet);
}
LoadedFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByEntityId(entityId);
BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByEntityId(entityId);
if (furniture == null) return;
Object action = Reflections.field$ServerboundInteractPacket$action.get(packet);
Object actionType = Reflections.method$ServerboundInteractPacket$Action$getType.invoke(action);
@@ -1733,7 +1735,7 @@ public class PacketConsumers {
// execute functions
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
.withParameter(DirectContextParameters.FURNITURE, furniture)
.withParameter(DirectContextParameters.POSITION, new WorldPosition(furniture.world(), furniture.position()))
.withParameter(DirectContextParameters.POSITION, furniture.position())
);
furniture.config().execute(context, EventTrigger.LEFT_CLICK);
furniture.config().execute(context, EventTrigger.BREAK);
@@ -1763,7 +1765,7 @@ public class PacketConsumers {
// execute functions
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
.withParameter(DirectContextParameters.FURNITURE, furniture)
.withParameter(DirectContextParameters.POSITION, new WorldPosition(furniture.world(), furniture.position()))
.withParameter(DirectContextParameters.POSITION, furniture.position())
);
furniture.config().execute(context, EventTrigger.RIGHT_CLICK);

View File

@@ -13,8 +13,8 @@ import net.momirealms.craftengine.bukkit.plugin.gui.CraftEngineInventoryHolder;
import net.momirealms.craftengine.bukkit.util.*;
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
import net.momirealms.craftengine.core.block.BlockSettings;
import net.momirealms.craftengine.core.block.BlockStateWrapper;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.PackedBlockState;
import net.momirealms.craftengine.core.entity.player.GameMode;
import net.momirealms.craftengine.core.entity.player.InteractionHand;
import net.momirealms.craftengine.core.entity.player.Player;
@@ -433,7 +433,7 @@ public class BukkitServerPlayer extends Player {
// instant break
boolean custom = immutableBlockState != null;
if (custom && getDestroyProgress(state, pos) >= 1f) {
PackedBlockState vanillaBlockState = immutableBlockState.vanillaBlockState();
BlockStateWrapper vanillaBlockState = immutableBlockState.vanillaBlockState();
// if it's not an instant break on client side, we should resend level event
if (vanillaBlockState != null && getDestroyProgress(vanillaBlockState.handle(), pos) < 1f) {
Object levelEventPacket = FastNMS.INSTANCE.constructor$ClientboundLevelEventPacket(
@@ -641,6 +641,11 @@ public class BukkitServerPlayer extends Player {
}
}
@Override
public void breakBlock(int x, int y, int z) {
platformPlayer().breakBlock(new Location(platformPlayer().getWorld(), x, y, z).getBlock());
}
private void broadcastDestroyProgress(org.bukkit.entity.Player player, BlockPos hitPos, Object blockPos, int stage) {
Object packet = FastNMS.INSTANCE.constructor$ClientboundBlockDestructionPacket(Integer.MAX_VALUE - entityID(), blockPos, stage);
for (org.bukkit.entity.Player other : player.getWorld().getPlayers()) {
@@ -698,12 +703,12 @@ public class BukkitServerPlayer extends Player {
}
@Override
public float getYRot() {
public float yRot() {
return platformPlayer().getPitch();
}
@Override
public float getXRot() {
public float xRot() {
return platformPlayer().getYaw();
}
@@ -878,4 +883,24 @@ public class BukkitServerPlayer extends Player {
public boolean isFlying() {
return platformPlayer().isFlying();
}
@Override
public int foodLevel() {
return platformPlayer().getFoodLevel();
}
@Override
public void setFoodLevel(int foodLevel) {
this.platformPlayer().setFoodLevel(Math.min(Math.max(0, foodLevel), 20));
}
@Override
public float saturation() {
return platformPlayer().getSaturation();
}
@Override
public void setSaturation(float saturation) {
this.platformPlayer().setSaturation(saturation);
}
}

View File

@@ -33,6 +33,12 @@ public class BlockStateUtils {
hasInit = true;
}
public static BlockStateWrapper toPackedBlockState(BlockData blockData) {
Object state = blockDataToBlockState(blockData);
int id = blockStateToId(state);
return BlockStateWrapper.create(state, id, isVanillaBlock(id));
}
public static boolean isCorrectTool(@NotNull ImmutableBlockState state, @Nullable Item<ItemStack> itemInHand) {
BlockSettings settings = state.settings();
if (settings.requireCorrectTool()) {
@@ -101,10 +107,10 @@ public class BlockStateUtils {
}
public static Key getBlockOwnerId(Block block) {
return getBlockOwnerId(block.getBlockData());
return getBlockOwnerIdFromData(block.getBlockData());
}
public static Key getBlockOwnerId(BlockData block) {
public static Key getBlockOwnerIdFromData(BlockData block) {
Object blockState = blockDataToBlockState(block);
return getBlockOwnerIdFromState(blockState);
}

View File

@@ -13,6 +13,10 @@ public class ComponentUtils {
return jsonElementToMinecraft(AdventureHelper.componentToJsonElement(component));
}
public static Object adventureToPaperAdventure(Component component) {
return jsonElementToPaperAdventure(AdventureHelper.componentToJsonElement(component));
}
public static Object jsonElementToMinecraft(JsonElement json) {
return FastNMS.INSTANCE.method$Component$Serializer$fromJson(json);
}

View File

@@ -274,7 +274,7 @@ public class InteractUtils {
}
public static boolean isInteractable(Player player, BlockData state, BlockHitResult hit, Item<ItemStack> item) {
Key blockType = BlockStateUtils.getBlockOwnerId(state);
Key blockType = BlockStateUtils.getBlockOwnerIdFromData(state);
if (INTERACTIONS.containsKey(blockType)) {
return INTERACTIONS.get(blockType).apply(player, item, state, hit);
} else {

View File

@@ -4,8 +4,8 @@ import net.momirealms.craftengine.core.util.Key;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nullable;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;

View File

@@ -1,7 +1,9 @@
package net.momirealms.craftengine.bukkit.util;
import net.momirealms.craftengine.core.block.BlockSounds;
import net.momirealms.craftengine.core.sound.SoundSource;
import net.momirealms.craftengine.core.util.Key;
import org.bukkit.SoundCategory;
public class SoundUtils {
@@ -21,4 +23,19 @@ public class SoundUtils {
public static Object getOrRegisterSoundEvent(Key key) throws ReflectiveOperationException {
return Reflections.method$SoundEvent$createVariableRangeEvent.invoke(null, KeyUtils.toResourceLocation(key));
}
public static SoundCategory toBukkit(SoundSource source) {
return switch (source) {
case BLOCK -> SoundCategory.BLOCKS;
case MUSIC -> SoundCategory.MUSIC;
case VOICE -> SoundCategory.VOICE;
case MASTER -> SoundCategory.MASTER;
case PLAYER -> SoundCategory.PLAYERS;
case RECORD -> SoundCategory.RECORDS;
case AMBIENT -> SoundCategory.AMBIENT;
case HOSTILE -> SoundCategory.HOSTILE;
case NEUTRAL -> SoundCategory.NEUTRAL;
case WEATHER -> SoundCategory.WEATHER;
};
}
}

View File

@@ -3,7 +3,10 @@ package net.momirealms.craftengine.bukkit.world;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.util.EntityUtils;
import net.momirealms.craftengine.bukkit.util.ItemUtils;
import net.momirealms.craftengine.bukkit.util.SoundUtils;
import net.momirealms.craftengine.core.block.BlockStateWrapper;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.sound.SoundSource;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.BlockInWorld;
@@ -86,6 +89,11 @@ public class BukkitWorld implements World {
});
}
@Override
public void playSound(Position location, Key sound, float volume, float pitch, SoundSource source) {
platformWorld().playSound(new Location(null, location.x(), location.y(), location.z()), sound.toString(), SoundUtils.toBukkit(source), volume, pitch);
}
@Override
public void playBlockSound(Position location, Key sound, float volume, float pitch) {
platformWorld().playSound(new Location(null, location.x(), location.y(), location.z()), sound.toString(), SoundCategory.BLOCKS, volume, pitch);
@@ -95,4 +103,11 @@ public class BukkitWorld implements World {
public long time() {
return platformWorld().getTime();
}
@Override
public void setBlockAt(int x, int y, int z, BlockStateWrapper blockState, int flags) {
Object worldServer = serverWorld();
Object blockPos = FastNMS.INSTANCE.constructor$BlockPos(x, y, z);
FastNMS.INSTANCE.method$LevelWriter$setBlock(worldServer, blockPos, blockState.handle(), flags);
}
}