mirror of
https://github.com/Xiao-MoMi/craft-engine.git
synced 2025-12-20 15:39:22 +00:00
@@ -75,7 +75,7 @@ repositories {
|
|||||||
```
|
```
|
||||||
```kotlin
|
```kotlin
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly("net.momirealms:craft-engine-core:0.0.65")
|
compileOnly("net.momirealms:craft-engine-core:0.0.66")
|
||||||
compileOnly("net.momirealms:craft-engine-bukkit:0.0.65")
|
compileOnly("net.momirealms:craft-engine-bukkit:0.0.66")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -18,6 +18,7 @@ repositories {
|
|||||||
maven("https://jitpack.io") // sxitem slimefun
|
maven("https://jitpack.io") // sxitem slimefun
|
||||||
maven("https://repo.codemc.io/repository/maven-public/") // quickshop
|
maven("https://repo.codemc.io/repository/maven-public/") // quickshop
|
||||||
maven("https://repo.nexomc.com/releases/") // nexo
|
maven("https://repo.nexomc.com/releases/") // nexo
|
||||||
|
maven("https://repo.opencollab.dev/main/") // geyser
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -41,8 +42,8 @@ dependencies {
|
|||||||
compileOnly("io.github.toxicity188:bettermodel:1.14.0")
|
compileOnly("io.github.toxicity188:bettermodel:1.14.0")
|
||||||
compileOnly("com.mojang:authlib:${rootProject.properties["authlib_version"]}")
|
compileOnly("com.mojang:authlib:${rootProject.properties["authlib_version"]}")
|
||||||
// MMOItems
|
// MMOItems
|
||||||
compileOnly("net.Indyuce:MMOItems-API:6.10-SNAPSHOT")
|
compileOnly("net.Indyuce:MMOItems-API:6.10.1-SNAPSHOT")
|
||||||
compileOnly("io.lumine:MythicLib-dist:1.6.2-SNAPSHOT")
|
compileOnly("io.lumine:MythicLib-dist:1.7.1-SNAPSHOT")
|
||||||
// Nexo
|
// Nexo
|
||||||
compileOnly("com.nexomc:nexo:1.13.0")
|
compileOnly("com.nexomc:nexo:1.13.0")
|
||||||
// LuckPerms
|
// LuckPerms
|
||||||
@@ -62,7 +63,7 @@ dependencies {
|
|||||||
// McMMO
|
// McMMO
|
||||||
compileOnly("com.gmail.nossr50.mcMMO:mcMMO:2.2.038")
|
compileOnly("com.gmail.nossr50.mcMMO:mcMMO:2.2.038")
|
||||||
// MMOCore
|
// MMOCore
|
||||||
compileOnly("net.Indyuce:MMOCore-API:1.12.1-SNAPSHOT")
|
compileOnly("net.Indyuce:MMOCore-API:1.13.1-SNAPSHOT")
|
||||||
// JobsReborn
|
// JobsReborn
|
||||||
compileOnly("com.github.Zrips:Jobs:v5.2.2.3")
|
compileOnly("com.github.Zrips:Jobs:v5.2.2.3")
|
||||||
// CustomFishing
|
// CustomFishing
|
||||||
@@ -88,6 +89,10 @@ dependencies {
|
|||||||
compileOnly("io.github.Slimefun:Slimefun4:RC-32")
|
compileOnly("io.github.Slimefun:Slimefun4:RC-32")
|
||||||
// QuickShop
|
// QuickShop
|
||||||
compileOnly("com.ghostchu:quickshop-api:6.2.0.10")
|
compileOnly("com.ghostchu:quickshop-api:6.2.0.10")
|
||||||
|
// Geyser
|
||||||
|
compileOnly("org.geysermc.geyser:api:2.9.0-SNAPSHOT")
|
||||||
|
// Floodgate
|
||||||
|
compileOnly("org.geysermc.floodgate:api:2.2.4-SNAPSHOT")
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package net.momirealms.craftengine.bukkit.compatibility;
|
|||||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||||
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
||||||
import net.momirealms.craftengine.bukkit.block.entity.renderer.element.BukkitBlockEntityElementConfigs;
|
import net.momirealms.craftengine.bukkit.block.entity.renderer.element.BukkitBlockEntityElementConfigs;
|
||||||
|
import net.momirealms.craftengine.bukkit.compatibility.bedrock.FloodgateUtils;
|
||||||
|
import net.momirealms.craftengine.bukkit.compatibility.bedrock.GeyserUtils;
|
||||||
import net.momirealms.craftengine.bukkit.compatibility.item.*;
|
import net.momirealms.craftengine.bukkit.compatibility.item.*;
|
||||||
import net.momirealms.craftengine.bukkit.compatibility.legacy.slimeworld.LegacySlimeFormatStorageAdaptor;
|
import net.momirealms.craftengine.bukkit.compatibility.legacy.slimeworld.LegacySlimeFormatStorageAdaptor;
|
||||||
import net.momirealms.craftengine.bukkit.compatibility.leveler.*;
|
import net.momirealms.craftengine.bukkit.compatibility.leveler.*;
|
||||||
@@ -19,6 +21,7 @@ import net.momirealms.craftengine.bukkit.compatibility.quickshop.QuickShopItemEx
|
|||||||
import net.momirealms.craftengine.bukkit.compatibility.region.WorldGuardRegionCondition;
|
import net.momirealms.craftengine.bukkit.compatibility.region.WorldGuardRegionCondition;
|
||||||
import net.momirealms.craftengine.bukkit.compatibility.skript.SkriptHook;
|
import net.momirealms.craftengine.bukkit.compatibility.skript.SkriptHook;
|
||||||
import net.momirealms.craftengine.bukkit.compatibility.slimeworld.SlimeFormatStorageAdaptor;
|
import net.momirealms.craftengine.bukkit.compatibility.slimeworld.SlimeFormatStorageAdaptor;
|
||||||
|
import net.momirealms.craftengine.bukkit.compatibility.tag.CustomNameplateHatSettings;
|
||||||
import net.momirealms.craftengine.bukkit.compatibility.tag.CustomNameplateProviders;
|
import net.momirealms.craftengine.bukkit.compatibility.tag.CustomNameplateProviders;
|
||||||
import net.momirealms.craftengine.bukkit.compatibility.viaversion.ViaVersionUtils;
|
import net.momirealms.craftengine.bukkit.compatibility.viaversion.ViaVersionUtils;
|
||||||
import net.momirealms.craftengine.bukkit.compatibility.worldedit.WorldEditBlockRegister;
|
import net.momirealms.craftengine.bukkit.compatibility.worldedit.WorldEditBlockRegister;
|
||||||
@@ -37,6 +40,7 @@ import net.momirealms.craftengine.core.plugin.config.Config;
|
|||||||
import net.momirealms.craftengine.core.plugin.context.Context;
|
import net.momirealms.craftengine.core.plugin.context.Context;
|
||||||
import net.momirealms.craftengine.core.plugin.context.condition.AlwaysFalseCondition;
|
import net.momirealms.craftengine.core.plugin.context.condition.AlwaysFalseCondition;
|
||||||
import net.momirealms.craftengine.core.plugin.context.event.EventConditions;
|
import net.momirealms.craftengine.core.plugin.context.event.EventConditions;
|
||||||
|
import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
|
||||||
import net.momirealms.craftengine.core.plugin.text.minimessage.FormattedLine;
|
import net.momirealms.craftengine.core.plugin.text.minimessage.FormattedLine;
|
||||||
import net.momirealms.craftengine.core.util.Key;
|
import net.momirealms.craftengine.core.util.Key;
|
||||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||||
@@ -53,6 +57,8 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
|
|||||||
private final Map<String, TagResolverProvider> tagResolverProviders;
|
private final Map<String, TagResolverProvider> tagResolverProviders;
|
||||||
private TagResolverProvider[] tagResolverProviderArray = null;
|
private TagResolverProvider[] tagResolverProviderArray = null;
|
||||||
private boolean hasPlaceholderAPI;
|
private boolean hasPlaceholderAPI;
|
||||||
|
private boolean hasGeyser;
|
||||||
|
private boolean hasFloodgate;
|
||||||
|
|
||||||
public BukkitCompatibilityManager(BukkitCraftEngine plugin) {
|
public BukkitCompatibilityManager(BukkitCraftEngine plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
@@ -96,6 +102,7 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
|
|||||||
registerTagResolverProvider(new CustomNameplateProviders.Background());
|
registerTagResolverProvider(new CustomNameplateProviders.Background());
|
||||||
registerTagResolverProvider(new CustomNameplateProviders.Nameplate());
|
registerTagResolverProvider(new CustomNameplateProviders.Nameplate());
|
||||||
registerTagResolverProvider(new CustomNameplateProviders.Bubble());
|
registerTagResolverProvider(new CustomNameplateProviders.Bubble());
|
||||||
|
new CustomNameplateHatSettings().register();
|
||||||
logHook("CustomNameplates");
|
logHook("CustomNameplates");
|
||||||
}
|
}
|
||||||
Key worldGuardRegion = Key.of("worldguard:region");
|
Key worldGuardRegion = Key.of("worldguard:region");
|
||||||
@@ -107,6 +114,12 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
|
|||||||
EventConditions.register(worldGuardRegion, new AlwaysFalseCondition.FactoryImpl<>());
|
EventConditions.register(worldGuardRegion, new AlwaysFalseCondition.FactoryImpl<>());
|
||||||
LootConditions.register(worldGuardRegion, new AlwaysFalseCondition.FactoryImpl<>());
|
LootConditions.register(worldGuardRegion, new AlwaysFalseCondition.FactoryImpl<>());
|
||||||
}
|
}
|
||||||
|
if (this.isPluginEnabled("Geyser-Spigot")) {
|
||||||
|
this.hasGeyser = true;
|
||||||
|
}
|
||||||
|
if (this.isPluginEnabled("floodgate")) {
|
||||||
|
this.hasFloodgate = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -181,7 +194,7 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void logHook(String plugin) {
|
private void logHook(String plugin) {
|
||||||
this.plugin.logger().info("[Compatibility] " + plugin + " hooked");
|
this.plugin.logger().info(TranslationManager.instance().translateLog("info.compatibility", plugin));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -252,8 +265,8 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
|
|||||||
if (VersionHelper.isOrAbove1_20_3()) {
|
if (VersionHelper.isOrAbove1_20_3()) {
|
||||||
this.plugin.logger().severe("");
|
this.plugin.logger().severe("");
|
||||||
if (Locale.getDefault() == Locale.SIMPLIFIED_CHINESE) {
|
if (Locale.getDefault() == Locale.SIMPLIFIED_CHINESE) {
|
||||||
this.plugin.logger().severe("[Compatibility] 插件需要更新 FastAsyncWorldEdit 到 2.13.0 或更高版本,以获得更好的兼容性。(当前版本: " + version + ")");
|
this.plugin.logger().severe("[兼容性] 插件需要更新 FastAsyncWorldEdit 到 2.13.0 或更高版本,以获得更好的兼容性。(当前版本: " + version + ")");
|
||||||
this.plugin.logger().severe("[Compatibility] 请前往 https://ci.athion.net/job/FastAsyncWorldEdit/ 下载最新版本");
|
this.plugin.logger().severe("[兼容性] 请前往 https://ci.athion.net/job/FastAsyncWorldEdit/ 下载最新版本");
|
||||||
} else {
|
} else {
|
||||||
this.plugin.logger().severe("[Compatibility] Update FastAsyncWorldEdit to v2.13.0+ for better compatibility (Current: " + version + ")");
|
this.plugin.logger().severe("[Compatibility] Update FastAsyncWorldEdit to v2.13.0+ for better compatibility (Current: " + version + ")");
|
||||||
this.plugin.logger().severe("[Compatibility] Download latest version: https://ci.athion.net/job/FastAsyncWorldEdit/");
|
this.plugin.logger().severe("[Compatibility] Download latest version: https://ci.athion.net/job/FastAsyncWorldEdit/");
|
||||||
@@ -365,4 +378,16 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
|
|||||||
}
|
}
|
||||||
return resolvers;
|
return resolvers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isBedrockPlayer(Player player) {
|
||||||
|
UUID uuid = player.uuid();
|
||||||
|
if (this.hasFloodgate) {
|
||||||
|
return FloodgateUtils.isFloodgatePlayer(uuid);
|
||||||
|
}
|
||||||
|
if (this.hasGeyser) {
|
||||||
|
return GeyserUtils.isGeyserPlayer(uuid);
|
||||||
|
}
|
||||||
|
return uuid.version() == 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.compatibility.bedrock;
|
||||||
|
|
||||||
|
import org.geysermc.floodgate.api.FloodgateApi;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public final class FloodgateUtils {
|
||||||
|
|
||||||
|
private FloodgateUtils() {}
|
||||||
|
|
||||||
|
public static boolean isFloodgatePlayer(UUID uuid) {
|
||||||
|
return FloodgateApi.getInstance().isFloodgatePlayer(uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.compatibility.bedrock;
|
||||||
|
|
||||||
|
import org.geysermc.api.Geyser;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public final class GeyserUtils {
|
||||||
|
|
||||||
|
private GeyserUtils() {}
|
||||||
|
|
||||||
|
public static boolean isGeyserPlayer(UUID uuid) {
|
||||||
|
return Geyser.api().isBedrockPlayer(uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,7 +25,9 @@ public class MMOItemsSource implements ExternalItemSource<ItemStack> {
|
|||||||
split = split[0].split("_", 2);
|
split = split[0].split("_", 2);
|
||||||
}
|
}
|
||||||
if (split.length == 1) return new ItemStack(Material.AIR);
|
if (split.length == 1) return new ItemStack(Material.AIR);
|
||||||
MMOItem mmoItem = MMOItems.plugin.getMMOItem(Type.get(split[0]), split[1].toUpperCase());
|
// 这里与使用和mmoitems相同的转换id方法
|
||||||
|
String mmoItemId = split[1].toUpperCase().replace("-", "_").replace(" ", "_");
|
||||||
|
MMOItem mmoItem = MMOItems.plugin.getMMOItem(Type.get(split[0]), mmoItemId);
|
||||||
return mmoItem == null ? new ItemStack(Material.AIR) : requireNonNull(mmoItem.newBuilder().build());
|
return mmoItem == null ? new ItemStack(Material.AIR) : requireNonNull(mmoItem.newBuilder().build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package net.momirealms.craftengine.bukkit.compatibility.model.bettermodel;
|
package net.momirealms.craftengine.bukkit.compatibility.model.bettermodel;
|
||||||
|
|
||||||
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement;
|
|
||||||
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig;
|
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig;
|
||||||
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory;
|
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory;
|
||||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||||
@@ -59,13 +58,12 @@ public class BetterModelBlockEntityElementConfig implements BlockEntityElementCo
|
|||||||
return BetterModelBlockEntityElement.class;
|
return BetterModelBlockEntityElement.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Factory implements BlockEntityElementConfigFactory {
|
public static class Factory implements BlockEntityElementConfigFactory<BetterModelBlockEntityElement> {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
@Override
|
||||||
public <E extends BlockEntityElement> BlockEntityElementConfig<E> create(Map<String, Object> arguments) {
|
public BetterModelBlockEntityElementConfig create(Map<String, Object> arguments) {
|
||||||
String model = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("model"), "warning.config.block.state.entity_renderer.better_model.missing_model");
|
String model = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("model"), "warning.config.block.state.entity_renderer.better_model.missing_model");
|
||||||
return (BlockEntityElementConfig<E>) new BetterModelBlockEntityElementConfig(
|
return new BetterModelBlockEntityElementConfig(
|
||||||
model,
|
model,
|
||||||
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"),
|
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"),
|
||||||
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"),
|
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"),
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package net.momirealms.craftengine.bukkit.compatibility.model.modelengine;
|
package net.momirealms.craftengine.bukkit.compatibility.model.modelengine;
|
||||||
|
|
||||||
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement;
|
|
||||||
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig;
|
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig;
|
||||||
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory;
|
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory;
|
||||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||||
@@ -49,13 +48,12 @@ public class ModelEngineBlockEntityElementConfig implements BlockEntityElementCo
|
|||||||
return ModelEngineBlockEntityElement.class;
|
return ModelEngineBlockEntityElement.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Factory implements BlockEntityElementConfigFactory {
|
public static class Factory implements BlockEntityElementConfigFactory<ModelEngineBlockEntityElement> {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
@Override
|
||||||
public <E extends BlockEntityElement> BlockEntityElementConfig<E> create(Map<String, Object> arguments) {
|
public ModelEngineBlockEntityElementConfig create(Map<String, Object> arguments) {
|
||||||
String model = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("model"), "warning.config.block.state.entity_renderer.model_engine.missing_model");
|
String model = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("model"), "warning.config.block.state.entity_renderer.model_engine.missing_model");
|
||||||
return (BlockEntityElementConfig<E>) new ModelEngineBlockEntityElementConfig(
|
return new ModelEngineBlockEntityElementConfig(
|
||||||
model,
|
model,
|
||||||
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"),
|
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"),
|
||||||
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"),
|
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"),
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ public class MythicItemDrop extends ItemDrop implements IItemDrop {
|
|||||||
context = ItemBuildContext.of(player);
|
context = ItemBuildContext.of(player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int amountInt = MiscUtils.fastFloor(amount + 0.5F);
|
int amountInt = MiscUtils.floor(amount + 0.5F);
|
||||||
ItemStack itemStack = this.customItem.buildItemStack(context, amountInt);
|
ItemStack itemStack = this.customItem.buildItemStack(context, amountInt);
|
||||||
return adapt(itemStack).amount(amountInt);
|
return adapt(itemStack).amount(amountInt);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
package net.momirealms.craftengine.bukkit.compatibility.papi;
|
package net.momirealms.craftengine.bukkit.compatibility.papi;
|
||||||
|
|
||||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
|
||||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||||
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||||
import net.momirealms.craftengine.bukkit.util.ItemStackUtils;
|
|
||||||
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
||||||
import net.momirealms.craftengine.core.item.Item;
|
import net.momirealms.craftengine.core.item.Item;
|
||||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||||
@@ -13,8 +11,6 @@ import org.bukkit.entity.Player;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
public class CheckItemExpansion extends PlaceholderExpansion {
|
public class CheckItemExpansion extends PlaceholderExpansion {
|
||||||
private final CraftEngine plugin;
|
private final CraftEngine plugin;
|
||||||
|
|
||||||
@@ -108,11 +104,6 @@ public class CheckItemExpansion extends PlaceholderExpansion {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int getItemCount(BukkitServerPlayer player, String[] param) {
|
private int getItemCount(BukkitServerPlayer player, String[] param) {
|
||||||
Key itemId = Key.of(param[0], param[1]);
|
return player.clearOrCountMatchingInventoryItems(Key.of(param[0], param[1]), 0);
|
||||||
Predicate<Object> predicate = nmsStack -> this.plugin.itemManager().wrap(ItemStackUtils.asCraftMirror(nmsStack)).id().equals(itemId);
|
|
||||||
Object inventory = FastNMS.INSTANCE.method$Player$getInventory(player.serverPlayer());
|
|
||||||
Object inventoryMenu = FastNMS.INSTANCE.field$Player$inventoryMenu(player.serverPlayer());
|
|
||||||
Object craftSlots = FastNMS.INSTANCE.method$InventoryMenu$getCraftSlots(inventoryMenu);
|
|
||||||
return FastNMS.INSTANCE.method$Inventory$clearOrCountMatchingItems(inventory, predicate, 0, craftSlots);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ public class CondIsFurniture extends Condition {
|
|||||||
@Override
|
@Override
|
||||||
public boolean check(Event event) {
|
public boolean check(Event event) {
|
||||||
return entities.check(event, entity -> {
|
return entities.check(event, entity -> {
|
||||||
BukkitFurniture baseEntity = CraftEngineFurniture.getLoadedFurnitureByBaseEntity(entity);
|
BukkitFurniture baseEntity = CraftEngineFurniture.getLoadedFurnitureByMetaEntity(entity);
|
||||||
return baseEntity != null;
|
return baseEntity != null;
|
||||||
}, isNegated());
|
}, isNegated());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public class EffRemoveFurniture extends Effect {
|
|||||||
@Override
|
@Override
|
||||||
protected void execute(Event e) {
|
protected void execute(Event e) {
|
||||||
for (Entity entity : entities.getArray(e)) {
|
for (Entity entity : entities.getArray(e)) {
|
||||||
Furniture bukkitFurniture = CraftEngineFurniture.getLoadedFurnitureByBaseEntity(entity);
|
Furniture bukkitFurniture = CraftEngineFurniture.getLoadedFurnitureByMetaEntity(entity);
|
||||||
if (bukkitFurniture != null) {
|
if (bukkitFurniture != null) {
|
||||||
bukkitFurniture.destroy();
|
bukkitFurniture.destroy();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ public class EvtCustomClick extends SkriptEvent {
|
|||||||
EventValues.registerEventValue(FurnitureInteractEvent.class, Location.class, FurnitureInteractEvent::location, EventValues.TIME_NOW);
|
EventValues.registerEventValue(FurnitureInteractEvent.class, Location.class, FurnitureInteractEvent::location, EventValues.TIME_NOW);
|
||||||
EventValues.registerEventValue(FurnitureInteractEvent.class, Player.class, FurnitureInteractEvent::player, EventValues.TIME_NOW);
|
EventValues.registerEventValue(FurnitureInteractEvent.class, Player.class, FurnitureInteractEvent::player, EventValues.TIME_NOW);
|
||||||
EventValues.registerEventValue(CustomBlockInteractEvent.class, Block.class, event -> null, EventValues.TIME_NOW);
|
EventValues.registerEventValue(CustomBlockInteractEvent.class, Block.class, event -> null, EventValues.TIME_NOW);
|
||||||
EventValues.registerEventValue(FurnitureInteractEvent.class, Entity.class, event -> event.furniture().baseEntity(), EventValues.TIME_NOW);
|
EventValues.registerEventValue(FurnitureInteractEvent.class, Entity.class, event -> event.furniture().getBukkitEntity(), EventValues.TIME_NOW);
|
||||||
EventValues.registerEventValue(FurnitureInteractEvent.class, World.class, event -> event.location().getWorld(), EventValues.TIME_NOW);
|
EventValues.registerEventValue(FurnitureInteractEvent.class, World.class, event -> event.location().getWorld(), EventValues.TIME_NOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,14 +30,14 @@ public class EvtCustomFurniture extends SkriptEvent {
|
|||||||
.description("Called when a furniture is broken by a player.");
|
.description("Called when a furniture is broken by a player.");
|
||||||
EventValues.registerEventValue(FurnitureBreakEvent.class, Location.class, FurnitureBreakEvent::location, EventValues.TIME_NOW);
|
EventValues.registerEventValue(FurnitureBreakEvent.class, Location.class, FurnitureBreakEvent::location, EventValues.TIME_NOW);
|
||||||
EventValues.registerEventValue(FurnitureBreakEvent.class, Player.class, FurnitureBreakEvent::player, EventValues.TIME_NOW);
|
EventValues.registerEventValue(FurnitureBreakEvent.class, Player.class, FurnitureBreakEvent::player, EventValues.TIME_NOW);
|
||||||
EventValues.registerEventValue(FurnitureBreakEvent.class, Entity.class, event -> event.furniture().baseEntity(), EventValues.TIME_NOW);
|
EventValues.registerEventValue(FurnitureBreakEvent.class, Entity.class, event -> event.furniture().getBukkitEntity(), EventValues.TIME_NOW);
|
||||||
EventValues.registerEventValue(FurnitureBreakEvent.class, World.class, event -> event.location().getWorld(), EventValues.TIME_NOW);
|
EventValues.registerEventValue(FurnitureBreakEvent.class, World.class, event -> event.location().getWorld(), EventValues.TIME_NOW);
|
||||||
|
|
||||||
Skript.registerEvent("Place Furniture", EvtCustomFurniture.class, FurniturePlaceEvent.class, "(plac(e|ing)|build[ing]) of [(custom|ce|craft-engine)] furniture[s] [[of] %-strings%]")
|
Skript.registerEvent("Place Furniture", EvtCustomFurniture.class, FurniturePlaceEvent.class, "(plac(e|ing)|build[ing]) of [(custom|ce|craft-engine)] furniture[s] [[of] %-strings%]")
|
||||||
.description("Called when a player places a furniture.");
|
.description("Called when a player places a furniture.");
|
||||||
EventValues.registerEventValue(FurniturePlaceEvent.class, Location.class, FurniturePlaceEvent::location, EventValues.TIME_NOW);
|
EventValues.registerEventValue(FurniturePlaceEvent.class, Location.class, FurniturePlaceEvent::location, EventValues.TIME_NOW);
|
||||||
EventValues.registerEventValue(FurniturePlaceEvent.class, Player.class, FurniturePlaceEvent::player, EventValues.TIME_NOW);
|
EventValues.registerEventValue(FurniturePlaceEvent.class, Player.class, FurniturePlaceEvent::player, EventValues.TIME_NOW);
|
||||||
EventValues.registerEventValue(FurniturePlaceEvent.class, Entity.class, event -> event.furniture().baseEntity(), EventValues.TIME_NOW);
|
EventValues.registerEventValue(FurniturePlaceEvent.class, Entity.class, event -> event.furniture().getBukkitEntity(), EventValues.TIME_NOW);
|
||||||
EventValues.registerEventValue(FurniturePlaceEvent.class, World.class, event -> event.location().getWorld(), EventValues.TIME_NOW);
|
EventValues.registerEventValue(FurniturePlaceEvent.class, World.class, event -> event.location().getWorld(), EventValues.TIME_NOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public class ExprEntityFurnitureID extends SimplePropertyExpression<Object, Stri
|
|||||||
@Override
|
@Override
|
||||||
public @Nullable String convert(Object object) {
|
public @Nullable String convert(Object object) {
|
||||||
if (object instanceof Entity entity) {
|
if (object instanceof Entity entity) {
|
||||||
return Optional.ofNullable(CraftEngineFurniture.getLoadedFurnitureByBaseEntity(entity))
|
return Optional.ofNullable(CraftEngineFurniture.getLoadedFurnitureByMetaEntity(entity))
|
||||||
.map(it -> it.id().toString())
|
.map(it -> it.id().toString())
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.compatibility.tag;
|
||||||
|
|
||||||
|
import io.papermc.paper.event.entity.EntityEquipmentChangedEvent;
|
||||||
|
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||||
|
import net.momirealms.craftengine.core.item.CustomItem;
|
||||||
|
import net.momirealms.craftengine.core.item.Item;
|
||||||
|
import net.momirealms.craftengine.core.item.ItemSettings;
|
||||||
|
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||||
|
import net.momirealms.craftengine.core.util.CustomDataType;
|
||||||
|
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||||
|
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||||
|
import net.momirealms.customnameplates.api.CNPlayer;
|
||||||
|
import net.momirealms.customnameplates.api.CustomNameplates;
|
||||||
|
import net.momirealms.customnameplates.api.CustomNameplatesAPI;
|
||||||
|
import net.momirealms.customnameplates.api.feature.tag.TagRenderer;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
|
import org.bukkit.inventory.EquipmentSlot;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public final class CustomNameplateHatSettings implements Listener {
|
||||||
|
public static final CustomDataType<Double> HAT_HEIGHT = new CustomDataType<>();
|
||||||
|
|
||||||
|
public void register() {
|
||||||
|
ItemSettings.Modifiers.registerFactory("hat-height", height -> {
|
||||||
|
double heightD = ResourceConfigUtils.getAsDouble(height, "hat-height");
|
||||||
|
return settings -> settings.addCustomData(HAT_HEIGHT, heightD);
|
||||||
|
});
|
||||||
|
Bukkit.getPluginManager().registerEvents(this, BukkitCraftEngine.instance().javaPlugin());
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
||||||
|
public void onEquipmentChange(EntityEquipmentChangedEvent event) {
|
||||||
|
if (!(event.getEntity() instanceof Player player)) return;
|
||||||
|
Map<EquipmentSlot, EntityEquipmentChangedEvent.EquipmentChange> equipmentChanges = event.getEquipmentChanges();
|
||||||
|
EntityEquipmentChangedEvent.EquipmentChange equipmentChange = equipmentChanges.get(EquipmentSlot.HEAD);
|
||||||
|
if (equipmentChange == null) return;
|
||||||
|
ItemStack newItem = equipmentChange.newItem();
|
||||||
|
updateHatHeight(player, newItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.HIGHEST)
|
||||||
|
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
// 稍微延迟一下,可以等待背包同步插件的处理
|
||||||
|
if (VersionHelper.isFolia()) {
|
||||||
|
player.getScheduler().runDelayed(BukkitCraftEngine.instance().javaPlugin(), t1 -> {
|
||||||
|
if (player.isOnline()) {
|
||||||
|
updateHatHeight(player, player.getInventory().getItem(EquipmentSlot.HEAD));
|
||||||
|
}
|
||||||
|
}, null, 10);
|
||||||
|
} else {
|
||||||
|
CraftEngine.instance().scheduler().sync().runLater(() -> {
|
||||||
|
if (player.isOnline()) {
|
||||||
|
updateHatHeight(player, player.getInventory().getItem(EquipmentSlot.HEAD));
|
||||||
|
}
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateHatHeight(Player player, ItemStack newItem) {
|
||||||
|
CNPlayer cnPlayer = CustomNameplatesAPI.getInstance().getPlayer(player.getUniqueId());
|
||||||
|
if (cnPlayer == null) return;
|
||||||
|
TagRenderer tagRender = CustomNameplates.getInstance().getUnlimitedTagManager().getTagRender(cnPlayer);
|
||||||
|
if (tagRender == null) return;
|
||||||
|
Item<ItemStack> wrapped = BukkitItemManager.instance().wrap(newItem);
|
||||||
|
Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
|
||||||
|
if (optionalCustomItem.isEmpty()) {
|
||||||
|
tagRender.hatOffset(0d);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Double customHeight = optionalCustomItem.get().settings().getCustomData(HAT_HEIGHT);
|
||||||
|
tagRender.hatOffset(Objects.requireNonNullElse(customHeight, 0d));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ import net.momirealms.craftengine.core.util.ReflectionUtils;
|
|||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@@ -63,7 +64,7 @@ public class WorldEditBlockRegister {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!input.contains(":")) {
|
if (!input.contains(":")) {
|
||||||
String lowerSearch = input.toLowerCase();
|
String lowerSearch = input.toLowerCase(Locale.ROOT);
|
||||||
return Stream.concat(
|
return Stream.concat(
|
||||||
namespacesInUse.stream().filter(n -> n.startsWith(lowerSearch)).map(n -> n + ":"),
|
namespacesInUse.stream().filter(n -> n.startsWith(lowerSearch)).map(n -> n + ":"),
|
||||||
BlockStateParser.fillSuggestions(input).stream()
|
BlockStateParser.fillSuggestions(input).stream()
|
||||||
|
|||||||
@@ -77,6 +77,10 @@ paper {
|
|||||||
register("ViaVersion") { required = false }
|
register("ViaVersion") { required = false }
|
||||||
register("QuickShop-Hikari") { required = false }
|
register("QuickShop-Hikari") { required = false }
|
||||||
|
|
||||||
|
// Geyser
|
||||||
|
register("Geyser-Spigot") { required = false }
|
||||||
|
register("floodgate") { required = false }
|
||||||
|
|
||||||
// AdvancedSlimePaper
|
// AdvancedSlimePaper
|
||||||
register("SlimeWorldPlugin") { required = false }
|
register("SlimeWorldPlugin") { required = false }
|
||||||
register("SlimeWorldManager") { required = false }
|
register("SlimeWorldManager") { required = false }
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import net.momirealms.craftengine.core.plugin.config.ConfigParser;
|
|||||||
import net.momirealms.craftengine.core.plugin.config.IdSectionConfigParser;
|
import net.momirealms.craftengine.core.plugin.config.IdSectionConfigParser;
|
||||||
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
|
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
|
||||||
import net.momirealms.craftengine.core.util.Key;
|
import net.momirealms.craftengine.core.util.Key;
|
||||||
|
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@@ -98,8 +99,8 @@ public final class BukkitAdvancementManager extends AbstractAdvancementManager {
|
|||||||
NetworkReflections.constructor$ClientboundUpdateAdvancementsPacket.newInstance(false, Arrays.asList(advancement), new HashSet<>(), advancementsToGrant, true) :
|
NetworkReflections.constructor$ClientboundUpdateAdvancementsPacket.newInstance(false, Arrays.asList(advancement), new HashSet<>(), advancementsToGrant, true) :
|
||||||
NetworkReflections.constructor$ClientboundUpdateAdvancementsPacket.newInstance(false, Arrays.asList(advancement), new HashSet<>(), advancementsToGrant);
|
NetworkReflections.constructor$ClientboundUpdateAdvancementsPacket.newInstance(false, Arrays.asList(advancement), new HashSet<>(), advancementsToGrant);
|
||||||
Object removePacket = VersionHelper.isOrAbove1_21_5() ?
|
Object removePacket = VersionHelper.isOrAbove1_21_5() ?
|
||||||
NetworkReflections.constructor$ClientboundUpdateAdvancementsPacket.newInstance(false, new ArrayList<>(), new HashSet<>() {{add(resourceLocation);}}, new HashMap<>(), true) :
|
NetworkReflections.constructor$ClientboundUpdateAdvancementsPacket.newInstance(false, new ArrayList<>(), MiscUtils.init(new HashSet<>(), s -> s.add(resourceLocation)), new HashMap<>(), true) :
|
||||||
NetworkReflections.constructor$ClientboundUpdateAdvancementsPacket.newInstance(false, new ArrayList<>(), new HashSet<>() {{add(resourceLocation);}}, new HashMap<>());
|
NetworkReflections.constructor$ClientboundUpdateAdvancementsPacket.newInstance(false, new ArrayList<>(), MiscUtils.init(new HashSet<>(), s -> s.add(resourceLocation)), new HashMap<>());
|
||||||
player.sendPackets(List.of(grantPacket, removePacket), false);
|
player.sendPackets(List.of(grantPacket, removePacket), false);
|
||||||
} catch (ReflectiveOperationException e) {
|
} catch (ReflectiveOperationException e) {
|
||||||
this.plugin.logger().warn("Failed to send toast for player " + player.name(), e);
|
this.plugin.logger().warn("Failed to send toast for player " + player.name(), e);
|
||||||
@@ -119,6 +120,11 @@ public final class BukkitAdvancementManager extends AbstractAdvancementManager {
|
|||||||
return LoadingSequence.ADVANCEMENT;
|
return LoadingSequence.ADVANCEMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int count() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void parseSection(Pack pack, Path path, String node, Key id, Map<String, Object> section) {
|
public void parseSection(Pack pack, Path path, String node, Key id, Map<String, Object> section) {
|
||||||
if (advancements.containsKey(id)) {
|
if (advancements.containsKey(id)) {
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
package net.momirealms.craftengine.bukkit.api;
|
package net.momirealms.craftengine.bukkit.api;
|
||||||
|
|
||||||
import net.momirealms.craftengine.bukkit.entity.BukkitEntity;
|
import net.momirealms.craftengine.bukkit.entity.BukkitEntity;
|
||||||
|
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||||
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||||
import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock;
|
import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock;
|
||||||
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
|
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
|
||||||
|
import net.momirealms.craftengine.core.item.Item;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public final class BukkitAdaptors {
|
public final class BukkitAdaptors {
|
||||||
@@ -62,4 +65,15 @@ public final class BukkitAdaptors {
|
|||||||
public static BukkitExistingBlock adapt(@NotNull final Block block) {
|
public static BukkitExistingBlock adapt(@NotNull final Block block) {
|
||||||
return new BukkitExistingBlock(block);
|
return new BukkitExistingBlock(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapts a Bukkit ItemStack to a CraftEngine wrapped item
|
||||||
|
*
|
||||||
|
* @param item the Bukkit ItemStack to adapt, must not be null
|
||||||
|
* @return a non-null Item instance wrapping the provided item
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public static Item<ItemStack> adapt(@NotNull final ItemStack item) {
|
||||||
|
return BukkitItemManager.instance().wrap(item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,12 @@ import net.momirealms.craftengine.bukkit.entity.seat.BukkitSeatManager;
|
|||||||
import net.momirealms.craftengine.bukkit.nms.CollisionEntity;
|
import net.momirealms.craftengine.bukkit.nms.CollisionEntity;
|
||||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||||
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
|
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
|
||||||
import net.momirealms.craftengine.core.entity.furniture.AnchorType;
|
import net.momirealms.craftengine.core.entity.furniture.AnchorType;
|
||||||
import net.momirealms.craftengine.core.entity.furniture.CustomFurniture;
|
import net.momirealms.craftengine.core.entity.furniture.CustomFurniture;
|
||||||
import net.momirealms.craftengine.core.entity.furniture.Furniture;
|
import net.momirealms.craftengine.core.entity.furniture.Furniture;
|
||||||
import net.momirealms.craftengine.core.entity.furniture.FurnitureExtraData;
|
import net.momirealms.craftengine.core.entity.furniture.FurnitureDataAccessor;
|
||||||
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
||||||
import net.momirealms.craftengine.core.item.Item;
|
import net.momirealms.craftengine.core.item.Item;
|
||||||
import net.momirealms.craftengine.core.loot.LootTable;
|
import net.momirealms.craftengine.core.loot.LootTable;
|
||||||
@@ -20,11 +21,13 @@ import net.momirealms.craftengine.core.util.Key;
|
|||||||
import net.momirealms.craftengine.core.world.World;
|
import net.momirealms.craftengine.core.world.World;
|
||||||
import net.momirealms.craftengine.core.world.WorldPosition;
|
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||||
import net.momirealms.sparrow.nbt.CompoundTag;
|
import net.momirealms.sparrow.nbt.CompoundTag;
|
||||||
|
import org.bukkit.FluidCollisionMode;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.persistence.PersistentDataType;
|
import org.bukkit.persistence.PersistentDataType;
|
||||||
|
import org.bukkit.util.RayTraceResult;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@@ -60,6 +63,45 @@ public final class CraftEngineFurniture {
|
|||||||
return BukkitFurnitureManager.instance().furnitureById(id).orElse(null);
|
return BukkitFurnitureManager.instance().furnitureById(id).orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs ray tracing to find the furniture entity that the player is currently targeting
|
||||||
|
*
|
||||||
|
* @param player The player performing the ray trace
|
||||||
|
* @param maxDistance Maximum ray trace distance (in blocks)
|
||||||
|
* @return The furniture being targeted by the player, or null if no furniture is found
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static BukkitFurniture rayTrace(Player player, double maxDistance) {
|
||||||
|
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
|
||||||
|
Location eyeLocation = serverPlayer.getEyeLocation();
|
||||||
|
RayTraceResult result = player.getWorld().rayTrace(eyeLocation, eyeLocation.getDirection(), maxDistance, FluidCollisionMode.NEVER, true, 0d, CraftEngineFurniture::isCollisionEntity);
|
||||||
|
if (result == null)
|
||||||
|
return null;
|
||||||
|
Entity hitEntity = result.getHitEntity();
|
||||||
|
if (hitEntity == null)
|
||||||
|
return null;
|
||||||
|
return getLoadedFurnitureByCollider(hitEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs ray tracing to find the furniture entity that the player is currently targeting
|
||||||
|
*
|
||||||
|
* @param player The player performing the ray trace
|
||||||
|
* @return The furniture being targeted by the player, or null if no furniture is found
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static BukkitFurniture rayTrace(Player player) {
|
||||||
|
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
|
||||||
|
Location eyeLocation = serverPlayer.getEyeLocation();
|
||||||
|
RayTraceResult result = player.getWorld().rayTrace(eyeLocation, eyeLocation.getDirection(), serverPlayer.getCachedInteractionRange(), FluidCollisionMode.NEVER, true, 0d, CraftEngineFurniture::isCollisionEntity);
|
||||||
|
if (result == null)
|
||||||
|
return null;
|
||||||
|
Entity hitEntity = result.getHitEntity();
|
||||||
|
if (hitEntity == null)
|
||||||
|
return null;
|
||||||
|
return getLoadedFurnitureByCollider(hitEntity);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Places furniture at certain location
|
* Places furniture at certain location
|
||||||
*
|
*
|
||||||
@@ -71,7 +113,7 @@ public final class CraftEngineFurniture {
|
|||||||
public static BukkitFurniture place(Location location, Key furnitureId) {
|
public static BukkitFurniture place(Location location, Key furnitureId) {
|
||||||
CustomFurniture furniture = byId(furnitureId);
|
CustomFurniture furniture = byId(furnitureId);
|
||||||
if (furniture == null) return null;
|
if (furniture == null) return null;
|
||||||
return place(location, furnitureId, furniture.getAnyAnchorType());
|
return place(location, furniture, furniture.anyVariantName(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -83,10 +125,24 @@ public final class CraftEngineFurniture {
|
|||||||
* @return the loaded furniture
|
* @return the loaded furniture
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@Deprecated(since = "0.0.66", forRemoval = true)
|
||||||
public static BukkitFurniture place(Location location, Key furnitureId, AnchorType anchorType) {
|
public static BukkitFurniture place(Location location, Key furnitureId, AnchorType anchorType) {
|
||||||
|
return place(location, furnitureId, anchorType.variantName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Places furniture at certain location
|
||||||
|
*
|
||||||
|
* @param location location
|
||||||
|
* @param furnitureId furniture to place
|
||||||
|
* @param variant variant type
|
||||||
|
* @return the loaded furniture
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static BukkitFurniture place(Location location, Key furnitureId, String variant) {
|
||||||
CustomFurniture furniture = byId(furnitureId);
|
CustomFurniture furniture = byId(furnitureId);
|
||||||
if (furniture == null) return null;
|
if (furniture == null) return null;
|
||||||
return BukkitFurnitureManager.instance().place(location, furniture, FurnitureExtraData.builder().anchorType(anchorType).build(), true);
|
return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.ofVariant(variant), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -98,8 +154,9 @@ public final class CraftEngineFurniture {
|
|||||||
* @return the loaded furniture
|
* @return the loaded furniture
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
|
@Deprecated(since = "0.0.66", forRemoval = true)
|
||||||
public static BukkitFurniture place(Location location, CustomFurniture furniture, AnchorType anchorType) {
|
public static BukkitFurniture place(Location location, CustomFurniture furniture, AnchorType anchorType) {
|
||||||
return BukkitFurnitureManager.instance().place(location, furniture, FurnitureExtraData.builder().anchorType(anchorType).build(), true);
|
return place(location, furniture, anchorType.variantName(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -112,10 +169,27 @@ public final class CraftEngineFurniture {
|
|||||||
* @return the loaded furniture
|
* @return the loaded furniture
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@Deprecated(since = "0.0.66", forRemoval = true)
|
||||||
public static BukkitFurniture place(Location location, Key furnitureId, AnchorType anchorType, boolean playSound) {
|
public static BukkitFurniture place(Location location, Key furnitureId, AnchorType anchorType, boolean playSound) {
|
||||||
CustomFurniture furniture = byId(furnitureId);
|
CustomFurniture furniture = byId(furnitureId);
|
||||||
if (furniture == null) return null;
|
if (furniture == null) return null;
|
||||||
return BukkitFurnitureManager.instance().place(location, furniture, FurnitureExtraData.builder().anchorType(anchorType).build(), playSound);
|
return place(location, furniture, anchorType.variantName(), playSound);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Places furniture at certain location
|
||||||
|
*
|
||||||
|
* @param location location
|
||||||
|
* @param furnitureId furniture to place
|
||||||
|
* @param variant variant
|
||||||
|
* @param playSound whether to play place sounds
|
||||||
|
* @return the loaded furniture
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static BukkitFurniture place(Location location, Key furnitureId, String variant, boolean playSound) {
|
||||||
|
CustomFurniture furniture = byId(furnitureId);
|
||||||
|
if (furniture == null) return null;
|
||||||
|
return place(location, furniture, variant, playSound);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -128,8 +202,51 @@ public final class CraftEngineFurniture {
|
|||||||
* @return the loaded furniture
|
* @return the loaded furniture
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
|
@Deprecated(since = "0.0.66", forRemoval = true)
|
||||||
public static BukkitFurniture place(Location location, CustomFurniture furniture, AnchorType anchorType, boolean playSound) {
|
public static BukkitFurniture place(Location location, CustomFurniture furniture, AnchorType anchorType, boolean playSound) {
|
||||||
return BukkitFurnitureManager.instance().place(location, furniture, FurnitureExtraData.builder().anchorType(anchorType).build(), playSound);
|
return place(location, furniture, anchorType.variantName(), playSound);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Places furniture at certain location
|
||||||
|
*
|
||||||
|
* @param location location
|
||||||
|
* @param furniture furniture to place
|
||||||
|
* @param variant variant
|
||||||
|
* @param playSound whether to play place sounds
|
||||||
|
* @return the loaded furniture
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public static BukkitFurniture place(Location location, CustomFurniture furniture, String variant, boolean playSound) {
|
||||||
|
return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.ofVariant(variant), playSound);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Places furniture at certain location
|
||||||
|
*
|
||||||
|
* @param location location
|
||||||
|
* @param furniture furniture to place
|
||||||
|
* @param data furniture data
|
||||||
|
* @param playSound whether to play place sounds
|
||||||
|
* @return the loaded furniture
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public static BukkitFurniture place(Location location, CustomFurniture furniture, CompoundTag data, boolean playSound) {
|
||||||
|
return BukkitFurnitureManager.instance().place(location, furniture, FurnitureDataAccessor.of(data), playSound);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Places furniture at certain location
|
||||||
|
*
|
||||||
|
* @param location location
|
||||||
|
* @param furniture furniture to place
|
||||||
|
* @param dataAccessor furniture data accessor
|
||||||
|
* @param playSound whether to play place sounds
|
||||||
|
* @return the loaded furniture
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public static BukkitFurniture place(Location location, CustomFurniture furniture, FurnitureDataAccessor dataAccessor, boolean playSound) {
|
||||||
|
return BukkitFurnitureManager.instance().place(location, furniture, dataAccessor, playSound);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -165,18 +282,30 @@ public final class CraftEngineFurniture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the base furniture by the base entity
|
* Gets the furniture by the meta entity
|
||||||
*
|
*
|
||||||
* @param baseEntity base entity
|
* @param baseEntity base entity
|
||||||
* @return the loaded furniture
|
* @return the loaded furniture
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public static BukkitFurniture getLoadedFurnitureByBaseEntity(@NotNull Entity baseEntity) {
|
public static BukkitFurniture getLoadedFurnitureByMetaEntity(@NotNull Entity baseEntity) {
|
||||||
return BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(baseEntity.getEntityId());
|
return BukkitFurnitureManager.instance().loadedFurnitureByMetaEntityId(baseEntity.getEntityId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the base furniture by the seat entity
|
* Gets the furniture by the meta entity
|
||||||
|
*
|
||||||
|
* @param baseEntity base entity
|
||||||
|
* @return the loaded furniture
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
@Deprecated(since = "0.0.66")
|
||||||
|
public static BukkitFurniture getLoadedFurnitureByBaseEntity(@NotNull Entity baseEntity) {
|
||||||
|
return getLoadedFurnitureByMetaEntity(baseEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the furniture by the seat entity
|
||||||
*
|
*
|
||||||
* @param seat seat entity
|
* @param seat seat entity
|
||||||
* @return the loaded furniture
|
* @return the loaded furniture
|
||||||
@@ -186,7 +315,22 @@ public final class CraftEngineFurniture {
|
|||||||
if (isSeat(seat)) {
|
if (isSeat(seat)) {
|
||||||
CompoundTag seatExtraData = BukkitSeatManager.instance().getSeatExtraData(seat);
|
CompoundTag seatExtraData = BukkitSeatManager.instance().getSeatExtraData(seat);
|
||||||
int entityId = seatExtraData.getInt("entity_id");
|
int entityId = seatExtraData.getInt("entity_id");
|
||||||
BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entityId);
|
BukkitFurnitureManager.instance().loadedFurnitureByMetaEntityId(entityId);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the furniture by the collider entity
|
||||||
|
*
|
||||||
|
* @param collider collider entity
|
||||||
|
* @return the loaded furniture
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static BukkitFurniture getLoadedFurnitureByCollider(@NotNull Entity collider) {
|
||||||
|
Object nmsEntity = FastNMS.INSTANCE.method$CraftEntity$getHandle(collider);
|
||||||
|
if (nmsEntity instanceof CollisionEntity collisionEntity) {
|
||||||
|
return BukkitFurnitureManager.instance().loadedFurnitureByColliderEntityId(collisionEntity.getEntityId());
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -199,7 +343,7 @@ public final class CraftEngineFurniture {
|
|||||||
*/
|
*/
|
||||||
public static boolean remove(@NotNull Entity entity) {
|
public static boolean remove(@NotNull Entity entity) {
|
||||||
if (!isFurniture(entity)) return false;
|
if (!isFurniture(entity)) return false;
|
||||||
BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entity.getEntityId());
|
BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByMetaEntityId(entity.getEntityId());
|
||||||
if (furniture == null) return false;
|
if (furniture == null) return false;
|
||||||
furniture.destroy();
|
furniture.destroy();
|
||||||
return true;
|
return true;
|
||||||
@@ -217,7 +361,7 @@ public final class CraftEngineFurniture {
|
|||||||
boolean dropLoot,
|
boolean dropLoot,
|
||||||
boolean playSound) {
|
boolean playSound) {
|
||||||
if (!isFurniture(entity)) return false;
|
if (!isFurniture(entity)) return false;
|
||||||
BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entity.getEntityId());
|
BukkitFurniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByMetaEntityId(entity.getEntityId());
|
||||||
if (furniture == null) return false;
|
if (furniture == null) return false;
|
||||||
remove(furniture, (net.momirealms.craftengine.core.entity.player.Player) null, dropLoot, playSound);
|
remove(furniture, (net.momirealms.craftengine.core.entity.player.Player) null, dropLoot, playSound);
|
||||||
return true;
|
return true;
|
||||||
@@ -237,7 +381,7 @@ public final class CraftEngineFurniture {
|
|||||||
boolean dropLoot,
|
boolean dropLoot,
|
||||||
boolean playSound) {
|
boolean playSound) {
|
||||||
if (!isFurniture(entity)) return false;
|
if (!isFurniture(entity)) return false;
|
||||||
Furniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entity.getEntityId());
|
Furniture furniture = BukkitFurnitureManager.instance().loadedFurnitureByMetaEntityId(entity.getEntityId());
|
||||||
if (furniture == null) return false;
|
if (furniture == null) return false;
|
||||||
remove(furniture, player, dropLoot, playSound);
|
remove(furniture, player, dropLoot, playSound);
|
||||||
return true;
|
return true;
|
||||||
@@ -286,16 +430,16 @@ public final class CraftEngineFurniture {
|
|||||||
boolean dropLoot,
|
boolean dropLoot,
|
||||||
boolean playSound) {
|
boolean playSound) {
|
||||||
if (!furniture.isValid()) return;
|
if (!furniture.isValid()) return;
|
||||||
Location location = ((BukkitFurniture) furniture).dropLocation();
|
Location location = ((BukkitFurniture) furniture).getDropLocation();
|
||||||
furniture.destroy();
|
furniture.destroy();
|
||||||
LootTable<ItemStack> lootTable = (LootTable<ItemStack>) furniture.config().lootTable();
|
LootTable<ItemStack> lootTable = (LootTable<ItemStack>) furniture.config.lootTable();
|
||||||
World world = new BukkitWorld(location.getWorld());
|
World world = new BukkitWorld(location.getWorld());
|
||||||
WorldPosition position = new WorldPosition(world, location.getX(), location.getY(), location.getZ());
|
WorldPosition position = new WorldPosition(world, location.getX(), location.getY(), location.getZ());
|
||||||
if (dropLoot && lootTable != null) {
|
if (dropLoot && lootTable != null) {
|
||||||
ContextHolder.Builder builder = ContextHolder.builder()
|
ContextHolder.Builder builder = ContextHolder.builder()
|
||||||
.withParameter(DirectContextParameters.POSITION, position)
|
.withParameter(DirectContextParameters.POSITION, position)
|
||||||
.withParameter(DirectContextParameters.FURNITURE, furniture)
|
.withParameter(DirectContextParameters.FURNITURE, furniture)
|
||||||
.withOptionalParameter(DirectContextParameters.FURNITURE_ITEM, furniture.extraData().item().orElse(null));
|
.withOptionalParameter(DirectContextParameters.FURNITURE_ITEM, furniture.dataAccessor.item().orElse(null));
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
Item<?> itemInHand = player.getItemInHand(InteractionHand.MAIN_HAND);
|
Item<?> itemInHand = player.getItemInHand(InteractionHand.MAIN_HAND);
|
||||||
builder.withParameter(DirectContextParameters.PLAYER, player)
|
builder.withParameter(DirectContextParameters.PLAYER, player)
|
||||||
@@ -307,7 +451,7 @@ public final class CraftEngineFurniture {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (playSound) {
|
if (playSound) {
|
||||||
world.playBlockSound(position, furniture.config().settings().sounds().breakSound());
|
world.playBlockSound(position, furniture.config.settings().sounds().breakSound());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
package net.momirealms.craftengine.bukkit.api.event;
|
package net.momirealms.craftengine.bukkit.api.event;
|
||||||
|
|
||||||
import net.momirealms.craftengine.core.entity.furniture.AnchorType;
|
|
||||||
import net.momirealms.craftengine.core.entity.furniture.CustomFurniture;
|
import net.momirealms.craftengine.core.entity.furniture.CustomFurniture;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.FurnitureVariant;
|
||||||
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.block.BlockFace;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.Cancellable;
|
import org.bukkit.event.Cancellable;
|
||||||
import org.bukkit.event.HandlerList;
|
import org.bukkit.event.HandlerList;
|
||||||
@@ -17,23 +16,20 @@ public final class FurnitureAttemptPlaceEvent extends PlayerEvent implements Can
|
|||||||
private boolean cancelled;
|
private boolean cancelled;
|
||||||
private final CustomFurniture furniture;
|
private final CustomFurniture furniture;
|
||||||
private final Location location;
|
private final Location location;
|
||||||
private final AnchorType anchorType;
|
private final FurnitureVariant variant;
|
||||||
private final BlockFace clickedFace;
|
|
||||||
private final Block clickedBlock;
|
private final Block clickedBlock;
|
||||||
private final InteractionHand hand;
|
private final InteractionHand hand;
|
||||||
|
|
||||||
public FurnitureAttemptPlaceEvent(@NotNull Player player,
|
public FurnitureAttemptPlaceEvent(@NotNull Player player,
|
||||||
@NotNull CustomFurniture furniture,
|
@NotNull CustomFurniture furniture,
|
||||||
@NotNull AnchorType anchorType,
|
@NotNull FurnitureVariant variant,
|
||||||
@NotNull Location location,
|
@NotNull Location location,
|
||||||
@NotNull BlockFace clickedFace,
|
|
||||||
@NotNull InteractionHand hand,
|
@NotNull InteractionHand hand,
|
||||||
@NotNull Block clickedBlock) {
|
@NotNull Block clickedBlock) {
|
||||||
super(player);
|
super(player);
|
||||||
this.furniture = furniture;
|
this.furniture = furniture;
|
||||||
this.location = location;
|
this.location = location;
|
||||||
this.anchorType = anchorType;
|
this.variant = variant;
|
||||||
this.clickedFace = clickedFace;
|
|
||||||
this.clickedBlock = clickedBlock;
|
this.clickedBlock = clickedBlock;
|
||||||
this.hand = hand;
|
this.hand = hand;
|
||||||
}
|
}
|
||||||
@@ -48,19 +44,14 @@ public final class FurnitureAttemptPlaceEvent extends PlayerEvent implements Can
|
|||||||
return hand;
|
return hand;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public BlockFace clickedFace() {
|
|
||||||
return clickedFace;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public Player player() {
|
public Player player() {
|
||||||
return getPlayer();
|
return getPlayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public AnchorType anchorType() {
|
public FurnitureVariant variant() {
|
||||||
return anchorType;
|
return variant;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package net.momirealms.craftengine.bukkit.api.event;
|
package net.momirealms.craftengine.bukkit.api.event;
|
||||||
|
|
||||||
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture;
|
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture;
|
||||||
import net.momirealms.craftengine.core.entity.furniture.HitBox;
|
import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBox;
|
||||||
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
@@ -16,23 +16,23 @@ public final class FurnitureInteractEvent extends PlayerEvent implements Cancell
|
|||||||
private final BukkitFurniture furniture;
|
private final BukkitFurniture furniture;
|
||||||
private final InteractionHand hand;
|
private final InteractionHand hand;
|
||||||
private final Location interactionPoint;
|
private final Location interactionPoint;
|
||||||
private final HitBox hitBox;
|
private final FurnitureHitBox furnitureHitBox;
|
||||||
|
|
||||||
public FurnitureInteractEvent(@NotNull Player player,
|
public FurnitureInteractEvent(@NotNull Player player,
|
||||||
@NotNull BukkitFurniture furniture,
|
@NotNull BukkitFurniture furniture,
|
||||||
@NotNull InteractionHand hand,
|
@NotNull InteractionHand hand,
|
||||||
@NotNull Location interactionPoint,
|
@NotNull Location interactionPoint,
|
||||||
@NotNull HitBox hitBox) {
|
@NotNull FurnitureHitBox furnitureHitBox) {
|
||||||
super(player);
|
super(player);
|
||||||
this.furniture = furniture;
|
this.furniture = furniture;
|
||||||
this.hand = hand;
|
this.hand = hand;
|
||||||
this.interactionPoint = interactionPoint;
|
this.interactionPoint = interactionPoint;
|
||||||
this.hitBox = hitBox;
|
this.furnitureHitBox = furnitureHitBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public HitBox hitBox() {
|
public FurnitureHitBox hitBox() {
|
||||||
return hitBox;
|
return furnitureHitBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import net.momirealms.craftengine.core.plugin.context.ContextHolder;
|
|||||||
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
|
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
|
||||||
import net.momirealms.craftengine.core.plugin.context.event.EventTrigger;
|
import net.momirealms.craftengine.core.plugin.context.event.EventTrigger;
|
||||||
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
|
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
|
||||||
|
import net.momirealms.craftengine.core.sound.SoundData;
|
||||||
import net.momirealms.craftengine.core.sound.SoundSource;
|
import net.momirealms.craftengine.core.sound.SoundSource;
|
||||||
import net.momirealms.craftengine.core.util.Cancellable;
|
import net.momirealms.craftengine.core.util.Cancellable;
|
||||||
import net.momirealms.craftengine.core.util.ItemUtils;
|
import net.momirealms.craftengine.core.util.ItemUtils;
|
||||||
@@ -38,6 +39,7 @@ import org.bukkit.event.block.BlockBreakEvent;
|
|||||||
import org.bukkit.event.block.BlockPhysicsEvent;
|
import org.bukkit.event.block.BlockPhysicsEvent;
|
||||||
import org.bukkit.event.block.BlockPlaceEvent;
|
import org.bukkit.event.block.BlockPlaceEvent;
|
||||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||||
|
import org.bukkit.event.entity.EntityDamageEvent;
|
||||||
import org.bukkit.event.world.GenericGameEvent;
|
import org.bukkit.event.world.GenericGameEvent;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
@@ -233,12 +235,14 @@ public final class BlockEventListener implements Listener {
|
|||||||
|
|
||||||
@EventHandler(priority = EventPriority.LOW)
|
@EventHandler(priority = EventPriority.LOW)
|
||||||
public void onStep(GenericGameEvent event) {
|
public void onStep(GenericGameEvent event) {
|
||||||
if (event.getEvent() != GameEvent.STEP) return;
|
GameEvent gameEvent = event.getEvent();
|
||||||
|
// 只处理落地和走路
|
||||||
|
if (gameEvent != GameEvent.STEP) return;
|
||||||
Entity entity = event.getEntity();
|
Entity entity = event.getEntity();
|
||||||
if (!(entity instanceof Player player)) return;
|
if (!(entity instanceof Player player)) return;
|
||||||
BlockPos pos = EntityUtils.getOnPos(player);
|
BlockPos pos = EntityUtils.getOnPos(player);
|
||||||
Block block = player.getWorld().getBlockAt(pos.x(), pos.y(), pos.z());
|
Block block = player.getWorld().getBlockAt(pos.x(), pos.y(), pos.z());
|
||||||
Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(block.getWorld()), LocationUtils.toBlockPos(block.getX(), block.getY(), block.getZ()));
|
Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(player.getWorld()), LocationUtils.toBlockPos(pos));
|
||||||
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(blockState);
|
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(blockState);
|
||||||
if (optionalCustomState.isPresent()) {
|
if (optionalCustomState.isPresent()) {
|
||||||
Location location = player.getLocation();
|
Location location = player.getLocation();
|
||||||
@@ -253,7 +257,8 @@ public final class BlockEventListener implements Listener {
|
|||||||
if (cancellable.isCancelled() && !Config.processCancelledStep()) {
|
if (cancellable.isCancelled() && !Config.processCancelledStep()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
player.playSound(location, state.settings().sounds().stepSound().id().toString(), SoundCategory.BLOCKS, state.settings().sounds().stepSound().volume().get(), state.settings().sounds().stepSound().pitch().get());
|
SoundData soundData = state.settings().sounds().stepSound();
|
||||||
|
player.playSound(location, soundData.id().toString(), SoundCategory.BLOCKS, soundData.volume().get(), soundData.pitch().get());
|
||||||
} else if (Config.enableSoundSystem()) {
|
} else if (Config.enableSoundSystem()) {
|
||||||
if (event.isCancelled() && !Config.processCancelledStep()) {
|
if (event.isCancelled() && !Config.processCancelledStep()) {
|
||||||
return;
|
return;
|
||||||
@@ -267,6 +272,32 @@ public final class BlockEventListener implements Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH)
|
||||||
|
public void onFall(EntityDamageEvent event) {
|
||||||
|
if (event.getCause() != EntityDamageEvent.DamageCause.FALL)
|
||||||
|
return;
|
||||||
|
if (!(event.getEntity() instanceof Player player)) return;
|
||||||
|
BlockPos pos = EntityUtils.getOnPos(player);
|
||||||
|
Object blockState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(player.getWorld()), LocationUtils.toBlockPos(pos));
|
||||||
|
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(blockState);
|
||||||
|
if (optionalCustomState.isPresent()) {
|
||||||
|
Location location = player.getLocation();
|
||||||
|
ImmutableBlockState state = optionalCustomState.get();
|
||||||
|
SoundData soundData = state.settings().sounds().fallSound();
|
||||||
|
player.playSound(location, soundData.id().toString(), SoundCategory.BLOCKS, soundData.volume().get(), soundData.pitch().get());
|
||||||
|
} else if (Config.enableSoundSystem()) {
|
||||||
|
if (event.isCancelled() && !Config.processCancelledStep()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Object soundType = FastNMS.INSTANCE.method$BlockBehaviour$BlockStateBase$getSoundType(blockState);
|
||||||
|
Object soundEvent = FastNMS.INSTANCE.field$SoundType$fallSound(soundType);
|
||||||
|
Object soundId = FastNMS.INSTANCE.field$SoundEvent$location(soundEvent);
|
||||||
|
if (this.manager.isStepSoundMissing(soundId)) {
|
||||||
|
player.playSound(player.getLocation(), soundId.toString(), SoundCategory.BLOCKS, 0.15f, 1f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||||
public void onBlockPhysics(BlockPhysicsEvent event) {
|
public void onBlockPhysics(BlockPhysicsEvent event) {
|
||||||
// for vanilla blocks
|
// for vanilla blocks
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import java.util.*;
|
|||||||
|
|
||||||
public final class BukkitBlockManager extends AbstractBlockManager {
|
public final class BukkitBlockManager extends AbstractBlockManager {
|
||||||
public static final Set<Object> CLIENT_SIDE_NOTE_BLOCKS = new HashSet<>(2048, 0.6f);
|
public static final Set<Object> CLIENT_SIDE_NOTE_BLOCKS = new HashSet<>(2048, 0.6f);
|
||||||
|
private static final Object BLOCK_POS$ZERO = LocationUtils.toBlockPos(0,0,0);
|
||||||
private static final Object ALWAYS_FALSE = FastNMS.INSTANCE.method$StatePredicate$always(false);
|
private static final Object ALWAYS_FALSE = FastNMS.INSTANCE.method$StatePredicate$always(false);
|
||||||
private static final Object ALWAYS_TRUE = FastNMS.INSTANCE.method$StatePredicate$always(true);
|
private static final Object ALWAYS_TRUE = FastNMS.INSTANCE.method$StatePredicate$always(true);
|
||||||
private static BukkitBlockManager instance;
|
private static BukkitBlockManager instance;
|
||||||
@@ -80,12 +81,14 @@ public final class BukkitBlockManager extends AbstractBlockManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init() {
|
public void init() {
|
||||||
|
super.init();
|
||||||
this.initMirrorRegistry();
|
this.initMirrorRegistry();
|
||||||
this.initFireBlock();
|
this.initFireBlock();
|
||||||
this.deceiveBukkitRegistry();
|
this.deceiveBukkitRegistry();
|
||||||
this.markVanillaNoteBlocks();
|
this.markVanillaNoteBlocks();
|
||||||
|
this.findViewBlockingVanillaBlocks();
|
||||||
Arrays.fill(this.immutableBlockStates, EmptyBlock.INSTANCE.defaultState());
|
Arrays.fill(this.immutableBlockStates, EmptyBlock.INSTANCE.defaultState());
|
||||||
this.plugin.networkManager().registerBlockStatePacketListeners(this.blockStateMappings); // 一定要预先初始化一次,预防id超出上限
|
this.plugin.networkManager().registerBlockStatePacketListeners(this.blockStateMappings, this::isViewBlockingBlock); // 一定要预先初始化一次,预防id超出上限
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BukkitBlockManager instance() {
|
public static BukkitBlockManager instance() {
|
||||||
@@ -125,7 +128,7 @@ public final class BukkitBlockManager extends AbstractBlockManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delayedLoad() {
|
public void delayedLoad() {
|
||||||
this.plugin.networkManager().registerBlockStatePacketListeners(this.blockStateMappings); // 重置方块映射表
|
this.plugin.networkManager().registerBlockStatePacketListeners(this.blockStateMappings, this::isViewBlockingBlock); // 重置方块映射表
|
||||||
super.delayedLoad();
|
super.delayedLoad();
|
||||||
this.cachedVisualBlockStatePacket = VisualBlockStatePacket.create();
|
this.cachedVisualBlockStatePacket = VisualBlockStatePacket.create();
|
||||||
for (BukkitServerPlayer player : BukkitNetworkManager.instance().onlineUsers()) {
|
for (BukkitServerPlayer player : BukkitNetworkManager.instance().onlineUsers()) {
|
||||||
@@ -223,7 +226,7 @@ public final class BukkitBlockManager extends AbstractBlockManager {
|
|||||||
protected void applyPlatformSettings(ImmutableBlockState state) {
|
protected void applyPlatformSettings(ImmutableBlockState state) {
|
||||||
DelegatingBlockState nmsState = (DelegatingBlockState) state.customBlockState().literalObject();
|
DelegatingBlockState nmsState = (DelegatingBlockState) state.customBlockState().literalObject();
|
||||||
nmsState.setBlockState(state);
|
nmsState.setBlockState(state);
|
||||||
Object nmsVisualState = state.vanillaBlockState().literalObject();
|
Object nmsVisualState = state.visualBlockState().literalObject();
|
||||||
|
|
||||||
BlockSettings settings = state.settings();
|
BlockSettings settings = state.settings();
|
||||||
try {
|
try {
|
||||||
@@ -240,8 +243,15 @@ public final class BukkitBlockManager extends AbstractBlockManager {
|
|||||||
boolean useShapeForLightOcclusion = settings.useShapeForLightOcclusion() == Tristate.UNDEFINED ? CoreReflections.field$BlockStateBase$useShapeForLightOcclusion.getBoolean(nmsVisualState) : settings.useShapeForLightOcclusion().asBoolean();
|
boolean useShapeForLightOcclusion = settings.useShapeForLightOcclusion() == Tristate.UNDEFINED ? CoreReflections.field$BlockStateBase$useShapeForLightOcclusion.getBoolean(nmsVisualState) : settings.useShapeForLightOcclusion().asBoolean();
|
||||||
CoreReflections.field$BlockStateBase$useShapeForLightOcclusion.set(nmsState, useShapeForLightOcclusion);
|
CoreReflections.field$BlockStateBase$useShapeForLightOcclusion.set(nmsState, useShapeForLightOcclusion);
|
||||||
CoreReflections.field$BlockStateBase$isRedstoneConductor.set(nmsState, settings.isRedstoneConductor().asBoolean() ? ALWAYS_TRUE : ALWAYS_FALSE);
|
CoreReflections.field$BlockStateBase$isRedstoneConductor.set(nmsState, settings.isRedstoneConductor().asBoolean() ? ALWAYS_TRUE : ALWAYS_FALSE);
|
||||||
|
|
||||||
|
boolean suffocating = settings.isSuffocating() == Tristate.UNDEFINED ? (canBlockView(state.visualBlockState())) : (settings.isSuffocating().asBoolean());
|
||||||
|
CoreReflections.field$BlockStateBase$isSuffocating.set(nmsState, suffocating ? ALWAYS_TRUE : ALWAYS_FALSE);
|
||||||
CoreReflections.field$BlockStateBase$isSuffocating.set(nmsState, settings.isSuffocating().asBoolean() ? ALWAYS_TRUE : ALWAYS_FALSE);
|
CoreReflections.field$BlockStateBase$isSuffocating.set(nmsState, settings.isSuffocating().asBoolean() ? ALWAYS_TRUE : ALWAYS_FALSE);
|
||||||
CoreReflections.field$BlockStateBase$isViewBlocking.set(nmsState, settings.isViewBlocking() == Tristate.UNDEFINED ? settings.isSuffocating().asBoolean() ? ALWAYS_TRUE : ALWAYS_FALSE : (settings.isViewBlocking().asBoolean() ? ALWAYS_TRUE : ALWAYS_FALSE));
|
CoreReflections.field$BlockStateBase$isViewBlocking.set(nmsState,
|
||||||
|
settings.isViewBlocking() == Tristate.UNDEFINED ?
|
||||||
|
(suffocating ? ALWAYS_TRUE : ALWAYS_FALSE) :
|
||||||
|
(settings.isViewBlocking().asBoolean() ? ALWAYS_TRUE : ALWAYS_FALSE)
|
||||||
|
);
|
||||||
|
|
||||||
DelegatingBlock nmsBlock = (DelegatingBlock) BlockStateUtils.getBlockOwner(nmsState);
|
DelegatingBlock nmsBlock = (DelegatingBlock) BlockStateUtils.getBlockOwner(nmsState);
|
||||||
ObjectHolder<BlockShape> shapeHolder = nmsBlock.shapeDelegate();
|
ObjectHolder<BlockShape> shapeHolder = nmsBlock.shapeDelegate();
|
||||||
@@ -291,9 +301,16 @@ public final class BukkitBlockManager extends AbstractBlockManager {
|
|||||||
this.burnableBlocks.add(nmsBlock);
|
this.burnableBlocks.add(nmsBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
Key vanillaBlockId = state.vanillaBlockState().ownerId();
|
Key vanillaBlockId = state.visualBlockState().ownerId();
|
||||||
BlockGenerator.field$CraftEngineBlock$isNoteBlock().set(nmsBlock, vanillaBlockId.equals(BlockKeys.NOTE_BLOCK));
|
BlockGenerator.field$CraftEngineBlock$isNoteBlock().set(nmsBlock, vanillaBlockId.equals(BlockKeys.NOTE_BLOCK));
|
||||||
BlockGenerator.field$CraftEngineBlock$isTripwire().set(nmsBlock, vanillaBlockId.equals(BlockKeys.TRIPWIRE));
|
BlockGenerator.field$CraftEngineBlock$isTripwire().set(nmsBlock, vanillaBlockId.equals(BlockKeys.TRIPWIRE));
|
||||||
|
if (vanillaBlockId.equals(BlockKeys.BARRIER)) {
|
||||||
|
state.setRestoreBlockState(createBlockState("minecraft:glass"));
|
||||||
|
} else {
|
||||||
|
state.setRestoreBlockState(state.visualBlockState());
|
||||||
|
}
|
||||||
|
// 根据客户端的状态决定其是否阻挡视线
|
||||||
|
super.viewBlockingBlocks[state.customBlockState().registryId()] = canBlockView(state.visualBlockState());
|
||||||
} catch (ReflectiveOperationException e) {
|
} catch (ReflectiveOperationException e) {
|
||||||
this.plugin.logger().warn("Failed to apply platform block settings for block state " + state, e);
|
this.plugin.logger().warn("Failed to apply platform block settings for block state " + state, e);
|
||||||
}
|
}
|
||||||
@@ -379,6 +396,23 @@ public final class BukkitBlockManager extends AbstractBlockManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean canBlockView(BlockStateWrapper wrapper) {
|
||||||
|
Object blockState = wrapper.literalObject();
|
||||||
|
if (!BlockStateUtils.isOcclude(blockState)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return FastNMS.INSTANCE.method$BlockStateBase$isCollisionShapeFullBlock(blockState, CoreReflections.instance$EmptyBlockGetter$INSTANCE, BLOCK_POS$ZERO);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void findViewBlockingVanillaBlocks() {
|
||||||
|
for (int i = 0; i < this.vanillaBlockStateCount; i++) {
|
||||||
|
BlockStateWrapper blockState = BlockRegistryMirror.byId(i);
|
||||||
|
if (canBlockView(blockState)) {
|
||||||
|
this.viewBlockingBlocks[i] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setVanillaBlockTags(Key id, List<String> tags) {
|
protected void setVanillaBlockTags(Key id, List<String> tags) {
|
||||||
Object block = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, KeyUtils.toResourceLocation(id));
|
Object block = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, KeyUtils.toResourceLocation(id));
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class BukkitCustomBlockStateWrapper extends AbstractBlockStateWrapper imp
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BlockStateWrapper visualBlockState() {
|
public BlockStateWrapper visualBlockState() {
|
||||||
return getImmutableBlockState().map(ImmutableBlockState::vanillaBlockState).orElse(null);
|
return getImmutableBlockState().map(ImmutableBlockState::visualBlockState).orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
|||||||
import net.momirealms.craftengine.core.block.behavior.AbstractBlockBehavior;
|
import net.momirealms.craftengine.core.block.behavior.AbstractBlockBehavior;
|
||||||
import net.momirealms.craftengine.core.block.properties.Property;
|
import net.momirealms.craftengine.core.block.properties.Property;
|
||||||
import net.momirealms.craftengine.core.util.*;
|
import net.momirealms.craftengine.core.util.*;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -22,7 +23,7 @@ import java.util.concurrent.Callable;
|
|||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
public class BukkitBlockBehavior extends AbstractBlockBehavior {
|
public class BukkitBlockBehavior extends AbstractBlockBehavior {
|
||||||
private static final Map<String, BiConsumer<BukkitBlockBehavior, Property<?>>> HARD_CODED_PROPERTY_DATA = new HashMap<>();
|
private static final Map<String, BiConsumer<@NotNull BukkitBlockBehavior, Property<?>>> HARD_CODED_PROPERTY_DATA = new HashMap<>();
|
||||||
static {
|
static {
|
||||||
HARD_CODED_PROPERTY_DATA.put("axis", (behavior, property) -> {
|
HARD_CODED_PROPERTY_DATA.put("axis", (behavior, property) -> {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@@ -180,7 +181,7 @@ public class BukkitBlockBehavior extends AbstractBlockBehavior {
|
|||||||
public boolean isPathFindable(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
|
public boolean isPathFindable(Object thisBlock, Object[] args, Callable<Object> superMethod) throws Exception {
|
||||||
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(args[0]);
|
Optional<ImmutableBlockState> optionalCustomState = BlockStateUtils.getOptionalCustomBlockState(args[0]);
|
||||||
if (optionalCustomState.isEmpty()) return false;
|
if (optionalCustomState.isEmpty()) return false;
|
||||||
BlockStateWrapper vanillaState = optionalCustomState.get().vanillaBlockState();
|
BlockStateWrapper vanillaState = optionalCustomState.get().visualBlockState();
|
||||||
if (vanillaState == null) return false;
|
if (vanillaState == null) return false;
|
||||||
return FastNMS.INSTANCE.method$BlockStateBase$isPathFindable(vanillaState.literalObject(), VersionHelper.isOrAbove1_20_5() ? null : args[1], VersionHelper.isOrAbove1_20_5() ? null : args[2], args[isPathFindable$type]);
|
return FastNMS.INSTANCE.method$BlockStateBase$isPathFindable(vanillaState.literalObject(), VersionHelper.isOrAbove1_20_5() ? null : args[1], VersionHelper.isOrAbove1_20_5() ? null : args[2], args[isPathFindable$type]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
|
|||||||
public static final Key SURFACE_SPREADING_BLOCK = Key.from("craftengine:surface_spreading_block");
|
public static final Key SURFACE_SPREADING_BLOCK = Key.from("craftengine:surface_spreading_block");
|
||||||
public static final Key SNOWY_BLOCK = Key.from("craftengine:snowy_block");
|
public static final Key SNOWY_BLOCK = Key.from("craftengine:snowy_block");
|
||||||
public static final Key HANGABLE_BLOCK = Key.from("craftengine:hangable_block");
|
public static final Key HANGABLE_BLOCK = Key.from("craftengine:hangable_block");
|
||||||
|
public static final Key DROP_EXPERIENCE_BLOCK = Key.from("craftengine:drop_experience_block");
|
||||||
|
public static final Key DROP_EXP_BLOCK = Key.from("craftengine:drop_exp_block");
|
||||||
|
|
||||||
public static void init() {
|
public static void init() {
|
||||||
register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE);
|
register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE);
|
||||||
@@ -92,5 +94,7 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
|
|||||||
register(SURFACE_SPREADING_BLOCK, SurfaceSpreadingBlockBehavior.FACTORY);
|
register(SURFACE_SPREADING_BLOCK, SurfaceSpreadingBlockBehavior.FACTORY);
|
||||||
register(SNOWY_BLOCK, SnowyBlockBehavior.FACTORY);
|
register(SNOWY_BLOCK, SnowyBlockBehavior.FACTORY);
|
||||||
register(HANGABLE_BLOCK, HangableBlockBehavior.FACTORY);
|
register(HANGABLE_BLOCK, HangableBlockBehavior.FACTORY);
|
||||||
|
register(DROP_EXPERIENCE_BLOCK, DropExperienceBlockBehavior.FACTORY);
|
||||||
|
register(DROP_EXP_BLOCK, DropExperienceBlockBehavior.FACTORY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ public class CropBlockBehavior extends BukkitBlockBehavior {
|
|||||||
if (isMaxAge(state))
|
if (isMaxAge(state))
|
||||||
return InteractionResult.PASS;
|
return InteractionResult.PASS;
|
||||||
boolean sendSwing = false;
|
boolean sendSwing = false;
|
||||||
Object visualState = state.vanillaBlockState().literalObject();
|
Object visualState = state.visualBlockState().literalObject();
|
||||||
Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState);
|
Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState);
|
||||||
if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
|
if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
|
||||||
boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState);
|
boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState);
|
||||||
@@ -158,7 +158,7 @@ public class CropBlockBehavior extends BukkitBlockBehavior {
|
|||||||
}
|
}
|
||||||
ImmutableBlockState customState = optionalCustomState.get();
|
ImmutableBlockState customState = optionalCustomState.get();
|
||||||
boolean sendParticles = false;
|
boolean sendParticles = false;
|
||||||
Object visualState = customState.vanillaBlockState().literalObject();
|
Object visualState = customState.visualBlockState().literalObject();
|
||||||
Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState);
|
Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState);
|
||||||
if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
|
if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
|
||||||
boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, level, pos, visualState);
|
boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, level, pos, visualState);
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.block.behavior;
|
||||||
|
|
||||||
|
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||||
|
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||||
|
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||||
|
import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
||||||
|
import net.momirealms.craftengine.bukkit.world.BukkitWorldManager;
|
||||||
|
import net.momirealms.craftengine.core.block.BlockBehavior;
|
||||||
|
import net.momirealms.craftengine.core.block.BlockSettings;
|
||||||
|
import net.momirealms.craftengine.core.block.CustomBlock;
|
||||||
|
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||||
|
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
|
||||||
|
import net.momirealms.craftengine.core.item.Item;
|
||||||
|
import net.momirealms.craftengine.core.loot.LootContext;
|
||||||
|
import net.momirealms.craftengine.core.plugin.context.Condition;
|
||||||
|
import net.momirealms.craftengine.core.plugin.context.Context;
|
||||||
|
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
|
||||||
|
import net.momirealms.craftengine.core.plugin.context.event.EventConditions;
|
||||||
|
import net.momirealms.craftengine.core.plugin.context.number.NumberProvider;
|
||||||
|
import net.momirealms.craftengine.core.plugin.context.number.NumberProviders;
|
||||||
|
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
|
||||||
|
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||||
|
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||||
|
import net.momirealms.craftengine.core.world.BlockPos;
|
||||||
|
import net.momirealms.craftengine.core.world.Vec3d;
|
||||||
|
import net.momirealms.craftengine.core.world.World;
|
||||||
|
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
public class DropExperienceBlockBehavior extends BukkitBlockBehavior {
|
||||||
|
public static final Factory FACTORY = new Factory();
|
||||||
|
private final NumberProvider amount;
|
||||||
|
private final Predicate<Context> condition;
|
||||||
|
|
||||||
|
public DropExperienceBlockBehavior(CustomBlock customBlock, NumberProvider amount, Predicate<Context> condition) {
|
||||||
|
super(customBlock);
|
||||||
|
this.amount = amount;
|
||||||
|
this.condition = condition;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void spawnAfterBreak(Object thisBlock, Object[] args, Callable<Object> superMethod) {
|
||||||
|
boolean dropExperience = (boolean) args[4]; // 通常来说是 false
|
||||||
|
Item<ItemStack> item = BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(args[3]));
|
||||||
|
if (!dropExperience) {
|
||||||
|
ImmutableBlockState state = BlockStateUtils.getOptionalCustomBlockState(args[0]).orElse(null);
|
||||||
|
if (state == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BlockSettings settings = state.settings();
|
||||||
|
if (settings.requireCorrectTool()) {
|
||||||
|
if (item.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boolean cannotBreak = !settings.isCorrectTool(item.id())
|
||||||
|
&& (!settings.respectToolComponent()
|
||||||
|
|| !FastNMS.INSTANCE.method$ItemStack$isCorrectToolForDrops(args[3], state.customBlockState().literalObject()));
|
||||||
|
if (cannotBreak) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
World world = BukkitWorldManager.instance().wrap(FastNMS.INSTANCE.method$Level$getCraftWorld(args[1]));
|
||||||
|
BlockPos pos = LocationUtils.fromBlockPos(args[2]);
|
||||||
|
tryDropExperience(world, pos, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tryDropExperience(World world, BlockPos pos, Item<ItemStack> item) {
|
||||||
|
Vec3d dropPos = Vec3d.atCenterOf(pos);
|
||||||
|
ContextHolder holder = ContextHolder.builder()
|
||||||
|
.withParameter(DirectContextParameters.POSITION, new WorldPosition(world, dropPos))
|
||||||
|
.withParameter(DirectContextParameters.ITEM_IN_HAND, item)
|
||||||
|
.build();
|
||||||
|
LootContext context = new LootContext(world, null, 1.0f, holder);
|
||||||
|
if (!this.condition.test(context)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int finalAmount = this.amount.getInt(context);
|
||||||
|
if (finalAmount <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
world.dropExp(dropPos, finalAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Factory implements BlockBehaviorFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
|
||||||
|
NumberProvider amount = NumberProviders.fromObject(ResourceConfigUtils.get(arguments, "amount", "count"));
|
||||||
|
List<Condition<Context>> conditionList = ResourceConfigUtils.parseConfigAsList(ResourceConfigUtils.get(arguments, "conditions", "condition"), EventConditions::fromMap);
|
||||||
|
return new DropExperienceBlockBehavior(block, amount, MiscUtils.allOf(conditionList));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -147,7 +147,7 @@ public class FenceGateBlockBehavior extends BukkitBlockBehavior implements IsPat
|
|||||||
Player player = context.getPlayer();
|
Player player = context.getPlayer();
|
||||||
if (player == null) return;
|
if (player == null) return;
|
||||||
this.toggle(state, context.getLevel(), context.getClickedPos(), player);
|
this.toggle(state, context.getLevel(), context.getClickedPos(), player);
|
||||||
if (!InteractUtils.isInteractable((org.bukkit.entity.Player) player.platformPlayer(), BlockStateUtils.fromBlockData(state.vanillaBlockState().literalObject()), context.getHitResult(), (Item<ItemStack>) context.getItem())) {
|
if (!InteractUtils.isInteractable((org.bukkit.entity.Player) player.platformPlayer(), BlockStateUtils.fromBlockData(state.visualBlockState().literalObject()), context.getHitResult(), (Item<ItemStack>) context.getItem())) {
|
||||||
player.swingHand(context.getHand());
|
player.swingHand(context.getHand());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ public class GrassBlockBehavior extends BukkitBlockBehavior {
|
|||||||
}
|
}
|
||||||
boolean sendParticles = false;
|
boolean sendParticles = false;
|
||||||
ImmutableBlockState customState = optionalCustomState.get();
|
ImmutableBlockState customState = optionalCustomState.get();
|
||||||
Object visualState = customState.vanillaBlockState().literalObject();
|
Object visualState = customState.visualBlockState().literalObject();
|
||||||
Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState);
|
Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState);
|
||||||
if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
|
if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
|
||||||
boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, level, blockPos, visualState);
|
boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, level, blockPos, visualState);
|
||||||
@@ -93,7 +93,7 @@ public class GrassBlockBehavior extends BukkitBlockBehavior {
|
|||||||
if (!block.isEmpty())
|
if (!block.isEmpty())
|
||||||
return InteractionResult.PASS;
|
return InteractionResult.PASS;
|
||||||
boolean sendSwing = false;
|
boolean sendSwing = false;
|
||||||
Object visualState = state.vanillaBlockState().literalObject();
|
Object visualState = state.visualBlockState().literalObject();
|
||||||
Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState);
|
Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState);
|
||||||
if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
|
if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
|
||||||
boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState);
|
boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState);
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ public class SaplingBlockBehavior extends BukkitBlockBehavior {
|
|||||||
}
|
}
|
||||||
ImmutableBlockState customState = optionalCustomState.get();
|
ImmutableBlockState customState = optionalCustomState.get();
|
||||||
boolean sendParticles = false;
|
boolean sendParticles = false;
|
||||||
Object visualState = customState.vanillaBlockState().literalObject();
|
Object visualState = customState.visualBlockState().literalObject();
|
||||||
Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState);
|
Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState);
|
||||||
if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
|
if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
|
||||||
boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, level, blockPos, visualState);
|
boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, level, blockPos, visualState);
|
||||||
@@ -153,7 +153,7 @@ public class SaplingBlockBehavior extends BukkitBlockBehavior {
|
|||||||
if (ItemUtils.isEmpty(item) || !item.vanillaId().equals(ItemKeys.BONE_MEAL) || player == null || player.isAdventureMode())
|
if (ItemUtils.isEmpty(item) || !item.vanillaId().equals(ItemKeys.BONE_MEAL) || player == null || player.isAdventureMode())
|
||||||
return InteractionResult.PASS;
|
return InteractionResult.PASS;
|
||||||
boolean sendSwing = false;
|
boolean sendSwing = false;
|
||||||
Object visualState = state.vanillaBlockState().literalObject();
|
Object visualState = state.visualBlockState().literalObject();
|
||||||
Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState);
|
Object visualStateBlock = BlockStateUtils.getBlockOwner(visualState);
|
||||||
if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
|
if (CoreReflections.clazz$BonemealableBlock.isInstance(visualStateBlock)) {
|
||||||
boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState);
|
boolean is = FastNMS.INSTANCE.method$BonemealableBlock$isValidBonemealTarget(visualStateBlock, context.getLevel().serverWorld(), LocationUtils.toBlockPos(context.getClickedPos()), visualState);
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ public class TrapDoorBlockBehavior extends BukkitBlockBehavior implements IsPath
|
|||||||
Player player = context.getPlayer();
|
Player player = context.getPlayer();
|
||||||
if (player == null) return;
|
if (player == null) return;
|
||||||
this.toggle(state, context.getLevel(), context.getClickedPos(), player);
|
this.toggle(state, context.getLevel(), context.getClickedPos(), player);
|
||||||
if (!InteractUtils.isInteractable((org.bukkit.entity.Player) player.platformPlayer(), BlockStateUtils.fromBlockData(state.vanillaBlockState().literalObject()), context.getHitResult(), (Item<ItemStack>) context.getItem())) {
|
if (!InteractUtils.isInteractable((org.bukkit.entity.Player) player.platformPlayer(), BlockStateUtils.fromBlockData(state.visualBlockState().literalObject()), context.getHitResult(), (Item<ItemStack>) context.getItem())) {
|
||||||
player.swingHand(context.getHand());
|
player.swingHand(context.getHand());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package net.momirealms.craftengine.bukkit.block.entity;
|
package net.momirealms.craftengine.bukkit.block.entity;
|
||||||
|
|
||||||
|
import net.momirealms.craftengine.bukkit.block.behavior.SeatBlockBehavior;
|
||||||
import net.momirealms.craftengine.bukkit.entity.seat.BukkitSeat;
|
import net.momirealms.craftengine.bukkit.entity.seat.BukkitSeat;
|
||||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||||
import net.momirealms.craftengine.core.block.entity.BlockEntity;
|
import net.momirealms.craftengine.core.block.entity.BlockEntity;
|
||||||
@@ -13,6 +14,8 @@ import net.momirealms.craftengine.core.world.BlockPos;
|
|||||||
import net.momirealms.craftengine.core.world.WorldPosition;
|
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||||
import net.momirealms.sparrow.nbt.CompoundTag;
|
import net.momirealms.sparrow.nbt.CompoundTag;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public class SeatBlockEntity extends BlockEntity implements SeatOwner {
|
public class SeatBlockEntity extends BlockEntity implements SeatOwner {
|
||||||
private final Seat<SeatBlockEntity>[] seats;
|
private final Seat<SeatBlockEntity>[] seats;
|
||||||
|
|
||||||
@@ -38,10 +41,12 @@ public class SeatBlockEntity extends BlockEntity implements SeatOwner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean spawnSeat(Player player) {
|
public boolean spawnSeat(Player player) {
|
||||||
Property<?> facing = super.blockState.owner().value().getProperty("facing");
|
|
||||||
int yRot = 0;
|
int yRot = 0;
|
||||||
if (facing != null && facing.valueClass() == HorizontalDirection.class) {
|
Optional<SeatBlockBehavior> behavior = super.blockState.behavior().getAs(SeatBlockBehavior.class);
|
||||||
HorizontalDirection direction = (HorizontalDirection) super.blockState.get(facing);
|
if (behavior.isEmpty()) return false;
|
||||||
|
Property<HorizontalDirection> facing = behavior.get().directionProperty();
|
||||||
|
if (facing != null) {
|
||||||
|
HorizontalDirection direction = super.blockState.get(facing);
|
||||||
yRot = switch (direction) {
|
yRot = switch (direction) {
|
||||||
case NORTH -> 0;
|
case NORTH -> 0;
|
||||||
case SOUTH -> 180;
|
case SOUTH -> 180;
|
||||||
|
|||||||
@@ -172,8 +172,10 @@ public class SimpleStorageBlockEntity extends BlockEntity {
|
|||||||
|
|
||||||
public void updateOpenBlockState(boolean open) {
|
public void updateOpenBlockState(boolean open) {
|
||||||
ImmutableBlockState state = super.world.getBlockStateAtIfLoaded(this.pos);
|
ImmutableBlockState state = super.world.getBlockStateAtIfLoaded(this.pos);
|
||||||
if (state == null || state.behavior() != this.behavior) return;
|
if (state == null) return;
|
||||||
Property<Boolean> property = this.behavior.openProperty();
|
SimpleStorageBlockBehavior behavior = state.behavior().getAs(SimpleStorageBlockBehavior.class).orElse(null);
|
||||||
|
if (behavior == null) return;
|
||||||
|
Property<Boolean> property = behavior.openProperty();
|
||||||
if (property == null) return;
|
if (property == null) return;
|
||||||
super.world.world().setBlockState(this.pos.x(), this.pos.y(), this.pos.z(), state.with(property, open), UpdateOption.UPDATE_ALL.flags());
|
super.world.world().setBlockState(this.pos.x(), this.pos.y(), this.pos.z(), state.with(property, open), UpdateOption.UPDATE_ALL.flags());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.block.entity.renderer.element;
|
||||||
|
|
||||||
|
import com.mojang.datafixers.util.Pair;
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntList;
|
||||||
|
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeHolders;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
|
||||||
|
import net.momirealms.craftengine.bukkit.world.score.BukkitTeamManager;
|
||||||
|
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement;
|
||||||
|
import net.momirealms.craftengine.core.entity.player.Player;
|
||||||
|
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||||
|
import net.momirealms.craftengine.core.world.BlockPos;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class ArmorStandBlockEntityElement implements BlockEntityElement {
|
||||||
|
public final ArmorStandBlockEntityElementConfig config;
|
||||||
|
public final Object cachedSpawnPacket;
|
||||||
|
public final Object cachedDespawnPacket;
|
||||||
|
public final Object cachedUpdatePosPacket;
|
||||||
|
public final Object cachedScalePacket;
|
||||||
|
public final Object cachedTeamPacket;
|
||||||
|
public final int entityId;
|
||||||
|
public final UUID uuid = UUID.randomUUID();
|
||||||
|
|
||||||
|
public ArmorStandBlockEntityElement(ArmorStandBlockEntityElementConfig config, BlockPos pos) {
|
||||||
|
this(config, pos, CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArmorStandBlockEntityElement(ArmorStandBlockEntityElementConfig config, BlockPos pos, int entityId, boolean posChanged) {
|
||||||
|
Vector3f position = config.position();
|
||||||
|
this.cachedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||||
|
entityId, this.uuid, pos.x() + position.x, pos.y() + position.y, pos.z() + position.z,
|
||||||
|
config.xRot(), config.yRot(), MEntityTypes.ARMOR_STAND, 0, CoreReflections.instance$Vec3$Zero, config.yRot()
|
||||||
|
);
|
||||||
|
this.config = config;
|
||||||
|
this.cachedDespawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(IntList.of(entityId));
|
||||||
|
this.entityId = entityId;
|
||||||
|
this.cachedUpdatePosPacket = posChanged ? FastNMS.INSTANCE.constructor$ClientboundEntityPositionSyncPacket(this.entityId, pos.x() + position.x, pos.y() + position.y, pos.z() + position.z, config.yRot(), config.xRot(), false) : null;
|
||||||
|
if (VersionHelper.isOrAbove1_20_5() && config.scale() != 1) {
|
||||||
|
Object attributeIns = FastNMS.INSTANCE.constructor$AttributeInstance(MAttributeHolders.SCALE, (Consumer<?>) (o) -> {});
|
||||||
|
FastNMS.INSTANCE.method$AttributeInstance$setBaseValue(attributeIns, config.scale());
|
||||||
|
this.cachedScalePacket = FastNMS.INSTANCE.constructor$ClientboundUpdateAttributesPacket(entityId, Collections.singletonList(attributeIns));
|
||||||
|
} else {
|
||||||
|
this.cachedScalePacket = null;
|
||||||
|
}
|
||||||
|
Object teamPacket = null;
|
||||||
|
if (config.glowColor != null) {
|
||||||
|
Object teamByColor = BukkitTeamManager.instance().getTeamByColor(config.glowColor);
|
||||||
|
if (teamByColor != null) {
|
||||||
|
teamPacket = FastNMS.INSTANCE.method$ClientboundSetPlayerTeamPacket$createMultiplePlayerPacket(teamByColor, List.of(this.uuid.toString()), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.cachedTeamPacket = teamPacket;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void hide(Player player) {
|
||||||
|
player.sendPacket(this.cachedDespawnPacket, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void show(Player player) {
|
||||||
|
player.sendPackets(List.of(this.cachedSpawnPacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player))), false);
|
||||||
|
player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundSetEquipmentPacket(this.entityId, List.of(
|
||||||
|
Pair.of(CoreReflections.instance$EquipmentSlot$HEAD, this.config.item(player).getLiteralObject())
|
||||||
|
)), false);
|
||||||
|
if (this.cachedDespawnPacket != null) {
|
||||||
|
player.sendPacket(this.cachedDespawnPacket, false);
|
||||||
|
}
|
||||||
|
if (this.cachedTeamPacket != null) {
|
||||||
|
player.sendPacket(this.cachedTeamPacket, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transform(Player player) {
|
||||||
|
if (this.cachedUpdatePosPacket != null) {
|
||||||
|
player.sendPackets(List.of(
|
||||||
|
this.cachedUpdatePosPacket,
|
||||||
|
FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player)),
|
||||||
|
FastNMS.INSTANCE.constructor$ClientboundSetEquipmentPacket(this.entityId, List.of(
|
||||||
|
Pair.of(CoreReflections.instance$EquipmentSlot$HEAD, this.config.item(player).getLiteralObject())
|
||||||
|
))
|
||||||
|
), false);
|
||||||
|
} else {
|
||||||
|
player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadataValues(player)), false);
|
||||||
|
player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundSetEquipmentPacket(this.entityId, List.of(
|
||||||
|
Pair.of(CoreReflections.instance$EquipmentSlot$HEAD, this.config.item(player).getLiteralObject())
|
||||||
|
)), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.block.entity.renderer.element;
|
||||||
|
|
||||||
|
import com.google.common.base.Objects;
|
||||||
|
import net.momirealms.craftengine.bukkit.entity.data.ArmorStandData;
|
||||||
|
import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData;
|
||||||
|
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||||
|
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig;
|
||||||
|
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory;
|
||||||
|
import net.momirealms.craftengine.core.entity.player.Player;
|
||||||
|
import net.momirealms.craftengine.core.item.Item;
|
||||||
|
import net.momirealms.craftengine.core.item.ItemKeys;
|
||||||
|
import net.momirealms.craftengine.core.util.Key;
|
||||||
|
import net.momirealms.craftengine.core.util.LegacyChatFormatter;
|
||||||
|
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||||
|
import net.momirealms.craftengine.core.world.BlockPos;
|
||||||
|
import net.momirealms.craftengine.core.world.Glowing;
|
||||||
|
import net.momirealms.craftengine.core.world.World;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public class ArmorStandBlockEntityElementConfig implements BlockEntityElementConfig<ArmorStandBlockEntityElement>, Glowing {
|
||||||
|
public static final Factory FACTORY = new Factory();
|
||||||
|
public final Function<Player, List<Object>> lazyMetadataPacket;
|
||||||
|
public final Key itemId;
|
||||||
|
public final float scale;
|
||||||
|
public final Vector3f position;
|
||||||
|
public final float xRot;
|
||||||
|
public final float yRot;
|
||||||
|
public final boolean small;
|
||||||
|
public final LegacyChatFormatter glowColor;
|
||||||
|
|
||||||
|
public ArmorStandBlockEntityElementConfig(Key itemId,
|
||||||
|
float scale,
|
||||||
|
Vector3f position,
|
||||||
|
float xRot,
|
||||||
|
float yRot,
|
||||||
|
boolean small,
|
||||||
|
LegacyChatFormatter glowColor) {
|
||||||
|
this.itemId = itemId;
|
||||||
|
this.glowColor = glowColor;
|
||||||
|
this.scale = scale;
|
||||||
|
this.position = position;
|
||||||
|
this.xRot = xRot;
|
||||||
|
this.yRot = yRot;
|
||||||
|
this.small = small;
|
||||||
|
this.lazyMetadataPacket = player -> {
|
||||||
|
List<Object> dataValues = new ArrayList<>(2);
|
||||||
|
if (glowColor != null) {
|
||||||
|
BaseEntityData.SharedFlags.addEntityData((byte) 0x60, dataValues);
|
||||||
|
} else {
|
||||||
|
BaseEntityData.SharedFlags.addEntityData((byte) 0x20, dataValues);
|
||||||
|
}
|
||||||
|
if (small) {
|
||||||
|
ArmorStandData.ArmorStandFlags.addEntityData((byte) 0x01, dataValues);
|
||||||
|
}
|
||||||
|
return dataValues;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public LegacyChatFormatter glowColor() {
|
||||||
|
return this.glowColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArmorStandBlockEntityElement create(World world, BlockPos pos) {
|
||||||
|
return new ArmorStandBlockEntityElement(this, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArmorStandBlockEntityElement create(World world, BlockPos pos, ArmorStandBlockEntityElement previous) {
|
||||||
|
if (previous.config.scale != scale || previous.config.glowColor != glowColor) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new ArmorStandBlockEntityElement(this, pos, previous.entityId,
|
||||||
|
previous.config.yRot != this.yRot ||
|
||||||
|
previous.config.xRot != this.xRot ||
|
||||||
|
!previous.config.position.equals(this.position)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArmorStandBlockEntityElement createExact(World world, BlockPos pos, ArmorStandBlockEntityElement previous) {
|
||||||
|
if (!previous.config.isSamePosition(this)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new ArmorStandBlockEntityElement(this, pos, previous.entityId, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<ArmorStandBlockEntityElement> elementClass() {
|
||||||
|
return ArmorStandBlockEntityElement.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Item<?> item(Player player) {
|
||||||
|
Item<ItemStack> wrappedItem = BukkitItemManager.instance().createWrappedItem(this.itemId, player);
|
||||||
|
return wrappedItem == null ? BukkitItemManager.instance().createWrappedItem(ItemKeys.BARRIER, player) : wrappedItem ;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Key itemId() {
|
||||||
|
return this.itemId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float scale() {
|
||||||
|
return this.scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f position() {
|
||||||
|
return this.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float yRot() {
|
||||||
|
return this.yRot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float xRot() {
|
||||||
|
return this.xRot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean small() {
|
||||||
|
return this.small;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Object> metadataValues(Player player) {
|
||||||
|
return this.lazyMetadataPacket.apply(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSamePosition(ArmorStandBlockEntityElementConfig that) {
|
||||||
|
return Float.compare(xRot, that.xRot) == 0 &&
|
||||||
|
Float.compare(yRot, that.yRot) == 0 &&
|
||||||
|
Objects.equal(position, that.position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Factory implements BlockEntityElementConfigFactory<ArmorStandBlockEntityElement> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArmorStandBlockEntityElementConfig create(Map<String, Object> arguments) {
|
||||||
|
return new ArmorStandBlockEntityElementConfig(
|
||||||
|
Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.block.state.entity_renderer.armor_stand.missing_item")),
|
||||||
|
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", 1f), "scale"),
|
||||||
|
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"),
|
||||||
|
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 0f), "pitch"),
|
||||||
|
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"),
|
||||||
|
ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("small", false), "small"),
|
||||||
|
ResourceConfigUtils.getAsEnum(arguments.get("glow-color"), LegacyChatFormatter.class, null)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ public class BukkitBlockEntityElementConfigs extends BlockEntityElementConfigs {
|
|||||||
register(ITEM_DISPLAY, ItemDisplayBlockEntityElementConfig.FACTORY);
|
register(ITEM_DISPLAY, ItemDisplayBlockEntityElementConfig.FACTORY);
|
||||||
register(TEXT_DISPLAY, TextDisplayBlockEntityElementConfig.FACTORY);
|
register(TEXT_DISPLAY, TextDisplayBlockEntityElementConfig.FACTORY);
|
||||||
register(ITEM, ItemBlockEntityElementConfig.FACTORY);
|
register(ITEM, ItemBlockEntityElementConfig.FACTORY);
|
||||||
|
register(ARMOR_STAND, ArmorStandBlockEntityElementConfig.FACTORY);
|
||||||
}
|
}
|
||||||
|
|
||||||
private BukkitBlockEntityElementConfigs() {}
|
private BukkitBlockEntityElementConfigs() {}
|
||||||
|
|||||||
@@ -2,35 +2,41 @@ package net.momirealms.craftengine.bukkit.block.entity.renderer.element;
|
|||||||
|
|
||||||
import net.momirealms.craftengine.bukkit.entity.data.ItemEntityData;
|
import net.momirealms.craftengine.bukkit.entity.data.ItemEntityData;
|
||||||
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||||
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement;
|
|
||||||
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig;
|
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig;
|
||||||
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory;
|
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory;
|
||||||
import net.momirealms.craftengine.core.entity.player.Player;
|
import net.momirealms.craftengine.core.entity.player.Player;
|
||||||
import net.momirealms.craftengine.core.item.Item;
|
import net.momirealms.craftengine.core.item.Item;
|
||||||
|
import net.momirealms.craftengine.core.item.ItemKeys;
|
||||||
import net.momirealms.craftengine.core.util.Key;
|
import net.momirealms.craftengine.core.util.Key;
|
||||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||||
import net.momirealms.craftengine.core.world.BlockPos;
|
import net.momirealms.craftengine.core.world.BlockPos;
|
||||||
import net.momirealms.craftengine.core.world.World;
|
import net.momirealms.craftengine.core.world.World;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class ItemBlockEntityElementConfig implements BlockEntityElementConfig<ItemBlockEntityElement> {
|
public class ItemBlockEntityElementConfig implements BlockEntityElementConfig<ItemBlockEntityElement> {
|
||||||
public static final Factory FACTORY = new Factory();
|
public static final Factory FACTORY = new Factory();
|
||||||
private final Function<Player, List<Object>> lazyMetadataPacket;
|
public final Function<Player, List<Object>> lazyMetadataPacket;
|
||||||
private final Function<Player, Item<?>> item;
|
public final Key itemId;
|
||||||
private final Vector3f position;
|
public final Vector3f position;
|
||||||
|
|
||||||
public ItemBlockEntityElementConfig(Function<Player, Item<?>> item, Vector3f position) {
|
public ItemBlockEntityElementConfig(Key itemId, Vector3f position) {
|
||||||
this.item = item;
|
this.itemId = itemId;
|
||||||
this.position = position;
|
this.position = position;
|
||||||
this.lazyMetadataPacket = player -> {
|
this.lazyMetadataPacket = player -> {
|
||||||
List<Object> dataValues = new ArrayList<>();
|
List<Object> dataValues = new ArrayList<>();
|
||||||
ItemEntityData.Item.addEntityDataIfNotDefaultValue(item.apply(player).getLiteralObject(), dataValues);
|
Item<ItemStack> wrappedItem = BukkitItemManager.instance().createWrappedItem(itemId, player);
|
||||||
ItemEntityData.NoGravity.addEntityDataIfNotDefaultValue(true, dataValues);
|
if (wrappedItem == null) {
|
||||||
|
wrappedItem = Objects.requireNonNull(BukkitItemManager.instance().createWrappedItem(ItemKeys.BARRIER, player));
|
||||||
|
}
|
||||||
|
ItemEntityData.Item.addEntityData(wrappedItem.getLiteralObject(), dataValues);
|
||||||
|
ItemEntityData.NoGravity.addEntityData(true, dataValues);
|
||||||
return dataValues;
|
return dataValues;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -45,6 +51,14 @@ public class ItemBlockEntityElementConfig implements BlockEntityElementConfig<It
|
|||||||
return new ItemBlockEntityElement(this, pos, previous.entityId1, previous.entityId2, !previous.config.position.equals(this.position));
|
return new ItemBlockEntityElement(this, pos, previous.entityId1, previous.entityId2, !previous.config.position.equals(this.position));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemBlockEntityElement createExact(World world, BlockPos pos, ItemBlockEntityElement previous) {
|
||||||
|
if (!previous.config.isSamePosition(this)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new ItemBlockEntityElement(this, pos, previous.entityId1, previous.entityId2, false);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<ItemBlockEntityElement> elementClass() {
|
public Class<ItemBlockEntityElement> elementClass() {
|
||||||
return ItemBlockEntityElement.class;
|
return ItemBlockEntityElement.class;
|
||||||
@@ -54,22 +68,24 @@ public class ItemBlockEntityElementConfig implements BlockEntityElementConfig<It
|
|||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Item<?> item(Player player) {
|
public Key itemId() {
|
||||||
return this.item.apply(player);
|
return itemId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Object> metadataValues(Player player) {
|
public List<Object> metadataValues(Player player) {
|
||||||
return this.lazyMetadataPacket.apply(player);
|
return this.lazyMetadataPacket.apply(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Factory implements BlockEntityElementConfigFactory {
|
public boolean isSamePosition(ItemBlockEntityElementConfig that) {
|
||||||
|
return this.position.equals(that.position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Factory implements BlockEntityElementConfigFactory<ItemBlockEntityElement> {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
@Override
|
||||||
public <E extends BlockEntityElement> BlockEntityElementConfig<E> create(Map<String, Object> arguments) {
|
public ItemBlockEntityElementConfig create(Map<String, Object> arguments) {
|
||||||
Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.block.state.entity_renderer.item_display.missing_item"));
|
return new ItemBlockEntityElementConfig(
|
||||||
return (BlockEntityElementConfig<E>) new ItemBlockEntityElementConfig(
|
Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.block.state.entity_renderer.item.missing_item")),
|
||||||
player -> BukkitItemManager.instance().createWrappedItem(itemId, player),
|
|
||||||
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position")
|
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +1,51 @@
|
|||||||
package net.momirealms.craftengine.bukkit.block.entity.renderer.element;
|
package net.momirealms.craftengine.bukkit.block.entity.renderer.element;
|
||||||
|
|
||||||
|
import com.google.common.base.Objects;
|
||||||
import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData;
|
import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData;
|
||||||
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||||
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement;
|
|
||||||
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig;
|
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig;
|
||||||
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory;
|
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory;
|
||||||
import net.momirealms.craftengine.core.entity.Billboard;
|
import net.momirealms.craftengine.core.entity.display.Billboard;
|
||||||
import net.momirealms.craftengine.core.entity.ItemDisplayContext;
|
import net.momirealms.craftengine.core.entity.display.ItemDisplayContext;
|
||||||
import net.momirealms.craftengine.core.entity.player.Player;
|
import net.momirealms.craftengine.core.entity.player.Player;
|
||||||
import net.momirealms.craftengine.core.item.Item;
|
import net.momirealms.craftengine.core.item.Item;
|
||||||
|
import net.momirealms.craftengine.core.item.ItemKeys;
|
||||||
|
import net.momirealms.craftengine.core.util.Color;
|
||||||
import net.momirealms.craftengine.core.util.Key;
|
import net.momirealms.craftengine.core.util.Key;
|
||||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||||
import net.momirealms.craftengine.core.world.BlockPos;
|
import net.momirealms.craftengine.core.world.BlockPos;
|
||||||
import net.momirealms.craftengine.core.world.World;
|
import net.momirealms.craftengine.core.world.World;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.joml.Quaternionf;
|
import org.joml.Quaternionf;
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementConfig<ItemDisplayBlockEntityElement> {
|
public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementConfig<ItemDisplayBlockEntityElement> {
|
||||||
public static final Factory FACTORY = new Factory();
|
public static final Factory FACTORY = new Factory();
|
||||||
private final Function<Player, List<Object>> lazyMetadataPacket;
|
public final Function<Player, List<Object>> lazyMetadataPacket;
|
||||||
private final Function<Player, Item<?>> item;
|
public final Key itemId;
|
||||||
private final Vector3f scale;
|
public final Vector3f scale;
|
||||||
private final Vector3f position;
|
public final Vector3f position;
|
||||||
private final Vector3f translation;
|
public final Vector3f translation;
|
||||||
private final float xRot;
|
public final float xRot;
|
||||||
private final float yRot;
|
public final float yRot;
|
||||||
private final Quaternionf rotation;
|
public final Quaternionf rotation;
|
||||||
private final ItemDisplayContext displayContext;
|
public final ItemDisplayContext displayContext;
|
||||||
private final Billboard billboard;
|
public final Billboard billboard;
|
||||||
private final float shadowRadius;
|
public final float shadowRadius;
|
||||||
private final float shadowStrength;
|
public final float shadowStrength;
|
||||||
|
public final Color glowColor;
|
||||||
|
public final int blockLight;
|
||||||
|
public final int skyLight;
|
||||||
|
public final float viewRange;
|
||||||
|
|
||||||
public ItemDisplayBlockEntityElementConfig(Function<Player, Item<?>> item,
|
public ItemDisplayBlockEntityElementConfig(Key itemId,
|
||||||
Vector3f scale,
|
Vector3f scale,
|
||||||
Vector3f position,
|
Vector3f position,
|
||||||
Vector3f translation,
|
Vector3f translation,
|
||||||
@@ -47,8 +55,12 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo
|
|||||||
ItemDisplayContext displayContext,
|
ItemDisplayContext displayContext,
|
||||||
Billboard billboard,
|
Billboard billboard,
|
||||||
float shadowRadius,
|
float shadowRadius,
|
||||||
float shadowStrength) {
|
float shadowStrength,
|
||||||
this.item = item;
|
@Nullable Color glowColor,
|
||||||
|
int blockLight,
|
||||||
|
int skyLight,
|
||||||
|
float viewRange) {
|
||||||
|
this.itemId = itemId;
|
||||||
this.scale = scale;
|
this.scale = scale;
|
||||||
this.position = position;
|
this.position = position;
|
||||||
this.translation = translation;
|
this.translation = translation;
|
||||||
@@ -59,16 +71,37 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo
|
|||||||
this.billboard = billboard;
|
this.billboard = billboard;
|
||||||
this.shadowRadius = shadowRadius;
|
this.shadowRadius = shadowRadius;
|
||||||
this.shadowStrength = shadowStrength;
|
this.shadowStrength = shadowStrength;
|
||||||
|
this.glowColor = glowColor;
|
||||||
|
this.blockLight = blockLight;
|
||||||
|
this.skyLight = skyLight;
|
||||||
|
this.viewRange = viewRange;
|
||||||
this.lazyMetadataPacket = player -> {
|
this.lazyMetadataPacket = player -> {
|
||||||
List<Object> dataValues = new ArrayList<>();
|
List<Object> dataValues = new ArrayList<>();
|
||||||
ItemDisplayEntityData.DisplayedItem.addEntityDataIfNotDefaultValue(item.apply(player).getLiteralObject(), dataValues);
|
if (glowColor != null) {
|
||||||
ItemDisplayEntityData.Scale.addEntityDataIfNotDefaultValue(this.scale, dataValues);
|
ItemDisplayEntityData.SharedFlags.addEntityData((byte) 0x40, dataValues);
|
||||||
ItemDisplayEntityData.RotationLeft.addEntityDataIfNotDefaultValue(this.rotation, dataValues);
|
ItemDisplayEntityData.GlowColorOverride.addEntityData(glowColor.color(), dataValues);
|
||||||
ItemDisplayEntityData.BillboardConstraints.addEntityDataIfNotDefaultValue(this.billboard.id(), dataValues);
|
} else {
|
||||||
ItemDisplayEntityData.Translation.addEntityDataIfNotDefaultValue(this.translation, dataValues);
|
ItemDisplayEntityData.SharedFlags.addEntityData((byte) 0x0, dataValues);
|
||||||
ItemDisplayEntityData.DisplayType.addEntityDataIfNotDefaultValue(this.displayContext.id(), dataValues);
|
ItemDisplayEntityData.GlowColorOverride.addEntityData(-1, dataValues);
|
||||||
ItemDisplayEntityData.ShadowRadius.addEntityDataIfNotDefaultValue(this.shadowRadius, dataValues);
|
}
|
||||||
ItemDisplayEntityData.ShadowStrength.addEntityDataIfNotDefaultValue(this.shadowStrength, dataValues);
|
Item<ItemStack> wrappedItem = BukkitItemManager.instance().createWrappedItem(itemId, player);
|
||||||
|
if (wrappedItem == null) {
|
||||||
|
wrappedItem = java.util.Objects.requireNonNull(BukkitItemManager.instance().createWrappedItem(ItemKeys.BARRIER, player));
|
||||||
|
}
|
||||||
|
ItemDisplayEntityData.DisplayedItem.addEntityData(wrappedItem.getLiteralObject(), dataValues);
|
||||||
|
ItemDisplayEntityData.Scale.addEntityData(this.scale, dataValues);
|
||||||
|
ItemDisplayEntityData.RotationLeft.addEntityData(this.rotation, dataValues);
|
||||||
|
ItemDisplayEntityData.BillboardConstraints.addEntityData(this.billboard.id(), dataValues);
|
||||||
|
ItemDisplayEntityData.Translation.addEntityData(this.translation, dataValues);
|
||||||
|
ItemDisplayEntityData.DisplayType.addEntityData(this.displayContext.id(), dataValues);
|
||||||
|
ItemDisplayEntityData.ShadowRadius.addEntityData(this.shadowRadius, dataValues);
|
||||||
|
ItemDisplayEntityData.ShadowStrength.addEntityData(this.shadowStrength, dataValues);
|
||||||
|
if (this.blockLight != -1 && this.skyLight != -1) {
|
||||||
|
ItemDisplayEntityData.BrightnessOverride.addEntityData(this.blockLight << 4 | this.skyLight << 20, dataValues);
|
||||||
|
} else {
|
||||||
|
ItemDisplayEntityData.BrightnessOverride.addEntityData(-1, dataValues);
|
||||||
|
}
|
||||||
|
ItemDisplayEntityData.ViewRange.addEntityData((float) (this.viewRange * player.displayEntityViewDistance()), dataValues);
|
||||||
return dataValues;
|
return dataValues;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -80,14 +113,6 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ItemDisplayBlockEntityElement create(World world, BlockPos pos, ItemDisplayBlockEntityElement previous) {
|
public ItemDisplayBlockEntityElement create(World world, BlockPos pos, ItemDisplayBlockEntityElement previous) {
|
||||||
Quaternionf previousRotation = previous.config.rotation;
|
|
||||||
if (previousRotation.x != 0 || previousRotation.y != 0 || previousRotation.z != 0 || previousRotation.w != 1) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Vector3f translation = previous.config.translation;
|
|
||||||
if (translation.x != 0 || translation.y != 0 || translation.z != 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return new ItemDisplayBlockEntityElement(this, pos, previous.entityId,
|
return new ItemDisplayBlockEntityElement(this, pos, previous.entityId,
|
||||||
previous.config.yRot != this.yRot ||
|
previous.config.yRot != this.yRot ||
|
||||||
previous.config.xRot != this.xRot ||
|
previous.config.xRot != this.xRot ||
|
||||||
@@ -95,13 +120,25 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemDisplayBlockEntityElement createExact(World world, BlockPos pos, ItemDisplayBlockEntityElement previous) {
|
||||||
|
if (!previous.config.isSamePosition(this)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new ItemDisplayBlockEntityElement(this, pos, previous.entityId, false);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<ItemDisplayBlockEntityElement> elementClass() {
|
public Class<ItemDisplayBlockEntityElement> elementClass() {
|
||||||
return ItemDisplayBlockEntityElement.class;
|
return ItemDisplayBlockEntityElement.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Item<?> item(Player player) {
|
public Color glowColor() {
|
||||||
return this.item.apply(player);
|
return glowColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Key itemId() {
|
||||||
|
return itemId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector3f scale() {
|
public Vector3f scale() {
|
||||||
@@ -148,24 +185,35 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo
|
|||||||
return this.lazyMetadataPacket.apply(player);
|
return this.lazyMetadataPacket.apply(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Factory implements BlockEntityElementConfigFactory {
|
public boolean isSamePosition(ItemDisplayBlockEntityElementConfig that) {
|
||||||
|
return Float.compare(xRot, that.xRot) == 0 &&
|
||||||
|
Float.compare(yRot, that.yRot) == 0 &&
|
||||||
|
Objects.equal(position, that.position) &&
|
||||||
|
Objects.equal(translation, that.translation) &&
|
||||||
|
Objects.equal(rotation, that.rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Factory implements BlockEntityElementConfigFactory<ItemDisplayBlockEntityElement> {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
@Override
|
||||||
public <E extends BlockEntityElement> BlockEntityElementConfig<E> create(Map<String, Object> arguments) {
|
public ItemDisplayBlockEntityElementConfig create(Map<String, Object> arguments) {
|
||||||
Key itemId = Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.block.state.entity_renderer.item_display.missing_item"));
|
Map<String, Object> brightness = ResourceConfigUtils.getAsMap(arguments.getOrDefault("brightness", Map.of()), "brightness");
|
||||||
return (BlockEntityElementConfig<E>) new ItemDisplayBlockEntityElementConfig(
|
return new ItemDisplayBlockEntityElementConfig(
|
||||||
player -> BukkitItemManager.instance().createWrappedItem(itemId, player),
|
Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.block.state.entity_renderer.item_display.missing_item")),
|
||||||
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("scale", 1f), "scale"),
|
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("scale", 1f), "scale"),
|
||||||
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"),
|
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"),
|
||||||
ResourceConfigUtils.getAsVector3f(arguments.get("translation"), "translation"),
|
ResourceConfigUtils.getAsVector3f(arguments.get("translation"), "translation"),
|
||||||
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 0f), "pitch"),
|
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 0f), "pitch"),
|
||||||
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"),
|
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"),
|
||||||
ResourceConfigUtils.getAsQuaternionf(arguments.getOrDefault("rotation", 0f), "rotation"),
|
ResourceConfigUtils.getAsQuaternionf(arguments.getOrDefault("rotation", 0f), "rotation"),
|
||||||
ItemDisplayContext.valueOf(arguments.getOrDefault("display-context", "none").toString().toUpperCase(Locale.ROOT)),
|
ResourceConfigUtils.getAsEnum(ResourceConfigUtils.get(arguments, "display-context", "display-transform"), ItemDisplayContext.class, ItemDisplayContext.NONE),
|
||||||
Billboard.valueOf(arguments.getOrDefault("billboard", "fixed").toString().toUpperCase(Locale.ROOT)),
|
ResourceConfigUtils.getAsEnum(arguments.get("billboard"), Billboard.class, Billboard.FIXED),
|
||||||
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-radius", 0f), "shadow-radius"),
|
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-radius", 0f), "shadow-radius"),
|
||||||
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-strength", 1f), "shadow-strength")
|
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-strength", 1f), "shadow-strength"),
|
||||||
|
Optional.ofNullable(arguments.get("glow-color")).map(it -> Color.fromStrings(it.toString().split(","))).orElse(null),
|
||||||
|
ResourceConfigUtils.getAsInt(brightness.getOrDefault("block-light", -1), "block-light"),
|
||||||
|
ResourceConfigUtils.getAsInt(brightness.getOrDefault("sky-light", -1), "sky-light"),
|
||||||
|
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("view-range", 1f), "view-range")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +1,49 @@
|
|||||||
package net.momirealms.craftengine.bukkit.block.entity.renderer.element;
|
package net.momirealms.craftengine.bukkit.block.entity.renderer.element;
|
||||||
|
|
||||||
|
import com.google.common.base.Objects;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.momirealms.craftengine.bukkit.entity.data.TextDisplayEntityData;
|
import net.momirealms.craftengine.bukkit.entity.data.TextDisplayEntityData;
|
||||||
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
|
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
|
||||||
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElement;
|
|
||||||
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig;
|
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfig;
|
||||||
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory;
|
import net.momirealms.craftengine.core.block.entity.render.element.BlockEntityElementConfigFactory;
|
||||||
import net.momirealms.craftengine.core.entity.Billboard;
|
import net.momirealms.craftengine.core.entity.display.Billboard;
|
||||||
|
import net.momirealms.craftengine.core.entity.display.TextDisplayAlignment;
|
||||||
import net.momirealms.craftengine.core.entity.player.Player;
|
import net.momirealms.craftengine.core.entity.player.Player;
|
||||||
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
|
import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext;
|
||||||
import net.momirealms.craftengine.core.util.AdventureHelper;
|
import net.momirealms.craftengine.core.util.AdventureHelper;
|
||||||
|
import net.momirealms.craftengine.core.util.Color;
|
||||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||||
import net.momirealms.craftengine.core.world.BlockPos;
|
import net.momirealms.craftengine.core.world.BlockPos;
|
||||||
import net.momirealms.craftengine.core.world.World;
|
import net.momirealms.craftengine.core.world.World;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.joml.Quaternionf;
|
import org.joml.Quaternionf;
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class TextDisplayBlockEntityElementConfig implements BlockEntityElementConfig<TextDisplayBlockEntityElement> {
|
public class TextDisplayBlockEntityElementConfig implements BlockEntityElementConfig<TextDisplayBlockEntityElement> {
|
||||||
public static final Factory FACTORY = new Factory();
|
public static final Factory FACTORY = new Factory();
|
||||||
private final Function<Player, List<Object>> lazyMetadataPacket;
|
public final Function<Player, List<Object>> lazyMetadataPacket;
|
||||||
private final String text;
|
public final String text;
|
||||||
private final Vector3f scale;
|
public final Vector3f scale;
|
||||||
private final Vector3f position;
|
public final Vector3f position;
|
||||||
private final Vector3f translation;
|
public final Vector3f translation;
|
||||||
private final float xRot;
|
public final float xRot;
|
||||||
private final float yRot;
|
public final float yRot;
|
||||||
private final Quaternionf rotation;
|
public final Quaternionf rotation;
|
||||||
private final Billboard billboard;
|
public final Billboard billboard;
|
||||||
|
public final Color glowColor;
|
||||||
|
public final int blockLight;
|
||||||
|
public final int skyLight;
|
||||||
|
public final float viewRange;
|
||||||
|
public final int lineWidth;
|
||||||
|
public final int backgroundColor;
|
||||||
|
public final byte opacity;
|
||||||
|
public final boolean hasShadow;
|
||||||
|
public final boolean isSeeThrough;
|
||||||
|
public final boolean useDefaultBackgroundColor;
|
||||||
|
public final TextDisplayAlignment alignment;
|
||||||
|
|
||||||
public TextDisplayBlockEntityElementConfig(String text,
|
public TextDisplayBlockEntityElementConfig(String text,
|
||||||
Vector3f scale,
|
Vector3f scale,
|
||||||
@@ -41,7 +52,18 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo
|
|||||||
float xRot,
|
float xRot,
|
||||||
float yRot,
|
float yRot,
|
||||||
Quaternionf rotation,
|
Quaternionf rotation,
|
||||||
Billboard billboard) {
|
Billboard billboard,
|
||||||
|
@Nullable Color glowColor,
|
||||||
|
int blockLight,
|
||||||
|
int skyLight,
|
||||||
|
float viewRange,
|
||||||
|
int lineWidth,
|
||||||
|
int backgroundColor,
|
||||||
|
byte opacity,
|
||||||
|
boolean hasShadow,
|
||||||
|
boolean isSeeThrough,
|
||||||
|
boolean useDefaultBackgroundColor,
|
||||||
|
TextDisplayAlignment alignment) {
|
||||||
this.text = text;
|
this.text = text;
|
||||||
this.scale = scale;
|
this.scale = scale;
|
||||||
this.position = position;
|
this.position = position;
|
||||||
@@ -50,13 +72,41 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo
|
|||||||
this.yRot = yRot;
|
this.yRot = yRot;
|
||||||
this.rotation = rotation;
|
this.rotation = rotation;
|
||||||
this.billboard = billboard;
|
this.billboard = billboard;
|
||||||
|
this.glowColor = glowColor;
|
||||||
|
this.blockLight = blockLight;
|
||||||
|
this.skyLight = skyLight;
|
||||||
|
this.viewRange = viewRange;
|
||||||
|
this.lineWidth = lineWidth;
|
||||||
|
this.backgroundColor = backgroundColor;
|
||||||
|
this.opacity = opacity;
|
||||||
|
this.hasShadow = hasShadow;
|
||||||
|
this.useDefaultBackgroundColor = useDefaultBackgroundColor;
|
||||||
|
this.alignment = alignment;
|
||||||
|
this.isSeeThrough = isSeeThrough;
|
||||||
this.lazyMetadataPacket = player -> {
|
this.lazyMetadataPacket = player -> {
|
||||||
List<Object> dataValues = new ArrayList<>();
|
List<Object> dataValues = new ArrayList<>();
|
||||||
TextDisplayEntityData.Text.addEntityDataIfNotDefaultValue(ComponentUtils.adventureToMinecraft(text(player)), dataValues);
|
if (glowColor != null) {
|
||||||
TextDisplayEntityData.Scale.addEntityDataIfNotDefaultValue(this.scale, dataValues);
|
TextDisplayEntityData.SharedFlags.addEntityData((byte) 0x40, dataValues);
|
||||||
TextDisplayEntityData.RotationLeft.addEntityDataIfNotDefaultValue(this.rotation, dataValues);
|
TextDisplayEntityData.GlowColorOverride.addEntityData(glowColor.color(), dataValues);
|
||||||
TextDisplayEntityData.BillboardConstraints.addEntityDataIfNotDefaultValue(this.billboard.id(), dataValues);
|
} else {
|
||||||
TextDisplayEntityData.Translation.addEntityDataIfNotDefaultValue(this.translation, dataValues);
|
TextDisplayEntityData.SharedFlags.addEntityData((byte) 0x0, dataValues);
|
||||||
|
TextDisplayEntityData.GlowColorOverride.addEntityData(-1, dataValues);
|
||||||
|
}
|
||||||
|
TextDisplayEntityData.Text.addEntityData(ComponentUtils.adventureToMinecraft(text(player)), dataValues);
|
||||||
|
TextDisplayEntityData.Scale.addEntityData(this.scale, dataValues);
|
||||||
|
TextDisplayEntityData.RotationLeft.addEntityData(this.rotation, dataValues);
|
||||||
|
TextDisplayEntityData.BillboardConstraints.addEntityData(this.billboard.id(), dataValues);
|
||||||
|
TextDisplayEntityData.Translation.addEntityData(this.translation, dataValues);
|
||||||
|
TextDisplayEntityData.LineWidth.addEntityData(this.lineWidth, dataValues);
|
||||||
|
TextDisplayEntityData.BackgroundColor.addEntityData(this.backgroundColor, dataValues);
|
||||||
|
TextDisplayEntityData.TextOpacity.addEntityData(this.opacity, dataValues);
|
||||||
|
TextDisplayEntityData.TextDisplayMasks.addEntityData(TextDisplayEntityData.encodeMask(this.hasShadow, this.isSeeThrough, this.useDefaultBackgroundColor, this.alignment), dataValues);
|
||||||
|
if (this.blockLight != -1 && this.skyLight != -1) {
|
||||||
|
TextDisplayEntityData.BrightnessOverride.addEntityData(this.blockLight << 4 | this.skyLight << 20, dataValues);
|
||||||
|
} else {
|
||||||
|
TextDisplayEntityData.BrightnessOverride.addEntityData(-1, dataValues);
|
||||||
|
}
|
||||||
|
TextDisplayEntityData.ViewRange.addEntityData((float) (this.viewRange * player.displayEntityViewDistance()), dataValues);
|
||||||
return dataValues;
|
return dataValues;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -68,14 +118,6 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TextDisplayBlockEntityElement create(World world, BlockPos pos, TextDisplayBlockEntityElement previous) {
|
public TextDisplayBlockEntityElement create(World world, BlockPos pos, TextDisplayBlockEntityElement previous) {
|
||||||
Quaternionf previousRotation = previous.config.rotation;
|
|
||||||
if (previousRotation.x != 0 || previousRotation.y != 0 || previousRotation.z != 0 || previousRotation.w != 1) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Vector3f translation = previous.config.translation;
|
|
||||||
if (translation.x != 0 || translation.y != 0 || translation.z != 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return new TextDisplayBlockEntityElement(this, pos, previous.entityId,
|
return new TextDisplayBlockEntityElement(this, pos, previous.entityId,
|
||||||
previous.config.yRot != this.yRot ||
|
previous.config.yRot != this.yRot ||
|
||||||
previous.config.xRot != this.xRot ||
|
previous.config.xRot != this.xRot ||
|
||||||
@@ -83,13 +125,25 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TextDisplayBlockEntityElement createExact(World world, BlockPos pos, TextDisplayBlockEntityElement previous) {
|
||||||
|
if (!previous.config.isSamePosition(this)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new TextDisplayBlockEntityElement(this, pos, previous.entityId, false);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<TextDisplayBlockEntityElement> elementClass() {
|
public Class<TextDisplayBlockEntityElement> elementClass() {
|
||||||
return TextDisplayBlockEntityElement.class;
|
return TextDisplayBlockEntityElement.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String text() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
public Component text(Player player) {
|
public Component text(Player player) {
|
||||||
return AdventureHelper.miniMessage().deserialize(this.text, PlayerOptionalContext.of(player).tagResolvers());
|
return AdventureHelper.miniMessage().deserialize(this.text, NetworkTextReplaceContext.of(player).tagResolvers());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector3f scale() {
|
public Vector3f scale() {
|
||||||
@@ -124,13 +178,21 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo
|
|||||||
return this.lazyMetadataPacket.apply(player);
|
return this.lazyMetadataPacket.apply(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Factory implements BlockEntityElementConfigFactory {
|
public boolean isSamePosition(TextDisplayBlockEntityElementConfig that) {
|
||||||
|
return Float.compare(xRot, that.xRot) == 0 &&
|
||||||
|
Float.compare(yRot, that.yRot) == 0 &&
|
||||||
|
Objects.equal(position, that.position) &&
|
||||||
|
Objects.equal(translation, that.translation) &&
|
||||||
|
Objects.equal(rotation, that.rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Factory implements BlockEntityElementConfigFactory<TextDisplayBlockEntityElement> {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
@Override
|
||||||
public <E extends BlockEntityElement> BlockEntityElementConfig<E> create(Map<String, Object> arguments) {
|
public TextDisplayBlockEntityElementConfig create(Map<String, Object> arguments) {
|
||||||
String text = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("text"), "warning.config.block.state.entity_renderer.text_display.missing_text");
|
String text = ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("text"), "warning.config.block.state.entity_renderer.text_display.missing_text");
|
||||||
return (BlockEntityElementConfig<E>) new TextDisplayBlockEntityElementConfig(
|
Map<String, Object> brightness = ResourceConfigUtils.getAsMap(arguments.getOrDefault("brightness", Map.of()), "brightness");
|
||||||
|
return new TextDisplayBlockEntityElementConfig(
|
||||||
text,
|
text,
|
||||||
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("scale", 1f), "scale"),
|
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("scale", 1f), "scale"),
|
||||||
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"),
|
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0.5f), "position"),
|
||||||
@@ -138,7 +200,18 @@ public class TextDisplayBlockEntityElementConfig implements BlockEntityElementCo
|
|||||||
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 0f), "pitch"),
|
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 0f), "pitch"),
|
||||||
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"),
|
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"),
|
||||||
ResourceConfigUtils.getAsQuaternionf(arguments.getOrDefault("rotation", 0f), "rotation"),
|
ResourceConfigUtils.getAsQuaternionf(arguments.getOrDefault("rotation", 0f), "rotation"),
|
||||||
Billboard.valueOf(arguments.getOrDefault("billboard", "fixed").toString().toUpperCase(Locale.ROOT))
|
Billboard.valueOf(arguments.getOrDefault("billboard", "fixed").toString().toUpperCase(Locale.ROOT)),
|
||||||
|
Optional.ofNullable(arguments.get("glow-color")).map(it -> Color.fromStrings(it.toString().split(","))).orElse(null),
|
||||||
|
ResourceConfigUtils.getAsInt(brightness.getOrDefault("block-light", -1), "block-light"),
|
||||||
|
ResourceConfigUtils.getAsInt(brightness.getOrDefault("sky-light", -1), "sky-light"),
|
||||||
|
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("view-range", 1f), "view-range"),
|
||||||
|
ResourceConfigUtils.getAsInt(arguments.getOrDefault("line-width", 200), "line-width"),
|
||||||
|
ResourceConfigUtils.getOrDefault(arguments.get("background-color"), o -> Color.fromStrings(o.toString().split(",")).color(), 0x40000000),
|
||||||
|
(byte) ResourceConfigUtils.getAsInt(arguments.getOrDefault("text-opacity", -1), "text-opacity"),
|
||||||
|
ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("has-shadow", false), "has-shadow"),
|
||||||
|
ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("is-see-through", false), "is-see-through"),
|
||||||
|
ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("use-default-background-color", false), "use-default-background-color"),
|
||||||
|
ResourceConfigUtils.getAsEnum(arguments.get("alignment"), TextDisplayAlignment.class, TextDisplayAlignment.CENTER)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,14 @@ package net.momirealms.craftengine.bukkit.entity;
|
|||||||
|
|
||||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||||
import net.momirealms.craftengine.bukkit.util.EntityUtils;
|
import net.momirealms.craftengine.bukkit.util.EntityUtils;
|
||||||
|
import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
||||||
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
|
import net.momirealms.craftengine.bukkit.world.BukkitWorld;
|
||||||
import net.momirealms.craftengine.core.entity.AbstractEntity;
|
import net.momirealms.craftengine.core.entity.AbstractEntity;
|
||||||
import net.momirealms.craftengine.core.entity.data.EntityData;
|
import net.momirealms.craftengine.core.entity.data.EntityData;
|
||||||
import net.momirealms.craftengine.core.util.Direction;
|
import net.momirealms.craftengine.core.util.Direction;
|
||||||
import net.momirealms.craftengine.core.util.Key;
|
import net.momirealms.craftengine.core.util.Key;
|
||||||
import net.momirealms.craftengine.core.world.World;
|
import net.momirealms.craftengine.core.world.World;
|
||||||
|
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@@ -39,7 +41,12 @@ public class BukkitEntity extends AbstractEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int entityID() {
|
public WorldPosition position() {
|
||||||
|
return LocationUtils.toWorldPosition(platformEntity().getLocation());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int entityId() {
|
||||||
return platformEntity().getEntityId();
|
return platformEntity().getEntityId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,6 +85,11 @@ public class BukkitEntity extends AbstractEntity {
|
|||||||
return EntityUtils.getEntityType(platformEntity());
|
return EntityUtils.getEntityType(platformEntity());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid() {
|
||||||
|
return platformEntity().isValid();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
return platformEntity().getName();
|
return platformEntity().getName();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package net.momirealms.craftengine.bukkit.entity;
|
package net.momirealms.craftengine.bukkit.entity;
|
||||||
|
|
||||||
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||||
import net.momirealms.craftengine.core.entity.ItemEntity;
|
import net.momirealms.craftengine.core.entity.item.ItemEntity;
|
||||||
import org.bukkit.entity.Item;
|
import org.bukkit.entity.Item;
|
||||||
|
|
||||||
public class BukkitItemEntity extends BukkitEntity implements ItemEntity {
|
public class BukkitItemEntity extends BukkitEntity implements ItemEntity {
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.entity.data;
|
||||||
|
|
||||||
|
public class ArmorStandData<T> extends LivingEntityData<T> {
|
||||||
|
public static final ArmorStandData<Byte> ArmorStandFlags = new ArmorStandData<>(ArmorStandData.class, EntityDataValue.Serializers$BYTE, (byte) 0);
|
||||||
|
// rotations
|
||||||
|
|
||||||
|
public ArmorStandData(Class<?> clazz, Object serializer, T defaultValue) {
|
||||||
|
super(clazz, serializer, defaultValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,7 +39,7 @@ public class BukkitEntityData<T> implements EntityData<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object create(Object entityDataAccessor, Object value) {
|
public Object create(Object entityDataAccessor, T value) {
|
||||||
return EntityDataValue.create(entityDataAccessor, value);
|
return EntityDataValue.create(entityDataAccessor, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package net.momirealms.craftengine.bukkit.entity.data;
|
package net.momirealms.craftengine.bukkit.entity.data;
|
||||||
|
|
||||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||||
|
import net.momirealms.craftengine.core.entity.display.TextDisplayAlignment;
|
||||||
|
|
||||||
public class TextDisplayEntityData<T> extends DisplayEntityData<T> {
|
public class TextDisplayEntityData<T> extends DisplayEntityData<T> {
|
||||||
public static final TextDisplayEntityData<Object> Text = new TextDisplayEntityData<>(TextDisplayEntityData.class, EntityDataValue.Serializers$COMPONENT, CoreReflections.instance$Component$empty);
|
public static final TextDisplayEntityData<Object> Text = new TextDisplayEntityData<>(TextDisplayEntityData.class, EntityDataValue.Serializers$COMPONENT, CoreReflections.instance$Component$empty);
|
||||||
@@ -12,4 +13,39 @@ public class TextDisplayEntityData<T> extends DisplayEntityData<T> {
|
|||||||
public TextDisplayEntityData(Class<?> clazz, Object serializer, T defaultValue) {
|
public TextDisplayEntityData(Class<?> clazz, Object serializer, T defaultValue) {
|
||||||
super(clazz, serializer, defaultValue);
|
super(clazz, serializer, defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final int HAS_SHADOW = 0x01;
|
||||||
|
public static final int IS_SEE_THROUGH = 0x02;
|
||||||
|
public static final int USE_DEFAULT_BG_COLOR = 0x04;
|
||||||
|
private static final int LEFT_ALIGNMENT = 0x08; // 8
|
||||||
|
private static final int RIGHT_ALIGNMENT = 0x10; // 16
|
||||||
|
|
||||||
|
public static byte encodeMask(boolean hasShadow, boolean isSeeThrough, boolean useDefaultBackground, TextDisplayAlignment alignment) {
|
||||||
|
int bitMask = 0;
|
||||||
|
|
||||||
|
if (hasShadow) {
|
||||||
|
bitMask |= HAS_SHADOW;
|
||||||
|
}
|
||||||
|
if (isSeeThrough) {
|
||||||
|
bitMask |= IS_SEE_THROUGH;
|
||||||
|
}
|
||||||
|
if (useDefaultBackground) {
|
||||||
|
bitMask |= USE_DEFAULT_BG_COLOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (alignment) {
|
||||||
|
case CENTER: // CENTER
|
||||||
|
break;
|
||||||
|
case LEFT: // LEFT
|
||||||
|
bitMask |= LEFT_ALIGNMENT;
|
||||||
|
break;
|
||||||
|
case RIGHT: // RIGHT
|
||||||
|
bitMask |= RIGHT_ALIGNMENT;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Invalid alignment value");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (byte) bitMask;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public class BukkitCollider implements Collider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int entityId() {
|
public int entityId() {
|
||||||
return this.collisionEntity.getId();
|
return this.collisionEntity.getEntityId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,74 +0,0 @@
|
|||||||
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.Context;
|
|
||||||
import net.momirealms.craftengine.core.plugin.context.event.EventTrigger;
|
|
||||||
import net.momirealms.craftengine.core.plugin.context.function.Function;
|
|
||||||
import net.momirealms.craftengine.core.util.Key;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.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<Context>>> 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<Context>>> 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<Context>>> events) {
|
|
||||||
this.events = events;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,290 +1,181 @@
|
|||||||
package net.momirealms.craftengine.bukkit.entity.furniture;
|
package net.momirealms.craftengine.bukkit.entity.furniture;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap;
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
|
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
|
||||||
import net.momirealms.craftengine.bukkit.entity.BukkitEntity;
|
import net.momirealms.craftengine.bukkit.entity.BukkitEntity;
|
||||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
|
||||||
import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
||||||
import net.momirealms.craftengine.core.entity.furniture.*;
|
import net.momirealms.craftengine.core.entity.furniture.*;
|
||||||
import net.momirealms.craftengine.core.entity.seat.Seat;
|
import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig;
|
||||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||||
import net.momirealms.craftengine.core.util.Key;
|
|
||||||
import net.momirealms.craftengine.core.util.QuaternionUtils;
|
import net.momirealms.craftengine.core.util.QuaternionUtils;
|
||||||
import net.momirealms.craftengine.core.world.WorldPosition;
|
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||||
|
import net.momirealms.craftengine.core.world.collision.AABB;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.ItemDisplay;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.persistence.PersistentDataType;
|
import org.bukkit.persistence.PersistentDataType;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.joml.Quaternionf;
|
import org.joml.Quaternionf;
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
public class BukkitFurniture implements Furniture {
|
@SuppressWarnings("DuplicatedCode")
|
||||||
private final CustomFurniture furniture;
|
public class BukkitFurniture extends Furniture {
|
||||||
private final CustomFurniture.Placement placement;
|
private final WeakReference<ItemDisplay> metaEntity;
|
||||||
private FurnitureExtraData extraData;
|
private Location location;
|
||||||
// location
|
|
||||||
private final Location location;
|
|
||||||
// base entity
|
|
||||||
private final WeakReference<Entity> baseEntity;
|
|
||||||
private final int baseEntityId;
|
|
||||||
// colliders
|
|
||||||
private final Collider[] colliderEntities;
|
|
||||||
// cache
|
|
||||||
private final List<Integer> fakeEntityIds;
|
|
||||||
private final List<Integer> entityIds;
|
|
||||||
private final Map<Integer, BukkitHitBox> hitBoxes = new Int2ObjectArrayMap<>();
|
|
||||||
private final Map<Integer, HitBoxPart> hitBoxParts = new Int2ObjectArrayMap<>();
|
|
||||||
private final boolean minimized;
|
|
||||||
private final boolean hasExternalModel;
|
|
||||||
// cached spawn packet
|
|
||||||
private Object cachedSpawnPacket;
|
|
||||||
private Object cachedMinimizedSpawnPacket;
|
|
||||||
|
|
||||||
public BukkitFurniture(Entity baseEntity,
|
public BukkitFurniture(ItemDisplay metaEntity, CustomFurniture config, FurnitureDataAccessor data) {
|
||||||
CustomFurniture furniture,
|
super(new BukkitEntity(metaEntity), data, config);
|
||||||
FurnitureExtraData extraData) {
|
this.metaEntity = new WeakReference<>(metaEntity);
|
||||||
this.extraData = extraData;
|
this.location = metaEntity.getLocation();
|
||||||
this.baseEntityId = baseEntity.getEntityId();
|
|
||||||
this.location = baseEntity.getLocation();
|
|
||||||
this.baseEntity = new WeakReference<>(baseEntity);
|
|
||||||
this.furniture = furniture;
|
|
||||||
this.minimized = furniture.settings().minimized();
|
|
||||||
this.placement = furniture.getValidPlacement(extraData.anchorType().orElseGet(furniture::getAnyAnchorType));
|
|
||||||
|
|
||||||
List<Integer> fakeEntityIds = new IntArrayList();
|
|
||||||
List<Integer> mainEntityIds = new IntArrayList();
|
|
||||||
mainEntityIds.add(this.baseEntityId);
|
|
||||||
|
|
||||||
// 绑定外部模型
|
|
||||||
Optional<ExternalModel> optionalExternal = placement.externalModel();
|
|
||||||
if (optionalExternal.isPresent()) {
|
|
||||||
try {
|
|
||||||
optionalExternal.get().bindModel(new BukkitEntity(baseEntity));
|
|
||||||
} catch (Exception e) {
|
|
||||||
CraftEngine.instance().logger().warn("Failed to load external model for furniture " + id(), e);
|
|
||||||
}
|
|
||||||
this.hasExternalModel = true;
|
|
||||||
} else {
|
|
||||||
this.hasExternalModel = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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<>(4);
|
|
||||||
WorldPosition position = position();
|
|
||||||
|
|
||||||
|
|
||||||
// 初始化家具的元素
|
|
||||||
for (FurnitureElement element : placement.elements()) {
|
|
||||||
int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet();
|
|
||||||
fakeEntityIds.add(entityId);
|
|
||||||
element.initPackets(this, entityId, conjugated, packet -> {
|
|
||||||
packets.add(packet);
|
|
||||||
if (this.minimized) minimizedPackets.add(packet);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化碰撞箱
|
|
||||||
for (HitBoxConfig hitBoxConfig : this.placement.hitBoxConfigs()) {
|
|
||||||
int[] ids = hitBoxConfig.acquireEntityIds(CoreReflections.instance$Entity$ENTITY_COUNTER::incrementAndGet);
|
|
||||||
List<HitBoxPart> aabbs = new ArrayList<>();
|
|
||||||
|
|
||||||
hitBoxConfig.initPacketsAndColliders(ids, position, conjugated, (packet, canBeMinimized) -> {
|
|
||||||
packets.add(packet);
|
|
||||||
if (this.minimized && !canBeMinimized) {
|
|
||||||
minimizedPackets.add(packet);
|
|
||||||
}
|
|
||||||
}, colliders::add, part -> {
|
|
||||||
this.hitBoxParts.put(part.entityId(), part);
|
|
||||||
aabbs.add(part);
|
|
||||||
});
|
|
||||||
|
|
||||||
BukkitHitBox hitBox = new BukkitHitBox(this, hitBoxConfig, aabbs.toArray(new HitBoxPart[0]));
|
|
||||||
for (int entityId : ids) {
|
|
||||||
fakeEntityIds.add(entityId);
|
|
||||||
mainEntityIds.add(entityId);
|
|
||||||
this.hitBoxes.put(entityId, hitBox);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化缓存的家具包
|
|
||||||
try {
|
|
||||||
this.cachedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets);
|
|
||||||
if (this.minimized) {
|
|
||||||
this.cachedMinimizedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(minimizedPackets);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
CraftEngine.instance().logger().warn("Failed to init spawn packets for furniture " + id(), e);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
this.fakeEntityIds = fakeEntityIds;
|
|
||||||
this.entityIds = mainEntityIds;
|
|
||||||
this.colliderEntities = colliders.toArray(new Collider[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initializeColliders() {
|
public void addCollidersToWorld() {
|
||||||
Object world = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(this.location.getWorld());
|
Object world = FastNMS.INSTANCE.field$CraftWorld$ServerLevel(this.location.getWorld());
|
||||||
for (Collider entity : this.colliderEntities) {
|
for (Collider entity : super.colliders) {
|
||||||
FastNMS.INSTANCE.method$LevelWriter$addFreshEntity(world, entity.handle());
|
|
||||||
Entity bukkitEntity = FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity.handle());
|
Entity bukkitEntity = FastNMS.INSTANCE.method$Entity$getBukkitEntity(entity.handle());
|
||||||
bukkitEntity.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_COLLISION, PersistentDataType.BYTE, (byte) 1);
|
bukkitEntity.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_COLLISION, PersistentDataType.BYTE, (byte) 1);
|
||||||
}
|
bukkitEntity.setPersistent(false);
|
||||||
}
|
if (!bukkitEntity.isValid()) {
|
||||||
|
FastNMS.INSTANCE.method$LevelWriter$addFreshEntity(world, entity.handle());
|
||||||
@NotNull
|
}
|
||||||
public Object spawnPacket(Player player) {
|
|
||||||
// TODO hasPermission might be slow, can we use a faster way in the future?
|
|
||||||
// TODO Make it based on conditions. So we can dynamically control which furniture should be sent to the player
|
|
||||||
if (!this.minimized || player.hasPermission(FurnitureManager.FURNITURE_ADMIN_NODE)) {
|
|
||||||
return this.cachedSpawnPacket;
|
|
||||||
} else {
|
|
||||||
return this.cachedMinimizedSpawnPacket;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WorldPosition position() {
|
public boolean setVariant(String variantName) {
|
||||||
return LocationUtils.toWorldPosition(this.location);
|
FurnitureVariant variant = this.config.getVariant(variantName);
|
||||||
}
|
if (variant == null) return false;
|
||||||
|
if (this.currentVariant == variant) return false;
|
||||||
@NotNull
|
// 检查新位置是否可用
|
||||||
public Location location() {
|
List<AABB> aabbs = new ArrayList<>();
|
||||||
return this.location.clone();
|
WorldPosition position = position();
|
||||||
}
|
for (FurnitureHitBoxConfig<?> hitBoxConfig : variant.hitBoxConfigs()) {
|
||||||
|
hitBoxConfig.prepareBoundingBox(position, aabbs::add, false);
|
||||||
@NotNull
|
}
|
||||||
public Entity baseEntity() {
|
if (!aabbs.isEmpty()) {
|
||||||
Entity entity = this.baseEntity.get();
|
if (!FastNMS.INSTANCE.checkEntityCollision(position.world.serverWorld(), aabbs.stream().map(it -> FastNMS.INSTANCE.constructor$AABB(it.minX, it.minY, it.minZ, it.maxX, it.maxY, it.maxZ)).toList(),
|
||||||
if (entity == null) {
|
o -> {
|
||||||
throw new RuntimeException("Base entity not found. It might be unloaded.");
|
for (Collider collider : super.colliders) {
|
||||||
|
if (o == collider.handle()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 删除椅子
|
||||||
|
super.destroySeats();
|
||||||
|
BukkitFurnitureManager.instance().invalidateFurniture(this);
|
||||||
|
super.clearColliders();
|
||||||
|
super.setVariantInternal(variant);
|
||||||
|
BukkitFurnitureManager.instance().initFurniture(this);
|
||||||
|
this.addCollidersToWorld();
|
||||||
|
this.refresh();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Boolean> moveTo(WorldPosition position) {
|
||||||
|
ItemDisplay itemDisplay = this.metaEntity.get();
|
||||||
|
if (itemDisplay == null) return CompletableFuture.completedFuture(false);
|
||||||
|
// 检查新位置是否可用
|
||||||
|
List<AABB> aabbs = new ArrayList<>();
|
||||||
|
for (FurnitureHitBoxConfig<?> hitBoxConfig : getCurrentVariant().hitBoxConfigs()) {
|
||||||
|
hitBoxConfig.prepareBoundingBox(position, aabbs::add, false);
|
||||||
|
}
|
||||||
|
if (!aabbs.isEmpty()) {
|
||||||
|
if (!FastNMS.INSTANCE.checkEntityCollision(position.world.serverWorld(), aabbs.stream().map(it -> FastNMS.INSTANCE.constructor$AABB(it.minX, it.minY, it.minZ, it.maxX, it.maxY, it.maxZ)).toList(),
|
||||||
|
o -> {
|
||||||
|
for (Collider collider : super.colliders) {
|
||||||
|
if (o == collider.handle()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})) {
|
||||||
|
return CompletableFuture.completedFuture(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 删除椅子
|
||||||
|
super.destroySeats();
|
||||||
|
// 准备传送
|
||||||
|
CompletableFuture<Boolean> future = new CompletableFuture<>();
|
||||||
|
BukkitFurnitureManager.instance().invalidateFurniture(this);
|
||||||
|
super.clearColliders();
|
||||||
|
this.location = LocationUtils.toLocation(position);
|
||||||
|
Object removePacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(MiscUtils.init(new IntArrayList(), l -> l.add(itemDisplay.getEntityId())));
|
||||||
|
for (Player player : itemDisplay.getTrackedPlayers()) {
|
||||||
|
BukkitAdaptors.adapt(player).sendPacket(removePacket, false);
|
||||||
|
}
|
||||||
|
itemDisplay.teleportAsync(this.location).thenAccept(result -> {
|
||||||
|
if (result) {
|
||||||
|
super.setVariantInternal(getCurrentVariant());
|
||||||
|
BukkitFurnitureManager.instance().initFurniture(this);
|
||||||
|
this.addCollidersToWorld();
|
||||||
|
Object addPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(itemDisplay.getEntityId(), itemDisplay.getUniqueId(),
|
||||||
|
itemDisplay.getX(), itemDisplay.getY(), itemDisplay.getZ(), itemDisplay.getPitch(), itemDisplay.getYaw(), MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0);
|
||||||
|
for (Player player : itemDisplay.getTrackedPlayers()) {
|
||||||
|
BukkitAdaptors.adapt(player).sendPacket(addPacket, false);
|
||||||
|
}
|
||||||
|
future.complete(true);
|
||||||
|
} else {
|
||||||
|
future.complete(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
@Override
|
||||||
|
protected void refresh() {
|
||||||
|
ItemDisplay itemDisplay = this.metaEntity.get();
|
||||||
|
if (itemDisplay == null) return;
|
||||||
|
Object removePacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(MiscUtils.init(new IntArrayList(), l -> l.add(itemDisplay.getEntityId())));
|
||||||
|
Object addPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(itemDisplay.getEntityId(), itemDisplay.getUniqueId(),
|
||||||
|
itemDisplay.getX(), itemDisplay.getY(), itemDisplay.getZ(), itemDisplay.getPitch(), itemDisplay.getYaw(), MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0);
|
||||||
|
for (Player player : itemDisplay.getTrackedPlayers()) {
|
||||||
|
BukkitAdaptors.adapt(player).sendPacket(removePacket, false);
|
||||||
|
BukkitAdaptors.adapt(player).sendPacket(addPacket, false);
|
||||||
}
|
}
|
||||||
return entity;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isValid() {
|
public void destroy() {
|
||||||
return baseEntity().isValid();
|
Optional.ofNullable(this.metaEntity.get()).ifPresent(Entity::remove);
|
||||||
|
for (Collider entity : super.colliders) {
|
||||||
|
entity.destroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
// 获取掉落物的位置,受到家具变种的影响
|
||||||
public Location dropLocation() {
|
public Location getDropLocation() {
|
||||||
Optional<Vector3f> dropOffset = this.placement.dropOffset();
|
Optional<Vector3f> dropOffset = this.getCurrentVariant().dropOffset();
|
||||||
if (dropOffset.isEmpty()) {
|
if (dropOffset.isEmpty()) {
|
||||||
return location();
|
return this.location;
|
||||||
}
|
}
|
||||||
Quaternionf conjugated = QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - this.location.getYaw()), 0).conjugate();
|
Quaternionf conjugated = QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - this.location.getYaw()), 0).conjugate();
|
||||||
Vector3f offset = conjugated.transform(new Vector3f(dropOffset.get()));
|
Vector3f offset = conjugated.transform(new Vector3f(dropOffset.get()));
|
||||||
return new Location(this.location.getWorld(), this.location.getX() + offset.x, this.location.getY() + offset.y, this.location.getZ() - offset.z);
|
return new Location(this.location.getWorld(), this.location.getX() + offset.x, this.location.getY() + offset.y, this.location.getZ() - offset.z);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public Location location() {
|
||||||
public void destroy() {
|
return location;
|
||||||
if (!isValid()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.baseEntity().remove();
|
|
||||||
this.destroyColliders();
|
|
||||||
this.destroySeats();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public Entity getBukkitEntity() {
|
||||||
public void destroyColliders() {
|
return this.metaEntity.get();
|
||||||
for (Collider entity : this.colliderEntities) {
|
|
||||||
if (entity != null)
|
|
||||||
entity.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void destroySeats() {
|
|
||||||
for (HitBox hitBox : this.hitBoxes.values()) {
|
|
||||||
for (Seat<HitBox> seat : hitBox.seats()) {
|
|
||||||
seat.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UUID uuid() {
|
|
||||||
return this.baseEntity().getUniqueId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int baseEntityId() {
|
|
||||||
return this.baseEntityId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public List<Integer> entityIds() {
|
|
||||||
return Collections.unmodifiableList(this.entityIds);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public List<Integer> fakeEntityIds() {
|
|
||||||
return Collections.unmodifiableList(this.fakeEntityIds);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collider[] collisionEntities() {
|
|
||||||
return this.colliderEntities;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable HitBox hitBoxByEntityId(int id) {
|
|
||||||
return this.hitBoxes.get(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable HitBoxPart hitBoxPartByEntityId(int id) {
|
|
||||||
return this.hitBoxParts.get(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull AnchorType anchorType() {
|
|
||||||
return this.placement.anchorType();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull Key id() {
|
|
||||||
return this.furniture.id();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull CustomFurniture config() {
|
|
||||||
return this.furniture;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasExternalModel() {
|
|
||||||
return hasExternalModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FurnitureExtraData extraData() {
|
|
||||||
return this.extraData;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setExtraData(FurnitureExtraData extraData) {
|
|
||||||
this.extraData = extraData;
|
|
||||||
this.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save() {
|
|
||||||
try {
|
|
||||||
this.baseEntity().getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY, this.extraData.toBytes());
|
|
||||||
} catch (IOException e) {
|
|
||||||
CraftEngine.instance().logger().warn("Failed to save furniture data.", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
package net.momirealms.craftengine.bukkit.entity.furniture;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
|
||||||
import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData;
|
|
||||||
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
|
||||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
|
||||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
|
||||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
|
|
||||||
import net.momirealms.craftengine.core.entity.Billboard;
|
|
||||||
import net.momirealms.craftengine.core.entity.ItemDisplayContext;
|
|
||||||
import net.momirealms.craftengine.core.entity.furniture.AbstractFurnitureElement;
|
|
||||||
import net.momirealms.craftengine.core.entity.furniture.Furniture;
|
|
||||||
import net.momirealms.craftengine.core.entity.furniture.FurnitureElement;
|
|
||||||
import net.momirealms.craftengine.core.item.Item;
|
|
||||||
import net.momirealms.craftengine.core.item.data.FireworkExplosion;
|
|
||||||
import net.momirealms.craftengine.core.util.Color;
|
|
||||||
import net.momirealms.craftengine.core.util.Key;
|
|
||||||
import net.momirealms.craftengine.core.world.WorldPosition;
|
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.inventory.ItemStack;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.joml.Quaternionf;
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
public class BukkitFurnitureElement extends AbstractFurnitureElement {
|
|
||||||
private final List<Object> commonValues;
|
|
||||||
|
|
||||||
public BukkitFurnitureElement(Key item,
|
|
||||||
Billboard billboard,
|
|
||||||
ItemDisplayContext transform,
|
|
||||||
Vector3f scale,
|
|
||||||
Vector3f translation,
|
|
||||||
Vector3f position,
|
|
||||||
Quaternionf rotation,
|
|
||||||
float shadowRadius,
|
|
||||||
float shadowStrength,
|
|
||||||
boolean applyDyedColor) {
|
|
||||||
super(item, billboard, transform, scale, translation, position, rotation, shadowRadius, shadowStrength, applyDyedColor);
|
|
||||||
this.commonValues = new ArrayList<>();
|
|
||||||
ItemDisplayEntityData.Scale.addEntityDataIfNotDefaultValue(scale(), this.commonValues);
|
|
||||||
ItemDisplayEntityData.RotationLeft.addEntityDataIfNotDefaultValue(rotation(), this.commonValues);
|
|
||||||
ItemDisplayEntityData.BillboardConstraints.addEntityDataIfNotDefaultValue(billboard().id(), this.commonValues);
|
|
||||||
ItemDisplayEntityData.Translation.addEntityDataIfNotDefaultValue(translation(), this.commonValues);
|
|
||||||
ItemDisplayEntityData.DisplayType.addEntityDataIfNotDefaultValue(transform().id(), this.commonValues);
|
|
||||||
ItemDisplayEntityData.ShadowRadius.addEntityDataIfNotDefaultValue(shadowRadius, this.commonValues);
|
|
||||||
ItemDisplayEntityData.ShadowStrength.addEntityDataIfNotDefaultValue(shadowStrength, this.commonValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initPackets(Furniture furniture, int entityId, @NotNull Quaternionf conjugated, Consumer<Object> packets) {
|
|
||||||
WorldPosition position = furniture.position();
|
|
||||||
Vector3f offset = conjugated.transform(new Vector3f(position()));
|
|
||||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
|
||||||
entityId, UUID.randomUUID(), position.x() + offset.x, position.y() + offset.y, position.z() - offset.z, 0, position.yRot(),
|
|
||||||
MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0
|
|
||||||
));
|
|
||||||
if (applyDyedColor()) {
|
|
||||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, getCachedValues(
|
|
||||||
furniture.extraData().dyedColor().orElse(null),
|
|
||||||
furniture.extraData().fireworkExplosionColors().orElse(null)
|
|
||||||
)));
|
|
||||||
} else {
|
|
||||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, getCachedValues(null, null)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized List<Object> getCachedValues(@Nullable Color color, int @Nullable [] colors) {
|
|
||||||
List<Object> cachedValues = new ArrayList<>(this.commonValues);
|
|
||||||
Item<ItemStack> item = BukkitItemManager.instance().createWrappedItem(item(), null);
|
|
||||||
if (item == null) {
|
|
||||||
item = BukkitItemManager.instance().wrap(new ItemStack(Material.BARRIER));
|
|
||||||
} else {
|
|
||||||
if (color != null) {
|
|
||||||
item.dyedColor(color);
|
|
||||||
}
|
|
||||||
if (colors != null) {
|
|
||||||
item.fireworkExplosion(new FireworkExplosion(
|
|
||||||
FireworkExplosion.Shape.SMALL_BALL,
|
|
||||||
new IntArrayList(colors),
|
|
||||||
new IntArrayList(),
|
|
||||||
false,
|
|
||||||
false
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
private float shadowRadius;
|
|
||||||
private float shadowStrength;
|
|
||||||
|
|
||||||
@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 Builder shadowStrength(float shadowStrength) {
|
|
||||||
this.shadowStrength = shadowStrength;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Builder shadowRadius(float shadowRadius) {
|
|
||||||
this.shadowRadius = shadowRadius;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FurnitureElement build() {
|
|
||||||
return new BukkitFurnitureElement(item, billboard, transform, scale, translation, position, rotation, shadowRadius, shadowStrength, applyDyedColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +1,26 @@
|
|||||||
package net.momirealms.craftengine.bukkit.entity.furniture;
|
package net.momirealms.craftengine.bukkit.entity.furniture;
|
||||||
|
|
||||||
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
|
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
|
||||||
import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.InteractionHitBoxConfig;
|
import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.InteractionFurnitureHitboxConfig;
|
||||||
import net.momirealms.craftengine.bukkit.nms.CollisionEntity;
|
import net.momirealms.craftengine.bukkit.nms.CollisionEntity;
|
||||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||||
import net.momirealms.craftengine.bukkit.plugin.network.handler.FurniturePacketHandler;
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
|
||||||
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
|
||||||
import net.momirealms.craftengine.bukkit.util.EntityUtils;
|
import net.momirealms.craftengine.bukkit.util.EntityUtils;
|
||||||
import net.momirealms.craftengine.bukkit.util.KeyUtils;
|
import net.momirealms.craftengine.bukkit.util.KeyUtils;
|
||||||
import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
||||||
import net.momirealms.craftengine.core.entity.furniture.*;
|
import net.momirealms.craftengine.core.entity.furniture.*;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.tick.FurnitureTicker;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.tick.TickingFurnitureImpl;
|
||||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||||
import net.momirealms.craftengine.core.sound.SoundData;
|
import net.momirealms.craftengine.core.sound.SoundData;
|
||||||
import net.momirealms.craftengine.core.util.Key;
|
import net.momirealms.craftengine.core.util.Key;
|
||||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||||
|
import net.momirealms.craftengine.core.world.CEWorld;
|
||||||
import net.momirealms.craftengine.core.world.WorldPosition;
|
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||||
|
import net.momirealms.craftengine.core.world.chunk.CEChunk;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.entity.*;
|
import org.bukkit.entity.*;
|
||||||
import org.bukkit.event.HandlerList;
|
import org.bukkit.event.HandlerList;
|
||||||
@@ -34,13 +38,17 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
|
|||||||
public static final NamespacedKey FURNITURE_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_KEY);
|
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_EXTRA_DATA_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_EXTRA_DATA_KEY);
|
||||||
public static final NamespacedKey FURNITURE_COLLISION = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_COLLISION);
|
public static final NamespacedKey FURNITURE_COLLISION = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_COLLISION);
|
||||||
|
private static BukkitFurnitureManager instance;
|
||||||
|
|
||||||
public static Class<?> COLLISION_ENTITY_CLASS = Interaction.class;
|
public static Class<?> COLLISION_ENTITY_CLASS = Interaction.class;
|
||||||
public static Object NMS_COLLISION_ENTITY_TYPE = MEntityTypes.INTERACTION;
|
public static Object NMS_COLLISION_ENTITY_TYPE = MEntityTypes.INTERACTION;
|
||||||
public static ColliderType COLLISION_ENTITY_TYPE = ColliderType.INTERACTION;
|
public static ColliderType COLLISION_ENTITY_TYPE = ColliderType.INTERACTION;
|
||||||
private static BukkitFurnitureManager instance;
|
|
||||||
private final BukkitCraftEngine plugin;
|
private final BukkitCraftEngine plugin;
|
||||||
private final Map<Integer, BukkitFurniture> furnitureByRealEntityId = new ConcurrentHashMap<>(256, 0.5f);
|
|
||||||
private final Map<Integer, BukkitFurniture> furnitureByEntityId = new ConcurrentHashMap<>(512, 0.5f);
|
private final Map<Integer, BukkitFurniture> byMetaEntityId = new ConcurrentHashMap<>(256, 0.5f);
|
||||||
|
private final Map<Integer, BukkitFurniture> byVirtualEntityId = new ConcurrentHashMap<>(512, 0.5f);
|
||||||
|
private final Map<Integer, BukkitFurniture> byColliderEntityId = new ConcurrentHashMap<>(512, 0.5f);
|
||||||
// Event listeners
|
// Event listeners
|
||||||
private final FurnitureEventListener furnitureEventListener;
|
private final FurnitureEventListener furnitureEventListener;
|
||||||
|
|
||||||
@@ -52,53 +60,56 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
|
|||||||
super(plugin);
|
super(plugin);
|
||||||
instance = this;
|
instance = this;
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.furnitureEventListener = new FurnitureEventListener(this);
|
this.furnitureEventListener = new FurnitureEventListener(this, plugin.worldManager());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Furniture place(WorldPosition position, CustomFurniture furniture, FurnitureExtraData extraData, boolean playSound) {
|
public Furniture place(WorldPosition position, CustomFurniture furniture, FurnitureDataAccessor dataAccessor, boolean playSound) {
|
||||||
return this.place(LocationUtils.toLocation(position), furniture, extraData, playSound);
|
return this.place(LocationUtils.toLocation(position), furniture, dataAccessor, playSound);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BukkitFurniture place(Location location, CustomFurniture furniture, FurnitureExtraData extraData, boolean playSound) {
|
public BukkitFurniture place(Location location, CustomFurniture furniture, FurnitureDataAccessor data, boolean playSound) {
|
||||||
Optional<AnchorType> optionalAnchorType = extraData.anchorType();
|
|
||||||
if (optionalAnchorType.isEmpty() || !furniture.isAllowedPlacement(optionalAnchorType.get())) {
|
|
||||||
extraData.anchorType(furniture.getAnyAnchorType());
|
|
||||||
}
|
|
||||||
Entity furnitureEntity = EntityUtils.spawnEntity(location.getWorld(), location, EntityType.ITEM_DISPLAY, entity -> {
|
Entity furnitureEntity = EntityUtils.spawnEntity(location.getWorld(), location, EntityType.ITEM_DISPLAY, entity -> {
|
||||||
ItemDisplay display = (ItemDisplay) entity;
|
ItemDisplay display = (ItemDisplay) entity;
|
||||||
display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_KEY, PersistentDataType.STRING, furniture.id().toString());
|
display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_KEY, PersistentDataType.STRING, furniture.id().toString());
|
||||||
try {
|
try {
|
||||||
display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY, extraData.toBytes());
|
display.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY, data.toBytes());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
this.plugin.logger().warn("Failed to set furniture PDC for " + furniture.id().toString(), e);
|
this.plugin.logger().warn("Failed to set furniture PDC for " + furniture.id().toString(), e);
|
||||||
}
|
}
|
||||||
handleBaseEntityLoadEarly(display);
|
handleMetaEntityDuringChunkLoad(display);
|
||||||
});
|
});
|
||||||
if (playSound) {
|
if (playSound) {
|
||||||
SoundData data = furniture.settings().sounds().placeSound();
|
SoundData sound = furniture.settings().sounds().placeSound();
|
||||||
location.getWorld().playSound(location, data.id().toString(), SoundCategory.BLOCKS, data.volume().get(), data.pitch().get());
|
location.getWorld().playSound(location, sound.id().toString(), SoundCategory.BLOCKS, sound.volume().get(), sound.pitch().get());
|
||||||
}
|
}
|
||||||
return loadedFurnitureByRealEntityId(furnitureEntity.getEntityId());
|
return loadedFurnitureByMetaEntityId(furnitureEntity.getEntityId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delayedInit() {
|
public void delayedInit() {
|
||||||
|
super.delayedInit();
|
||||||
|
|
||||||
|
// 确定碰撞箱实体类型
|
||||||
|
COLLISION_ENTITY_TYPE = Config.colliderType();
|
||||||
COLLISION_ENTITY_CLASS = Config.colliderType() == ColliderType.INTERACTION ? Interaction.class : Boat.class;
|
COLLISION_ENTITY_CLASS = Config.colliderType() == ColliderType.INTERACTION ? Interaction.class : Boat.class;
|
||||||
NMS_COLLISION_ENTITY_TYPE = Config.colliderType() == ColliderType.INTERACTION ? MEntityTypes.INTERACTION : MEntityTypes.OAK_BOAT;
|
NMS_COLLISION_ENTITY_TYPE = Config.colliderType() == ColliderType.INTERACTION ? MEntityTypes.INTERACTION : MEntityTypes.OAK_BOAT;
|
||||||
COLLISION_ENTITY_TYPE = Config.colliderType();
|
|
||||||
|
// 注册事件
|
||||||
Bukkit.getPluginManager().registerEvents(this.furnitureEventListener, this.plugin.javaPlugin());
|
Bukkit.getPluginManager().registerEvents(this.furnitureEventListener, this.plugin.javaPlugin());
|
||||||
|
|
||||||
|
// 对世界上已有实体的记录
|
||||||
if (VersionHelper.isFolia()) {
|
if (VersionHelper.isFolia()) {
|
||||||
BiConsumer<Entity, Runnable> taskExecutor = (entity, runnable) -> entity.getScheduler().run(this.plugin.javaPlugin(), (t) -> runnable.run(), () -> {});
|
BiConsumer<Entity, Runnable> taskExecutor = (entity, runnable) -> entity.getScheduler().run(this.plugin.javaPlugin(), (t) -> runnable.run(), () -> {});
|
||||||
for (World world : Bukkit.getWorlds()) {
|
for (World world : Bukkit.getWorlds()) {
|
||||||
List<Entity> entities = world.getEntities();
|
List<Entity> entities = world.getEntities();
|
||||||
for (Entity entity : entities) {
|
for (Entity entity : entities) {
|
||||||
if (entity instanceof ItemDisplay display) {
|
if (entity instanceof ItemDisplay display) {
|
||||||
taskExecutor.accept(entity, () -> handleBaseEntityLoadEarly(display));
|
taskExecutor.accept(entity, () -> handleMetaEntityDuringChunkLoad(display));
|
||||||
} else if (entity instanceof Interaction interaction) {
|
} else if (entity instanceof Interaction interaction) {
|
||||||
taskExecutor.accept(entity, () -> handleCollisionEntityLoadOnEntitiesLoad(interaction));
|
taskExecutor.accept(entity, () -> handleCollisionEntityDuringChunkLoad(interaction));
|
||||||
} else if (entity instanceof Boat boat) {
|
} else if (entity instanceof Boat boat) {
|
||||||
taskExecutor.accept(entity, () -> handleCollisionEntityLoadOnEntitiesLoad(boat));
|
taskExecutor.accept(entity, () -> handleCollisionEntityDuringChunkLoad(boat));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,11 +118,11 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
|
|||||||
List<Entity> entities = world.getEntities();
|
List<Entity> entities = world.getEntities();
|
||||||
for (Entity entity : entities) {
|
for (Entity entity : entities) {
|
||||||
if (entity instanceof ItemDisplay display) {
|
if (entity instanceof ItemDisplay display) {
|
||||||
handleBaseEntityLoadEarly(display);
|
handleMetaEntityDuringChunkLoad(display);
|
||||||
} else if (entity instanceof Interaction interaction) {
|
} else if (entity instanceof Interaction interaction) {
|
||||||
handleCollisionEntityLoadOnEntitiesLoad(interaction);
|
handleCollisionEntityDuringChunkLoad(interaction);
|
||||||
} else if (entity instanceof Boat boat) {
|
} else if (entity instanceof Boat boat) {
|
||||||
handleCollisionEntityLoadOnEntitiesLoad(boat);
|
handleCollisionEntityDuringChunkLoad(boat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,155 +131,174 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disable() {
|
public void disable() {
|
||||||
|
super.disable();
|
||||||
HandlerList.unregisterAll(this.furnitureEventListener);
|
HandlerList.unregisterAll(this.furnitureEventListener);
|
||||||
unload();
|
unload();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFurnitureRealEntity(int entityId) {
|
public boolean isFurnitureMetaEntity(int entityId) {
|
||||||
return this.furnitureByRealEntityId.containsKey(entityId);
|
return this.byMetaEntityId.containsKey(entityId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public BukkitFurniture loadedFurnitureByRealEntityId(int entityId) {
|
public BukkitFurniture loadedFurnitureByMetaEntityId(int entityId) {
|
||||||
return this.furnitureByRealEntityId.get(entityId);
|
return this.byMetaEntityId.get(entityId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public BukkitFurniture loadedFurnitureByEntityId(int entityId) {
|
|
||||||
return this.furnitureByEntityId.get(entityId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected CustomFurniture.Builder furnitureBuilder() {
|
public BukkitFurniture loadedFurnitureByVirtualEntityId(int entityId) {
|
||||||
return BukkitCustomFurniture.builder();
|
return this.byVirtualEntityId.get(entityId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
protected FurnitureElement.Builder furnitureElementBuilder() {
|
public BukkitFurniture loadedFurnitureByColliderEntityId(int entityId) {
|
||||||
return BukkitFurnitureElement.builder();
|
return this.byColliderEntityId.get(entityId);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void handleBaseEntityUnload(Entity entity) {
|
// 当元数据实体被卸载了
|
||||||
|
protected void handleMetaEntityUnload(ItemDisplay entity) {
|
||||||
|
// 不是持久化的
|
||||||
|
if (!entity.isPersistent()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
int id = entity.getEntityId();
|
int id = entity.getEntityId();
|
||||||
BukkitFurniture furniture = this.furnitureByRealEntityId.remove(id);
|
BukkitFurniture furniture = this.byMetaEntityId.remove(id);
|
||||||
if (furniture != null) {
|
if (furniture != null) {
|
||||||
Location location = entity.getLocation();
|
Location location = entity.getLocation();
|
||||||
|
// 区块还在加载的时候,就重复卸载了。为极其特殊情况
|
||||||
boolean isPreventing = FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()), location.getBlockX() >> 4, location.getBlockZ() >> 4);
|
boolean isPreventing = FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()), location.getBlockX() >> 4, location.getBlockZ() >> 4);
|
||||||
if (!isPreventing) {
|
if (!isPreventing) {
|
||||||
furniture.destroySeats();
|
furniture.destroySeats();
|
||||||
}
|
}
|
||||||
for (int sub : furniture.entityIds()) {
|
for (int sub : furniture.virtualEntityIds()) {
|
||||||
this.furnitureByEntityId.remove(sub);
|
this.byVirtualEntityId.remove(sub);
|
||||||
|
}
|
||||||
|
for (int sub : furniture.colliderEntityIds()) {
|
||||||
|
this.byColliderEntityId.remove(sub);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 保险起见,collision实体卸载也移除一下
|
||||||
protected void handleCollisionEntityUnload(Entity entity) {
|
protected void handleCollisionEntityUnload(Entity entity) {
|
||||||
int id = entity.getEntityId();
|
int id = entity.getEntityId();
|
||||||
this.furnitureByRealEntityId.remove(id);
|
this.byColliderEntityId.remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation") // just a misleading name `getTrackedPlayers`
|
// 检查这个区块的实体是否已经被加载了
|
||||||
protected void handleBaseEntityLoadLate(ItemDisplay display, int depth) {
|
private boolean isEntitiesLoaded(Location location) {
|
||||||
// must be a furniture item
|
CEWorld ceWorld = this.plugin.worldManager().getWorld(location.getWorld());
|
||||||
String id = display.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING);
|
CEChunk ceChunk = ceWorld.getChunkAtIfLoaded(location.getBlockX() >> 4, location.getBlockZ() >> 4);
|
||||||
|
if (ceChunk == null) return false;
|
||||||
|
return ceChunk.isEntitiesLoaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void handleMetaEntityDuringChunkLoad(ItemDisplay entity) {
|
||||||
|
// 实体可能不是持久的
|
||||||
|
if (!entity.isPersistent()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取家具pdc
|
||||||
|
String id = entity.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING);
|
||||||
if (id == null) return;
|
if (id == null) return;
|
||||||
|
|
||||||
Key key = Key.of(id);
|
// 处理无效的家具
|
||||||
Optional<CustomFurniture> optionalFurniture = furnitureById(key);
|
|
||||||
if (optionalFurniture.isEmpty()) return;
|
|
||||||
|
|
||||||
CustomFurniture customFurniture = optionalFurniture.get();
|
|
||||||
BukkitFurniture previous = this.furnitureByRealEntityId.get(display.getEntityId());
|
|
||||||
if (previous != null) return;
|
|
||||||
|
|
||||||
Location location = display.getLocation();
|
|
||||||
boolean above1_20_1 = VersionHelper.isOrAbove1_20_2();
|
|
||||||
boolean preventChange = FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()), location.getBlockX() >> 4, location.getBlockZ() >> 4);
|
|
||||||
if (above1_20_1) {
|
|
||||||
if (!preventChange) {
|
|
||||||
BukkitFurniture furniture = addNewFurniture(display, customFurniture);
|
|
||||||
furniture.initializeColliders();
|
|
||||||
for (Player player : display.getTrackedPlayers()) {
|
|
||||||
BukkitAdaptors.adapt(player).entityPacketHandlers().computeIfAbsent(furniture.baseEntityId(), k -> new FurniturePacketHandler(furniture.fakeEntityIds()));
|
|
||||||
this.plugin.networkManager().sendPacket(BukkitAdaptors.adapt(player), furniture.spawnPacket(player));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
BukkitFurniture furniture = addNewFurniture(display, customFurniture);
|
|
||||||
for (Player player : display.getTrackedPlayers()) {
|
|
||||||
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
|
|
||||||
serverPlayer.entityPacketHandlers().computeIfAbsent(furniture.baseEntityId(), k -> new FurniturePacketHandler(furniture.fakeEntityIds()));
|
|
||||||
this.plugin.networkManager().sendPacket(serverPlayer, furniture.spawnPacket(player));
|
|
||||||
}
|
|
||||||
if (preventChange) {
|
|
||||||
this.plugin.scheduler().sync().runLater(furniture::initializeColliders, 1, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4);
|
|
||||||
} else {
|
|
||||||
furniture.initializeColliders();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (depth > 2) return;
|
|
||||||
this.plugin.scheduler().sync().runLater(() -> handleBaseEntityLoadLate(display, depth + 1), 1, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void handleCollisionEntityLoadLate(Entity entity, int depth) {
|
|
||||||
// remove the entity if it's not a collision entity, it might be wrongly copied by WorldEdit
|
|
||||||
if (FastNMS.INSTANCE.method$CraftEntity$getHandle(entity) instanceof CollisionEntity) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// not a collision entity
|
|
||||||
Byte flag = entity.getPersistentDataContainer().get(FURNITURE_COLLISION, PersistentDataType.BYTE);
|
|
||||||
if (flag == null || flag != 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Location location = entity.getLocation();
|
|
||||||
World world = location.getWorld();
|
|
||||||
int chunkX = location.getBlockX() >> 4;
|
|
||||||
int chunkZ = location.getBlockZ() >> 4;
|
|
||||||
if (!FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(world), chunkX, chunkZ)) {
|
|
||||||
entity.remove();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (depth > 2) return;
|
|
||||||
plugin.scheduler().sync().runLater(() -> {
|
|
||||||
handleCollisionEntityLoadLate(entity, depth + 1);
|
|
||||||
}, 1, world, chunkX, chunkZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void handleBaseEntityLoadEarly(ItemDisplay display) {
|
|
||||||
String id = display.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING);
|
|
||||||
if (id == null) return;
|
|
||||||
// Remove the entity if it's not a valid furniture
|
|
||||||
if (Config.handleInvalidFurniture()) {
|
if (Config.handleInvalidFurniture()) {
|
||||||
String mapped = Config.furnitureMappings().get(id);
|
String mapped = Config.furnitureMappings().get(id);
|
||||||
if (mapped != null) {
|
if (mapped != null) {
|
||||||
if (mapped.isEmpty()) {
|
if (mapped.isEmpty()) {
|
||||||
display.remove();
|
entity.remove();
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
id = mapped;
|
id = mapped;
|
||||||
display.getPersistentDataContainer().set(FURNITURE_KEY, PersistentDataType.STRING, id);
|
entity.getPersistentDataContainer().set(FURNITURE_KEY, PersistentDataType.STRING, id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取家具配置
|
||||||
Key key = Key.of(id);
|
Key key = Key.of(id);
|
||||||
Optional<CustomFurniture> optionalFurniture = furnitureById(key);
|
Optional<CustomFurniture> optionalFurniture = furnitureById(key);
|
||||||
if (optionalFurniture.isPresent()) {
|
if (optionalFurniture.isEmpty()) return;
|
||||||
CustomFurniture customFurniture = optionalFurniture.get();
|
|
||||||
BukkitFurniture previous = this.furnitureByRealEntityId.get(display.getEntityId());
|
// 只对1.20.2及以上生效,1.20.1比较特殊
|
||||||
if (previous != null) return;
|
if (!VersionHelper.isOrAbove1_20_2()) {
|
||||||
BukkitFurniture furniture = addNewFurniture(display, customFurniture);
|
return;
|
||||||
furniture.initializeColliders(); // safely do it here
|
}
|
||||||
|
|
||||||
|
// 已经在其他事件里加载过了
|
||||||
|
CustomFurniture customFurniture = optionalFurniture.get();
|
||||||
|
BukkitFurniture previous = this.byMetaEntityId.get(entity.getEntityId());
|
||||||
|
if (previous != null) return;
|
||||||
|
|
||||||
|
// 创建新的家具
|
||||||
|
createFurnitureInstance(entity, customFurniture);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
protected void handleMetaEntityAfterChunkLoad(ItemDisplay entity) {
|
||||||
|
// 实体可能不是持久的
|
||||||
|
if (!entity.isPersistent()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取家具pdc
|
||||||
|
String id = entity.getPersistentDataContainer().get(FURNITURE_KEY, PersistentDataType.STRING);
|
||||||
|
if (id == null) return;
|
||||||
|
|
||||||
|
// 这个区块还处于加载实体中,这个时候不处理(1.20.1需要特殊处理)
|
||||||
|
Location location = entity.getLocation();
|
||||||
|
if (VersionHelper.isOrAbove1_20_2() && !isEntitiesLoaded(location)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取家具配置
|
||||||
|
Key key = Key.of(id);
|
||||||
|
Optional<CustomFurniture> optionalFurniture = furnitureById(key);
|
||||||
|
if (optionalFurniture.isEmpty()) return;
|
||||||
|
|
||||||
|
// 已经在其他事件里加载过了
|
||||||
|
CustomFurniture customFurniture = optionalFurniture.get();
|
||||||
|
BukkitFurniture previous = this.byMetaEntityId.get(entity.getEntityId());
|
||||||
|
if (previous != null) return;
|
||||||
|
|
||||||
|
createFurnitureInstance(entity, customFurniture);
|
||||||
|
|
||||||
|
// 补发一次包,修复
|
||||||
|
for (Player player : entity.getTrackedPlayers()) {
|
||||||
|
BukkitAdaptors.adapt(player).sendPacket(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||||
|
entity.getEntityId(), entity.getUniqueId(), location.getX(), location.getY(), location.getZ(), location.getPitch(), location.getYaw(),
|
||||||
|
MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0
|
||||||
|
), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleCollisionEntityLoadOnEntitiesLoad(Entity collisionEntity) {
|
protected void handleCollisionEntityAfterChunkLoad(Entity entity) {
|
||||||
|
// 如果是碰撞实体,那么就忽略
|
||||||
|
if (FastNMS.INSTANCE.method$CraftEntity$getHandle(entity) instanceof CollisionEntity) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 看看有没有碰撞实体的pdc
|
||||||
|
Byte flag = entity.getPersistentDataContainer().get(FURNITURE_COLLISION, PersistentDataType.BYTE);
|
||||||
|
if (flag == null || flag != 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 实体未加载
|
||||||
|
Location location = entity.getLocation();
|
||||||
|
if (!isEntitiesLoaded(location)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除被WorldEdit错误复制的碰撞实体
|
||||||
|
runSafeEntityOperation(location, entity::remove);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handleCollisionEntityDuringChunkLoad(Entity collisionEntity) {
|
||||||
// faster
|
// faster
|
||||||
if (FastNMS.INSTANCE.method$CraftEntity$getHandle(collisionEntity) instanceof CollisionEntity) {
|
if (FastNMS.INSTANCE.method$CraftEntity$getHandle(collisionEntity) instanceof CollisionEntity) {
|
||||||
collisionEntity.remove();
|
collisionEntity.remove();
|
||||||
@@ -284,34 +314,76 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
|
|||||||
collisionEntity.remove();
|
collisionEntity.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
private FurnitureExtraData getFurnitureExtraData(Entity baseEntity) throws IOException {
|
private FurnitureDataAccessor getFurnitureDataAccessor(Entity baseEntity) {
|
||||||
byte[] extraData = baseEntity.getPersistentDataContainer().get(FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY);
|
byte[] extraData = baseEntity.getPersistentDataContainer().get(FURNITURE_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY);
|
||||||
if (extraData == null) return FurnitureExtraData.builder().build();
|
if (extraData == null) return new FurnitureDataAccessor(null);
|
||||||
return FurnitureExtraData.fromBytes(extraData);
|
try {
|
||||||
|
return FurnitureDataAccessor.fromBytes(extraData);
|
||||||
|
} catch (IOException e) {
|
||||||
|
// 损坏了?一般不会
|
||||||
|
return new FurnitureDataAccessor(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized BukkitFurniture addNewFurniture(ItemDisplay display, CustomFurniture furniture) {
|
// 创建家具实例,并初始化碰撞实体
|
||||||
FurnitureExtraData extraData;
|
private BukkitFurniture createFurnitureInstance(ItemDisplay display, CustomFurniture furniture) {
|
||||||
try {
|
BukkitFurniture bukkitFurniture = new BukkitFurniture(display, furniture, getFurnitureDataAccessor(display));
|
||||||
extraData = getFurnitureExtraData(display);
|
initFurniture(bukkitFurniture);
|
||||||
} catch (IOException e) {
|
Location location = display.getLocation();
|
||||||
extraData = FurnitureExtraData.builder().build();
|
runSafeEntityOperation(location, bukkitFurniture::addCollidersToWorld);
|
||||||
plugin.logger().warn("Furniture extra data could not be loaded", e);
|
|
||||||
}
|
|
||||||
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 : bukkitFurniture.collisionEntities()) {
|
|
||||||
int collisionEntityId = FastNMS.INSTANCE.method$Entity$getId(collisionEntity.handle());
|
|
||||||
this.furnitureByRealEntityId.put(collisionEntityId, bukkitFurniture);
|
|
||||||
}
|
|
||||||
return bukkitFurniture;
|
return bukkitFurniture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void initFurniture(BukkitFurniture furniture) {
|
||||||
|
int entityId = furniture.entityId();
|
||||||
|
this.byMetaEntityId.put(entityId, furniture);
|
||||||
|
for (int id : furniture.virtualEntityIds()) {
|
||||||
|
this.byVirtualEntityId.put(id, furniture);
|
||||||
|
}
|
||||||
|
for (Collider collisionEntity : furniture.colliders()) {
|
||||||
|
this.byColliderEntityId.put(collisionEntity.entityId(), furniture);
|
||||||
|
}
|
||||||
|
if (!this.syncTickers.containsKey(entityId)) {
|
||||||
|
FurnitureTicker<BukkitFurniture> ticker = furniture.config.behavior().createSyncFurnitureTicker(furniture);
|
||||||
|
if (ticker != null) {
|
||||||
|
TickingFurnitureImpl<BukkitFurniture> tickingFurniture = new TickingFurnitureImpl<>(furniture, ticker);
|
||||||
|
this.syncTickers.put(entityId, tickingFurniture);
|
||||||
|
this.addSyncFurnitureTicker(tickingFurniture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!this.asyncTickers.containsKey(entityId)) {
|
||||||
|
FurnitureTicker<BukkitFurniture> ticker = furniture.config.behavior().createAsyncBlockEntityTicker(furniture);
|
||||||
|
if (ticker != null) {
|
||||||
|
TickingFurnitureImpl<BukkitFurniture> tickingFurniture = new TickingFurnitureImpl<>(furniture, ticker);
|
||||||
|
this.asyncTickers.put(entityId, tickingFurniture);
|
||||||
|
this.addAsyncFurnitureTicker(tickingFurniture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void invalidateFurniture(BukkitFurniture furniture) {
|
||||||
|
int entityId = furniture.entityId();
|
||||||
|
// 移除entity id映射
|
||||||
|
this.byMetaEntityId.remove(entityId);
|
||||||
|
for (int id : furniture.virtualEntityIds()) {
|
||||||
|
this.byVirtualEntityId.remove(id);
|
||||||
|
}
|
||||||
|
for (Collider collisionEntity : furniture.colliders()) {
|
||||||
|
this.byColliderEntityId.remove(collisionEntity.entityId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runSafeEntityOperation(Location location, Runnable action) {
|
||||||
|
boolean preventChange = FastNMS.INSTANCE.method$ServerLevel$isPreventingStatusUpdates(FastNMS.INSTANCE.field$CraftWorld$ServerLevel(location.getWorld()), location.getBlockX() >> 4, location.getBlockZ() >> 4);
|
||||||
|
if (preventChange) {
|
||||||
|
this.plugin.scheduler().sync().runLater(action, 1, location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4);
|
||||||
|
} else {
|
||||||
|
action.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected HitBoxConfig defaultHitBox() {
|
protected FurnitureHitBoxConfig<?> defaultHitBox() {
|
||||||
return InteractionHitBoxConfig.DEFAULT;
|
return InteractionFurnitureHitboxConfig.DEFAULT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
package net.momirealms.craftengine.bukkit.entity.furniture;
|
|
||||||
|
|
||||||
import net.momirealms.craftengine.bukkit.entity.seat.BukkitSeat;
|
|
||||||
import net.momirealms.craftengine.core.entity.furniture.Furniture;
|
|
||||||
import net.momirealms.craftengine.core.entity.furniture.HitBox;
|
|
||||||
import net.momirealms.craftengine.core.entity.furniture.HitBoxConfig;
|
|
||||||
import net.momirealms.craftengine.core.entity.furniture.HitBoxPart;
|
|
||||||
import net.momirealms.craftengine.core.entity.seat.Seat;
|
|
||||||
import net.momirealms.craftengine.core.entity.seat.SeatConfig;
|
|
||||||
import net.momirealms.craftengine.core.world.EntityHitResult;
|
|
||||||
import net.momirealms.craftengine.core.world.Vec3d;
|
|
||||||
import net.momirealms.sparrow.nbt.CompoundTag;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
public class BukkitHitBox implements HitBox {
|
|
||||||
private final Furniture furniture;
|
|
||||||
private final HitBoxConfig config;
|
|
||||||
private final HitBoxPart[] parts;
|
|
||||||
private final Seat<HitBox>[] seats;
|
|
||||||
|
|
||||||
public BukkitHitBox(Furniture furniture, HitBoxConfig config, HitBoxPart[] parts) {
|
|
||||||
this.parts = parts;
|
|
||||||
this.config = config;
|
|
||||||
this.furniture = furniture;
|
|
||||||
this.seats = createSeats(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private Seat<HitBox>[] createSeats(HitBoxConfig config) {
|
|
||||||
SeatConfig[] seatConfigs = config.seats();
|
|
||||||
Seat<HitBox>[] seats = new Seat[seatConfigs.length];
|
|
||||||
for (int i = 0; i < seatConfigs.length; i++) {
|
|
||||||
seats[i] = new BukkitSeat<>(this, seatConfigs[i]);
|
|
||||||
}
|
|
||||||
return seats;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HitBoxPart[] parts() {
|
|
||||||
return this.parts;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HitBoxConfig config() {
|
|
||||||
return this.config;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Seat<HitBox>[] seats() {
|
|
||||||
return this.seats;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Optional<EntityHitResult> clip(Vec3d min, Vec3d max) {
|
|
||||||
for (HitBoxPart hbe : this.parts) {
|
|
||||||
Optional<EntityHitResult> result = hbe.aabb().clip(min, max);
|
|
||||||
if (result.isPresent()) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void saveCustomData(CompoundTag data) {
|
|
||||||
data.putString("type", "furniture");
|
|
||||||
data.putInt("entity_id", this.furniture.baseEntityId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,8 +2,22 @@ package net.momirealms.craftengine.bukkit.entity.furniture;
|
|||||||
|
|
||||||
import com.destroystokyo.paper.event.entity.EntityAddToWorldEvent;
|
import com.destroystokyo.paper.event.entity.EntityAddToWorldEvent;
|
||||||
import com.destroystokyo.paper.event.entity.EntityRemoveFromWorldEvent;
|
import com.destroystokyo.paper.event.entity.EntityRemoveFromWorldEvent;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
|
||||||
|
import net.momirealms.craftengine.bukkit.api.event.FurnitureInteractEvent;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||||
|
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
|
||||||
|
import net.momirealms.craftengine.bukkit.world.BukkitWorldManager;
|
||||||
|
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
||||||
|
import net.momirealms.craftengine.core.item.Item;
|
||||||
|
import net.momirealms.craftengine.core.item.ItemKeys;
|
||||||
|
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||||
|
import net.momirealms.craftengine.core.world.CEWorld;
|
||||||
|
import net.momirealms.craftengine.core.world.chunk.CEChunk;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.entity.ItemDisplay;
|
import org.bukkit.entity.ItemDisplay;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
@@ -11,14 +25,18 @@ import org.bukkit.event.world.ChunkUnloadEvent;
|
|||||||
import org.bukkit.event.world.EntitiesLoadEvent;
|
import org.bukkit.event.world.EntitiesLoadEvent;
|
||||||
import org.bukkit.event.world.WorldLoadEvent;
|
import org.bukkit.event.world.WorldLoadEvent;
|
||||||
import org.bukkit.event.world.WorldUnloadEvent;
|
import org.bukkit.event.world.WorldUnloadEvent;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class FurnitureEventListener implements Listener {
|
public class FurnitureEventListener implements Listener {
|
||||||
private final BukkitFurnitureManager manager;
|
private final BukkitFurnitureManager manager;
|
||||||
|
private final BukkitWorldManager worldManager;
|
||||||
|
|
||||||
public FurnitureEventListener(final BukkitFurnitureManager manager) {
|
public FurnitureEventListener(final BukkitFurnitureManager manager, final BukkitWorldManager worldManager) {
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
|
this.worldManager = worldManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -29,21 +47,26 @@ public class FurnitureEventListener implements Listener {
|
|||||||
List<Entity> entities = event.getEntities();
|
List<Entity> entities = event.getEntities();
|
||||||
for (Entity entity : entities) {
|
for (Entity entity : entities) {
|
||||||
if (entity instanceof ItemDisplay itemDisplay) {
|
if (entity instanceof ItemDisplay itemDisplay) {
|
||||||
this.manager.handleBaseEntityLoadEarly(itemDisplay);
|
this.manager.handleMetaEntityDuringChunkLoad(itemDisplay);
|
||||||
} else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) {
|
} else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) {
|
||||||
this.manager.handleCollisionEntityLoadOnEntitiesLoad(entity);
|
this.manager.handleCollisionEntityDuringChunkLoad(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
CEWorld world = this.worldManager.getWorld(event.getWorld());
|
||||||
|
CEChunk ceChunk = world.getChunkAtIfLoaded(event.getChunk().getChunkKey());
|
||||||
|
if (ceChunk != null) {
|
||||||
|
ceChunk.setEntitiesLoaded(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
|
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOW)
|
||||||
public void onWorldLoad(WorldLoadEvent event) {
|
public void onWorldLoad(WorldLoadEvent event) {
|
||||||
List<Entity> entities = event.getWorld().getEntities();
|
List<Entity> entities = event.getWorld().getEntities();
|
||||||
for (Entity entity : entities) {
|
for (Entity entity : entities) {
|
||||||
if (entity instanceof ItemDisplay itemDisplay) {
|
if (entity instanceof ItemDisplay itemDisplay) {
|
||||||
this.manager.handleBaseEntityLoadEarly(itemDisplay);
|
this.manager.handleMetaEntityDuringChunkLoad(itemDisplay);
|
||||||
} else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) {
|
} else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) {
|
||||||
this.manager.handleCollisionEntityLoadOnEntitiesLoad(entity);
|
this.manager.handleCollisionEntityDuringChunkLoad(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -52,9 +75,9 @@ public class FurnitureEventListener implements Listener {
|
|||||||
public void onEntityLoad(EntityAddToWorldEvent event) {
|
public void onEntityLoad(EntityAddToWorldEvent event) {
|
||||||
Entity entity = event.getEntity();
|
Entity entity = event.getEntity();
|
||||||
if (entity instanceof ItemDisplay itemDisplay) {
|
if (entity instanceof ItemDisplay itemDisplay) {
|
||||||
this.manager.handleBaseEntityLoadLate(itemDisplay, 0);
|
this.manager.handleMetaEntityAfterChunkLoad(itemDisplay);
|
||||||
} else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) {
|
} else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) {
|
||||||
this.manager.handleCollisionEntityLoadLate(entity, 0);
|
this.manager.handleCollisionEntityAfterChunkLoad(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,8 +88,8 @@ public class FurnitureEventListener implements Listener {
|
|||||||
public void onChunkUnload(ChunkUnloadEvent event) {
|
public void onChunkUnload(ChunkUnloadEvent event) {
|
||||||
Entity[] entities = event.getChunk().getEntities();
|
Entity[] entities = event.getChunk().getEntities();
|
||||||
for (Entity entity : entities) {
|
for (Entity entity : entities) {
|
||||||
if (entity instanceof ItemDisplay) {
|
if (entity instanceof ItemDisplay itemDisplay) {
|
||||||
this.manager.handleBaseEntityUnload(entity);
|
this.manager.handleMetaEntityUnload(itemDisplay);
|
||||||
} else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) {
|
} else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) {
|
||||||
this.manager.handleCollisionEntityUnload(entity);
|
this.manager.handleCollisionEntityUnload(entity);
|
||||||
}
|
}
|
||||||
@@ -77,8 +100,8 @@ public class FurnitureEventListener implements Listener {
|
|||||||
public void onWorldUnload(WorldUnloadEvent event) {
|
public void onWorldUnload(WorldUnloadEvent event) {
|
||||||
List<Entity> entities = event.getWorld().getEntities();
|
List<Entity> entities = event.getWorld().getEntities();
|
||||||
for (Entity entity : entities) {
|
for (Entity entity : entities) {
|
||||||
if (entity instanceof ItemDisplay) {
|
if (entity instanceof ItemDisplay itemDisplay) {
|
||||||
this.manager.handleBaseEntityUnload(entity);
|
this.manager.handleMetaEntityUnload(itemDisplay);
|
||||||
} else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) {
|
} else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) {
|
||||||
this.manager.handleCollisionEntityUnload(entity);
|
this.manager.handleCollisionEntityUnload(entity);
|
||||||
}
|
}
|
||||||
@@ -88,10 +111,50 @@ public class FurnitureEventListener implements Listener {
|
|||||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
||||||
public void onEntityUnload(EntityRemoveFromWorldEvent event) {
|
public void onEntityUnload(EntityRemoveFromWorldEvent event) {
|
||||||
Entity entity = event.getEntity();
|
Entity entity = event.getEntity();
|
||||||
if (entity instanceof ItemDisplay) {
|
if (entity instanceof ItemDisplay itemDisplay) {
|
||||||
this.manager.handleBaseEntityUnload(entity);
|
this.manager.handleMetaEntityUnload(itemDisplay);
|
||||||
} else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) {
|
} else if (BukkitFurnitureManager.COLLISION_ENTITY_CLASS.isInstance(entity)) {
|
||||||
this.manager.handleCollisionEntityUnload(entity);
|
this.manager.handleCollisionEntityUnload(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||||
|
public void onInteractFurniture(FurnitureInteractEvent event) {
|
||||||
|
Player bukkitPlayer = event.getPlayer();
|
||||||
|
BukkitServerPlayer player = BukkitAdaptors.adapt(bukkitPlayer);
|
||||||
|
if (!(player.canInstabuild() && player.hasPermission("minecraft.debugstick")) && !player.hasPermission("minecraft.debugstick.always")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Item<ItemStack> itemInHand = player.getItemInHand(InteractionHand.MAIN_HAND);
|
||||||
|
if (!itemInHand.vanillaId().equals(ItemKeys.DEBUG_STICK)) return;
|
||||||
|
BukkitFurniture furniture = event.furniture();
|
||||||
|
List<String> variants = new ArrayList<>(furniture.config.variants().keySet());
|
||||||
|
if (variants.size() == 1) {
|
||||||
|
try {
|
||||||
|
Object systemChatPacket = NetworkReflections.constructor$ClientboundSystemChatPacket.newInstance(
|
||||||
|
ComponentUtils.adventureToMinecraft(Component.translatable("item.minecraft.debug_stick.empty").arguments(Component.text(furniture.id().asString()))), true);
|
||||||
|
player.sendPacket(systemChatPacket, false);
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
CraftEngine.instance().logger().warn("Could not create system chat packet", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String variantName = furniture.getCurrentVariant().name();
|
||||||
|
int index = variants.indexOf(variantName) + 1;
|
||||||
|
if (index >= variants.size()) {
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
furniture.setVariant(variants.get(index));
|
||||||
|
try {
|
||||||
|
Object systemChatPacket = NetworkReflections.constructor$ClientboundSystemChatPacket.newInstance(
|
||||||
|
ComponentUtils.adventureToMinecraft(Component.translatable("item.minecraft.debug_stick.update")
|
||||||
|
.arguments(
|
||||||
|
Component.text("variant"),
|
||||||
|
Component.text(variants.get(index))
|
||||||
|
)), true);
|
||||||
|
player.sendPacket(systemChatPacket, false);
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
CraftEngine.instance().logger().warn("Could not create system chat packet", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.entity.furniture.element;
|
||||||
|
|
||||||
|
import com.mojang.datafixers.util.Pair;
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntList;
|
||||||
|
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeHolders;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
|
||||||
|
import net.momirealms.craftengine.bukkit.world.score.BukkitTeamManager;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.Furniture;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.FurnitureColorSource;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement;
|
||||||
|
import net.momirealms.craftengine.core.entity.player.Player;
|
||||||
|
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||||
|
import net.momirealms.craftengine.core.world.Vec3d;
|
||||||
|
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class ArmorStandFurnitureElement implements FurnitureElement {
|
||||||
|
private final ArmorStandFurnitureElementConfig config;
|
||||||
|
private final FurnitureColorSource colorSource;
|
||||||
|
public final Object cachedSpawnPacket;
|
||||||
|
public final Object cachedDespawnPacket;
|
||||||
|
public final Object cachedScalePacket;
|
||||||
|
public final Object cachedTeamPacket;
|
||||||
|
public final int entityId;
|
||||||
|
public final UUID uuid = UUID.randomUUID();
|
||||||
|
|
||||||
|
public ArmorStandFurnitureElement(Furniture furniture, ArmorStandFurnitureElementConfig config) {
|
||||||
|
this.config = config;
|
||||||
|
this.entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet();
|
||||||
|
WorldPosition furniturePos = furniture.position();
|
||||||
|
Vec3d position = Furniture.getRelativePosition(furniturePos, config.position());
|
||||||
|
this.cachedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||||
|
this.entityId, this.uuid, position.x, position.y, position.z,
|
||||||
|
furniturePos.xRot, furniturePos.yRot, MEntityTypes.ARMOR_STAND, 0, CoreReflections.instance$Vec3$Zero, furniturePos.yRot
|
||||||
|
);
|
||||||
|
this.colorSource = furniture.dataAccessor.getColorSource();
|
||||||
|
this.cachedDespawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(IntList.of(this.entityId));
|
||||||
|
if (VersionHelper.isOrAbove1_20_5() && config.scale != 1) {
|
||||||
|
Object attributeIns = FastNMS.INSTANCE.constructor$AttributeInstance(MAttributeHolders.SCALE, (Consumer<?>) (o) -> {});
|
||||||
|
FastNMS.INSTANCE.method$AttributeInstance$setBaseValue(attributeIns, config.scale());
|
||||||
|
this.cachedScalePacket = FastNMS.INSTANCE.constructor$ClientboundUpdateAttributesPacket(this.entityId, Collections.singletonList(attributeIns));
|
||||||
|
} else {
|
||||||
|
this.cachedScalePacket = null;
|
||||||
|
}
|
||||||
|
Object teamPacket = null;
|
||||||
|
if (config.glowColor != null) {
|
||||||
|
Object teamByColor = BukkitTeamManager.instance().getTeamByColor(config.glowColor);
|
||||||
|
if (teamByColor != null) {
|
||||||
|
teamPacket = FastNMS.INSTANCE.method$ClientboundSetPlayerTeamPacket$createMultiplePlayerPacket(teamByColor, List.of(this.uuid.toString()), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.cachedTeamPacket = teamPacket;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void show(Player player) {
|
||||||
|
player.sendPackets(List.of(this.cachedSpawnPacket, FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadata.apply(player))), false);
|
||||||
|
player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundSetEquipmentPacket(this.entityId, List.of(
|
||||||
|
Pair.of(CoreReflections.instance$EquipmentSlot$HEAD, this.config.item(player, this.colorSource).getLiteralObject())
|
||||||
|
)), false);
|
||||||
|
if (this.cachedScalePacket != null) {
|
||||||
|
player.sendPacket(this.cachedScalePacket, false);
|
||||||
|
}
|
||||||
|
if (this.cachedTeamPacket != null) {
|
||||||
|
player.sendPacket(this.cachedTeamPacket, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void hide(Player player) {
|
||||||
|
player.sendPacket(this.cachedDespawnPacket, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] virtualEntityIds() {
|
||||||
|
return new int[] {this.entityId};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void collectVirtualEntityId(Consumer<Integer> collector) {
|
||||||
|
collector.accept(this.entityId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.entity.furniture.element;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
|
import net.momirealms.craftengine.bukkit.entity.data.ArmorStandData;
|
||||||
|
import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData;
|
||||||
|
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.Furniture;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.FurnitureColorSource;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigFactory;
|
||||||
|
import net.momirealms.craftengine.core.entity.player.Player;
|
||||||
|
import net.momirealms.craftengine.core.item.Item;
|
||||||
|
import net.momirealms.craftengine.core.item.ItemKeys;
|
||||||
|
import net.momirealms.craftengine.core.item.data.FireworkExplosion;
|
||||||
|
import net.momirealms.craftengine.core.util.Key;
|
||||||
|
import net.momirealms.craftengine.core.util.LegacyChatFormatter;
|
||||||
|
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||||
|
import net.momirealms.craftengine.core.world.Glowing;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public class ArmorStandFurnitureElementConfig implements FurnitureElementConfig<ArmorStandFurnitureElement>, Glowing {
|
||||||
|
public static final Factory FACTORY = new Factory();
|
||||||
|
public final Function<Player, List<Object>> metadata;
|
||||||
|
public final Key itemId;
|
||||||
|
public final float scale;
|
||||||
|
public final boolean applyDyedColor;
|
||||||
|
public final Vector3f position;
|
||||||
|
public final boolean small;
|
||||||
|
public final LegacyChatFormatter glowColor;
|
||||||
|
|
||||||
|
public ArmorStandFurnitureElementConfig(Key itemId,
|
||||||
|
float scale,
|
||||||
|
Vector3f position,
|
||||||
|
boolean applyDyedColor,
|
||||||
|
boolean small,
|
||||||
|
LegacyChatFormatter glowColor) {
|
||||||
|
this.position = position;
|
||||||
|
this.applyDyedColor = applyDyedColor;
|
||||||
|
this.small = small;
|
||||||
|
this.scale = scale;
|
||||||
|
this.itemId = itemId;
|
||||||
|
this.glowColor = glowColor;
|
||||||
|
this.metadata = (player) -> {
|
||||||
|
List<Object> dataValues = new ArrayList<>(2);
|
||||||
|
if (glowColor != null) {
|
||||||
|
BaseEntityData.SharedFlags.addEntityData((byte) 0x60, dataValues);
|
||||||
|
} else {
|
||||||
|
BaseEntityData.SharedFlags.addEntityData((byte) 0x20, dataValues);
|
||||||
|
}
|
||||||
|
if (small) {
|
||||||
|
ArmorStandData.ArmorStandFlags.addEntityData((byte) 0x01, dataValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataValues;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Item<?> item(Player player, FurnitureColorSource colorSource) {
|
||||||
|
Item<ItemStack> wrappedItem = BukkitItemManager.instance().createWrappedItem(itemId, player);
|
||||||
|
if (applyDyedColor && colorSource != null && wrappedItem != null) {
|
||||||
|
Optional.ofNullable(colorSource.dyedColor()).ifPresent(wrappedItem::dyedColor);
|
||||||
|
Optional.ofNullable(colorSource.fireworkColors()).ifPresent(colors -> wrappedItem.fireworkExplosion(new FireworkExplosion(
|
||||||
|
FireworkExplosion.Shape.SMALL_BALL,
|
||||||
|
new IntArrayList(colors),
|
||||||
|
new IntArrayList(),
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
return Optional.ofNullable(wrappedItem).orElseGet(() -> BukkitItemManager.instance().createWrappedItem(ItemKeys.BARRIER, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public LegacyChatFormatter glowColor() {
|
||||||
|
return this.glowColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float scale() {
|
||||||
|
return scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean small() {
|
||||||
|
return small;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f position() {
|
||||||
|
return this.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean applyDyedColor() {
|
||||||
|
return this.applyDyedColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Key itemId() {
|
||||||
|
return this.itemId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArmorStandFurnitureElement create(@NotNull Furniture furniture) {
|
||||||
|
return new ArmorStandFurnitureElement(furniture, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Factory implements FurnitureElementConfigFactory<ArmorStandFurnitureElement> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArmorStandFurnitureElementConfig create(Map<String, Object> arguments) {
|
||||||
|
return new ArmorStandFurnitureElementConfig(
|
||||||
|
Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.furniture.element.armor_stand.missing_item")),
|
||||||
|
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", 1f), "scale"),
|
||||||
|
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0f), "position"),
|
||||||
|
ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("apply-dyed-color", true), "apply-dyed-color"),
|
||||||
|
ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("small", false), "small"),
|
||||||
|
ResourceConfigUtils.getAsEnum(arguments.get("glow-color"), LegacyChatFormatter.class, null)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.entity.furniture.element;
|
||||||
|
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigs;
|
||||||
|
|
||||||
|
public class BukkitFurnitureElementConfigs extends FurnitureElementConfigs {
|
||||||
|
|
||||||
|
static {
|
||||||
|
register(ITEM_DISPLAY, ItemDisplayFurnitureElementConfig.FACTORY);
|
||||||
|
register(TEXT_DISPLAY, TextDisplayFurnitureElementConfig.FACTORY);
|
||||||
|
register(ITEM, ItemFurnitureElementConfig.FACTORY);
|
||||||
|
register(ARMOR_STAND, ArmorStandFurnitureElementConfig.FACTORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BukkitFurnitureElementConfigs() {}
|
||||||
|
|
||||||
|
public static void init() {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.entity.furniture.element;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
|
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.Furniture;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.FurnitureColorSource;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement;
|
||||||
|
import net.momirealms.craftengine.core.entity.player.Player;
|
||||||
|
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||||
|
import net.momirealms.craftengine.core.world.Vec3d;
|
||||||
|
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class ItemDisplayFurnitureElement implements FurnitureElement {
|
||||||
|
private final ItemDisplayFurnitureElementConfig config;
|
||||||
|
private final WorldPosition position;
|
||||||
|
private final int entityId;
|
||||||
|
private final Object despawnPacket;
|
||||||
|
private final FurnitureColorSource colorSource;
|
||||||
|
private final UUID uuid = UUID.randomUUID();
|
||||||
|
|
||||||
|
public ItemDisplayFurnitureElement(Furniture furniture, ItemDisplayFurnitureElementConfig config) {
|
||||||
|
this.config = config;
|
||||||
|
this.entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet();
|
||||||
|
WorldPosition furniturePos = furniture.position();
|
||||||
|
Vec3d position = Furniture.getRelativePosition(furniturePos, config.position());
|
||||||
|
this.position = new WorldPosition(furniturePos.world, position.x, position.y, position.z, furniturePos.xRot, furniturePos.yRot);
|
||||||
|
this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(MiscUtils.init(new IntArrayList(), a -> a.add(entityId)));
|
||||||
|
this.colorSource = furniture.dataAccessor.getColorSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void show(Player player) {
|
||||||
|
player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundBundlePacket(List.of(
|
||||||
|
FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||||
|
this.entityId, this.uuid,
|
||||||
|
this.position.x, this.position.y, this.position.z, 0, this.position.yRot,
|
||||||
|
MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0
|
||||||
|
),
|
||||||
|
FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadata.apply(player, this.colorSource))
|
||||||
|
)), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void hide(Player player) {
|
||||||
|
player.sendPacket(this.despawnPacket, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] virtualEntityIds() {
|
||||||
|
return new int[] {this.entityId};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void collectVirtualEntityId(Consumer<Integer> collector) {
|
||||||
|
collector.accept(this.entityId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,218 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.entity.furniture.element;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
|
import net.momirealms.craftengine.bukkit.entity.data.ItemDisplayEntityData;
|
||||||
|
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||||
|
import net.momirealms.craftengine.core.entity.display.Billboard;
|
||||||
|
import net.momirealms.craftengine.core.entity.display.ItemDisplayContext;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.Furniture;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.FurnitureColorSource;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigFactory;
|
||||||
|
import net.momirealms.craftengine.core.entity.player.Player;
|
||||||
|
import net.momirealms.craftengine.core.item.Item;
|
||||||
|
import net.momirealms.craftengine.core.item.ItemKeys;
|
||||||
|
import net.momirealms.craftengine.core.item.data.FireworkExplosion;
|
||||||
|
import net.momirealms.craftengine.core.util.Color;
|
||||||
|
import net.momirealms.craftengine.core.util.Key;
|
||||||
|
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.joml.Quaternionf;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
public class ItemDisplayFurnitureElementConfig implements FurnitureElementConfig<ItemDisplayFurnitureElement> {
|
||||||
|
public static final Factory FACTORY = new Factory();
|
||||||
|
public final BiFunction<Player, FurnitureColorSource, List<Object>> metadata;
|
||||||
|
public final Key itemId;
|
||||||
|
public final Vector3f scale;
|
||||||
|
public final Vector3f position;
|
||||||
|
public final Vector3f translation;
|
||||||
|
public final float xRot;
|
||||||
|
public final float yRot;
|
||||||
|
public final Quaternionf rotation;
|
||||||
|
public final ItemDisplayContext displayContext;
|
||||||
|
public final Billboard billboard;
|
||||||
|
public final float shadowRadius;
|
||||||
|
public final float shadowStrength;
|
||||||
|
public final boolean applyDyedColor;
|
||||||
|
public final Color glowColor;
|
||||||
|
public final int blockLight;
|
||||||
|
public final int skyLight;
|
||||||
|
public final float viewRange;
|
||||||
|
|
||||||
|
public ItemDisplayFurnitureElementConfig(Key itemId,
|
||||||
|
Vector3f scale,
|
||||||
|
Vector3f position,
|
||||||
|
Vector3f translation,
|
||||||
|
float xRot,
|
||||||
|
float yRot,
|
||||||
|
Quaternionf rotation,
|
||||||
|
ItemDisplayContext displayContext,
|
||||||
|
Billboard billboard,
|
||||||
|
float shadowRadius,
|
||||||
|
float shadowStrength,
|
||||||
|
boolean applyDyedColor,
|
||||||
|
@Nullable Color glowColor,
|
||||||
|
int blockLight,
|
||||||
|
int skyLight,
|
||||||
|
float viewRange) {
|
||||||
|
this.scale = scale;
|
||||||
|
this.position = position;
|
||||||
|
this.translation = translation;
|
||||||
|
this.xRot = xRot;
|
||||||
|
this.yRot = yRot;
|
||||||
|
this.rotation = rotation;
|
||||||
|
this.displayContext = displayContext;
|
||||||
|
this.billboard = billboard;
|
||||||
|
this.shadowRadius = shadowRadius;
|
||||||
|
this.shadowStrength = shadowStrength;
|
||||||
|
this.applyDyedColor = applyDyedColor;
|
||||||
|
this.itemId = itemId;
|
||||||
|
this.glowColor = glowColor;
|
||||||
|
this.blockLight = blockLight;
|
||||||
|
this.skyLight = skyLight;
|
||||||
|
this.viewRange = viewRange;
|
||||||
|
BiFunction<Player, FurnitureColorSource, Item<?>> itemFunction = (player, colorSource) -> {
|
||||||
|
Item<ItemStack> wrappedItem = BukkitItemManager.instance().createWrappedItem(itemId, player);
|
||||||
|
if (applyDyedColor && colorSource != null && wrappedItem != null) {
|
||||||
|
Optional.ofNullable(colorSource.dyedColor()).ifPresent(wrappedItem::dyedColor);
|
||||||
|
Optional.ofNullable(colorSource.fireworkColors()).ifPresent(colors -> wrappedItem.fireworkExplosion(new FireworkExplosion(
|
||||||
|
FireworkExplosion.Shape.SMALL_BALL,
|
||||||
|
new IntArrayList(colors),
|
||||||
|
new IntArrayList(),
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
return Optional.ofNullable(wrappedItem).orElseGet(() -> BukkitItemManager.instance().createWrappedItem(ItemKeys.BARRIER, null));
|
||||||
|
};
|
||||||
|
this.metadata = (player, source) -> {
|
||||||
|
List<Object> dataValues = new ArrayList<>();
|
||||||
|
if (glowColor != null) {
|
||||||
|
ItemDisplayEntityData.SharedFlags.addEntityData((byte) 0x40, dataValues);
|
||||||
|
ItemDisplayEntityData.GlowColorOverride.addEntityData(glowColor.color(), dataValues);
|
||||||
|
}
|
||||||
|
ItemDisplayEntityData.DisplayedItem.addEntityData(itemFunction.apply(player, source).getLiteralObject(), dataValues);
|
||||||
|
ItemDisplayEntityData.Scale.addEntityDataIfNotDefaultValue(this.scale, dataValues);
|
||||||
|
ItemDisplayEntityData.RotationLeft.addEntityDataIfNotDefaultValue(this.rotation, dataValues);
|
||||||
|
ItemDisplayEntityData.BillboardConstraints.addEntityDataIfNotDefaultValue(this.billboard.id(), dataValues);
|
||||||
|
ItemDisplayEntityData.Translation.addEntityDataIfNotDefaultValue(this.translation, dataValues);
|
||||||
|
ItemDisplayEntityData.DisplayType.addEntityDataIfNotDefaultValue(this.displayContext.id(), dataValues);
|
||||||
|
ItemDisplayEntityData.ShadowRadius.addEntityDataIfNotDefaultValue(this.shadowRadius, dataValues);
|
||||||
|
ItemDisplayEntityData.ShadowStrength.addEntityDataIfNotDefaultValue(this.shadowStrength, dataValues);
|
||||||
|
if (this.blockLight != -1 && this.skyLight != -1) {
|
||||||
|
ItemDisplayEntityData.BrightnessOverride.addEntityData(this.blockLight << 4 | this.skyLight << 20, dataValues);
|
||||||
|
}
|
||||||
|
ItemDisplayEntityData.ViewRange.addEntityDataIfNotDefaultValue((float) (this.viewRange * player.displayEntityViewDistance()), dataValues);
|
||||||
|
return dataValues;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f scale() {
|
||||||
|
return this.scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f position() {
|
||||||
|
return this.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f translation() {
|
||||||
|
return this.translation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float xRot() {
|
||||||
|
return this.xRot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float yRot() {
|
||||||
|
return this.yRot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Quaternionf rotation() {
|
||||||
|
return this.rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemDisplayContext displayContext() {
|
||||||
|
return this.displayContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Billboard billboard() {
|
||||||
|
return this.billboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float shadowRadius() {
|
||||||
|
return this.shadowRadius;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float shadowStrength() {
|
||||||
|
return this.shadowStrength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean applyDyedColor() {
|
||||||
|
return this.applyDyedColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BiFunction<Player, FurnitureColorSource, List<Object>> metadata() {
|
||||||
|
return this.metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Key itemId() {
|
||||||
|
return this.itemId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Color glowColor() {
|
||||||
|
return this.glowColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int blockLight() {
|
||||||
|
return this.blockLight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int skyLight() {
|
||||||
|
return this.skyLight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float viewRange() {
|
||||||
|
return this.viewRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemDisplayFurnitureElement create(@NotNull Furniture furniture) {
|
||||||
|
return new ItemDisplayFurnitureElement(furniture, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Factory implements FurnitureElementConfigFactory<ItemDisplayFurnitureElement> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemDisplayFurnitureElementConfig create(Map<String, Object> arguments) {
|
||||||
|
Map<String, Object> brightness = ResourceConfigUtils.getAsMap(arguments.getOrDefault("brightness", Map.of()), "brightness");
|
||||||
|
return new ItemDisplayFurnitureElementConfig(
|
||||||
|
Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.furniture.element.item_display.missing_item")),
|
||||||
|
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("scale", 1f), "scale"),
|
||||||
|
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0f), "position"),
|
||||||
|
ResourceConfigUtils.getAsVector3f(arguments.get("translation"), "translation"),
|
||||||
|
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 0f), "pitch"),
|
||||||
|
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"),
|
||||||
|
ResourceConfigUtils.getAsQuaternionf(arguments.getOrDefault("rotation", 0f), "rotation"),
|
||||||
|
ResourceConfigUtils.getAsEnum(ResourceConfigUtils.get(arguments, "display-context", "display-transform"), ItemDisplayContext.class, ItemDisplayContext.NONE),
|
||||||
|
ResourceConfigUtils.getAsEnum(arguments.get("billboard"), Billboard.class, Billboard.FIXED),
|
||||||
|
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-radius", 0f), "shadow-radius"),
|
||||||
|
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-strength", 1f), "shadow-strength"),
|
||||||
|
ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("apply-dyed-color", true), "apply-dyed-color"),
|
||||||
|
Optional.ofNullable(arguments.get("glow-color")).map(it -> Color.fromStrings(it.toString().split(","))).orElse(null),
|
||||||
|
ResourceConfigUtils.getAsInt(brightness.getOrDefault("block-light", -1), "block-light"),
|
||||||
|
ResourceConfigUtils.getAsInt(brightness.getOrDefault("sky-light", -1), "sky-light"),
|
||||||
|
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("view-range", 1f), "view-range")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.entity.furniture.element;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
|
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.Furniture;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.FurnitureColorSource;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement;
|
||||||
|
import net.momirealms.craftengine.core.entity.player.Player;
|
||||||
|
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||||
|
import net.momirealms.craftengine.core.world.Vec3d;
|
||||||
|
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class ItemFurnitureElement implements FurnitureElement {
|
||||||
|
private final ItemFurnitureElementConfig config;
|
||||||
|
public final int entityId1;
|
||||||
|
public final int entityId2;
|
||||||
|
private final Object despawnPacket;
|
||||||
|
private final FurnitureColorSource colorSource;
|
||||||
|
public final Object cachedSpawnPacket1;
|
||||||
|
public final Object cachedSpawnPacket2;
|
||||||
|
public final Object cachedRidePacket;
|
||||||
|
|
||||||
|
public ItemFurnitureElement(Furniture furniture, ItemFurnitureElementConfig config) {
|
||||||
|
this.config = config;
|
||||||
|
this.entityId1 = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet();
|
||||||
|
this.entityId2 = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet();
|
||||||
|
WorldPosition furniturePos = furniture.position();
|
||||||
|
Vec3d position = Furniture.getRelativePosition(furniturePos, config.position());
|
||||||
|
this.cachedSpawnPacket1 = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||||
|
entityId1, UUID.randomUUID(), position.x, position.y, position.z,
|
||||||
|
0, 0, MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0
|
||||||
|
);
|
||||||
|
this.cachedSpawnPacket2 = FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||||
|
entityId2, UUID.randomUUID(), position.x, position.y, position.z,
|
||||||
|
0, 0, MEntityTypes.ITEM, 0, CoreReflections.instance$Vec3$Zero, 0
|
||||||
|
);
|
||||||
|
this.cachedRidePacket = FastNMS.INSTANCE.constructor$ClientboundSetPassengersPacket(entityId1, entityId2);
|
||||||
|
this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(MiscUtils.init(new IntArrayList(),
|
||||||
|
a -> {
|
||||||
|
a.add(entityId1);
|
||||||
|
a.add(entityId2);
|
||||||
|
}
|
||||||
|
));
|
||||||
|
this.colorSource = furniture.dataAccessor.getColorSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void show(Player player) {
|
||||||
|
player.sendPackets(List.of(
|
||||||
|
this.cachedSpawnPacket1,
|
||||||
|
this.cachedSpawnPacket2,
|
||||||
|
this.cachedRidePacket,
|
||||||
|
FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId2, this.config.metadata().apply(player, this.colorSource)
|
||||||
|
)), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void hide(Player player) {
|
||||||
|
player.sendPacket(this.despawnPacket, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] virtualEntityIds() {
|
||||||
|
return new int[] {this.entityId1, this.entityId2};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void collectVirtualEntityId(Consumer<Integer> collector) {
|
||||||
|
collector.accept(this.entityId1);
|
||||||
|
collector.accept(this.entityId2);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.entity.furniture.element;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
|
import net.momirealms.craftengine.bukkit.entity.data.ItemEntityData;
|
||||||
|
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.Furniture;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.FurnitureColorSource;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigFactory;
|
||||||
|
import net.momirealms.craftengine.core.entity.player.Player;
|
||||||
|
import net.momirealms.craftengine.core.item.Item;
|
||||||
|
import net.momirealms.craftengine.core.item.ItemKeys;
|
||||||
|
import net.momirealms.craftengine.core.item.data.FireworkExplosion;
|
||||||
|
import net.momirealms.craftengine.core.util.Key;
|
||||||
|
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
public class ItemFurnitureElementConfig implements FurnitureElementConfig<ItemFurnitureElement> {
|
||||||
|
public static final Factory FACTORY = new Factory();
|
||||||
|
public final BiFunction<Player, FurnitureColorSource, List<Object>> metadata;
|
||||||
|
public final Key itemId;
|
||||||
|
public final boolean applyDyedColor;
|
||||||
|
public final Vector3f position;
|
||||||
|
|
||||||
|
public ItemFurnitureElementConfig(Key itemId,
|
||||||
|
Vector3f position,
|
||||||
|
boolean applyDyedColor) {
|
||||||
|
this.position = position;
|
||||||
|
this.applyDyedColor = applyDyedColor;
|
||||||
|
this.itemId = itemId;
|
||||||
|
BiFunction<Player, FurnitureColorSource, Item<?>> itemFunction = (player, colorSource) -> {
|
||||||
|
Item<ItemStack> wrappedItem = BukkitItemManager.instance().createWrappedItem(itemId, player);
|
||||||
|
if (applyDyedColor && colorSource != null && wrappedItem != null) {
|
||||||
|
Optional.ofNullable(colorSource.dyedColor()).ifPresent(wrappedItem::dyedColor);
|
||||||
|
Optional.ofNullable(colorSource.fireworkColors()).ifPresent(colors -> wrappedItem.fireworkExplosion(new FireworkExplosion(
|
||||||
|
FireworkExplosion.Shape.SMALL_BALL,
|
||||||
|
new IntArrayList(colors),
|
||||||
|
new IntArrayList(),
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
return Optional.ofNullable(wrappedItem).orElseGet(() -> BukkitItemManager.instance().createWrappedItem(ItemKeys.BARRIER, null));
|
||||||
|
};
|
||||||
|
this.metadata = (player, source) -> {
|
||||||
|
List<Object> dataValues = new ArrayList<>();
|
||||||
|
ItemEntityData.Item.addEntityData(itemFunction.apply(player, source).getLiteralObject(), dataValues);
|
||||||
|
ItemEntityData.NoGravity.addEntityData(true, dataValues);
|
||||||
|
return dataValues;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f position() {
|
||||||
|
return this.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean applyDyedColor() {
|
||||||
|
return this.applyDyedColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BiFunction<Player, FurnitureColorSource, List<Object>> metadata() {
|
||||||
|
return this.metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Key itemId() {
|
||||||
|
return this.itemId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemFurnitureElement create(@NotNull Furniture furniture) {
|
||||||
|
return new ItemFurnitureElement(furniture, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Factory implements FurnitureElementConfigFactory<ItemFurnitureElement> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemFurnitureElementConfig create(Map<String, Object> arguments) {
|
||||||
|
return new ItemFurnitureElementConfig(
|
||||||
|
Key.of(ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("item"), "warning.config.furniture.element.item.missing_item")),
|
||||||
|
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0f), "position"),
|
||||||
|
ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("apply-dyed-color", true), "apply-dyed-color")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.entity.furniture.element;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
|
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.Furniture;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElement;
|
||||||
|
import net.momirealms.craftengine.core.entity.player.Player;
|
||||||
|
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||||
|
import net.momirealms.craftengine.core.world.Vec3d;
|
||||||
|
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class TextDisplayFurnitureElement implements FurnitureElement {
|
||||||
|
private final TextDisplayFurnitureElementConfig config;
|
||||||
|
private final WorldPosition position;
|
||||||
|
private final int entityId;
|
||||||
|
private final Object despawnPacket;
|
||||||
|
private final UUID uuid = UUID.randomUUID();
|
||||||
|
|
||||||
|
public TextDisplayFurnitureElement(Furniture furniture, TextDisplayFurnitureElementConfig config) {
|
||||||
|
this.config = config;
|
||||||
|
this.entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet();
|
||||||
|
WorldPosition furniturePos = furniture.position();
|
||||||
|
Vec3d position = Furniture.getRelativePosition(furniturePos, config.position());
|
||||||
|
this.position = new WorldPosition(furniturePos.world, position.x, position.y, position.z, furniturePos.xRot, furniturePos.yRot);
|
||||||
|
this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(MiscUtils.init(new IntArrayList(), a -> a.add(entityId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void show(Player player) {
|
||||||
|
player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundBundlePacket(List.of(
|
||||||
|
FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||||
|
this.entityId, this.uuid,
|
||||||
|
this.position.x, this.position.y, this.position.z, 0, this.position.yRot,
|
||||||
|
MEntityTypes.TEXT_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0
|
||||||
|
),
|
||||||
|
FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, this.config.metadata.apply(player))
|
||||||
|
)), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void hide(Player player) {
|
||||||
|
player.sendPacket(this.despawnPacket, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] virtualEntityIds() {
|
||||||
|
return new int[] {this.entityId};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void collectVirtualEntityId(Consumer<Integer> collector) {
|
||||||
|
collector.accept(this.entityId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,251 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.entity.furniture.element;
|
||||||
|
|
||||||
|
import net.momirealms.craftengine.bukkit.entity.data.TextDisplayEntityData;
|
||||||
|
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
|
||||||
|
import net.momirealms.craftengine.core.entity.display.Billboard;
|
||||||
|
import net.momirealms.craftengine.core.entity.display.ItemDisplayContext;
|
||||||
|
import net.momirealms.craftengine.core.entity.display.TextDisplayAlignment;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.Furniture;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfig;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.element.FurnitureElementConfigFactory;
|
||||||
|
import net.momirealms.craftengine.core.entity.player.Player;
|
||||||
|
import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext;
|
||||||
|
import net.momirealms.craftengine.core.util.AdventureHelper;
|
||||||
|
import net.momirealms.craftengine.core.util.Color;
|
||||||
|
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.joml.Quaternionf;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public class TextDisplayFurnitureElementConfig implements FurnitureElementConfig<TextDisplayFurnitureElement> {
|
||||||
|
public static final Factory FACTORY = new Factory();
|
||||||
|
public final Function<Player, List<Object>> metadata;
|
||||||
|
public final String text;
|
||||||
|
public final Vector3f scale;
|
||||||
|
public final Vector3f position;
|
||||||
|
public final Vector3f translation;
|
||||||
|
public final float xRot;
|
||||||
|
public final float yRot;
|
||||||
|
public final Quaternionf rotation;
|
||||||
|
public final ItemDisplayContext displayContext;
|
||||||
|
public final Billboard billboard;
|
||||||
|
public final float shadowRadius;
|
||||||
|
public final float shadowStrength;
|
||||||
|
public final Color glowColor;
|
||||||
|
public final int blockLight;
|
||||||
|
public final int skyLight;
|
||||||
|
public final float viewRange;
|
||||||
|
public final int lineWidth;
|
||||||
|
public final int backgroundColor;
|
||||||
|
public final byte opacity;
|
||||||
|
public final boolean hasShadow;
|
||||||
|
public final boolean isSeeThrough;
|
||||||
|
public final boolean useDefaultBackgroundColor;
|
||||||
|
public final TextDisplayAlignment alignment;
|
||||||
|
|
||||||
|
public TextDisplayFurnitureElementConfig(String text,
|
||||||
|
Vector3f scale,
|
||||||
|
Vector3f position,
|
||||||
|
Vector3f translation,
|
||||||
|
float xRot,
|
||||||
|
float yRot,
|
||||||
|
Quaternionf rotation,
|
||||||
|
ItemDisplayContext displayContext,
|
||||||
|
Billboard billboard,
|
||||||
|
float shadowRadius,
|
||||||
|
float shadowStrength,
|
||||||
|
@Nullable Color glowColor,
|
||||||
|
int blockLight,
|
||||||
|
int skyLight,
|
||||||
|
float viewRange,
|
||||||
|
int lineWidth,
|
||||||
|
int backgroundColor,
|
||||||
|
byte opacity,
|
||||||
|
boolean hasShadow,
|
||||||
|
boolean isSeeThrough,
|
||||||
|
boolean useDefaultBackgroundColor,
|
||||||
|
TextDisplayAlignment alignment) {
|
||||||
|
this.text = text;
|
||||||
|
this.scale = scale;
|
||||||
|
this.position = position;
|
||||||
|
this.translation = translation;
|
||||||
|
this.xRot = xRot;
|
||||||
|
this.yRot = yRot;
|
||||||
|
this.rotation = rotation;
|
||||||
|
this.displayContext = displayContext;
|
||||||
|
this.billboard = billboard;
|
||||||
|
this.shadowRadius = shadowRadius;
|
||||||
|
this.shadowStrength = shadowStrength;
|
||||||
|
this.glowColor = glowColor;
|
||||||
|
this.blockLight = blockLight;
|
||||||
|
this.skyLight = skyLight;
|
||||||
|
this.viewRange = viewRange;
|
||||||
|
this.lineWidth = lineWidth;
|
||||||
|
this.backgroundColor = backgroundColor;
|
||||||
|
this.opacity = opacity;
|
||||||
|
this.hasShadow = hasShadow;
|
||||||
|
this.useDefaultBackgroundColor = useDefaultBackgroundColor;
|
||||||
|
this.alignment = alignment;
|
||||||
|
this.isSeeThrough = isSeeThrough;
|
||||||
|
this.metadata = (player) -> {
|
||||||
|
List<Object> dataValues = new ArrayList<>();
|
||||||
|
if (glowColor != null) {
|
||||||
|
TextDisplayEntityData.SharedFlags.addEntityData((byte) 0x40, dataValues);
|
||||||
|
TextDisplayEntityData.GlowColorOverride.addEntityData(glowColor.color(), dataValues);
|
||||||
|
}
|
||||||
|
TextDisplayEntityData.Scale.addEntityDataIfNotDefaultValue(this.scale, dataValues);
|
||||||
|
TextDisplayEntityData.RotationLeft.addEntityDataIfNotDefaultValue(this.rotation, dataValues);
|
||||||
|
TextDisplayEntityData.BillboardConstraints.addEntityDataIfNotDefaultValue(this.billboard.id(), dataValues);
|
||||||
|
TextDisplayEntityData.Translation.addEntityDataIfNotDefaultValue(this.translation, dataValues);
|
||||||
|
TextDisplayEntityData.ShadowRadius.addEntityDataIfNotDefaultValue(this.shadowRadius, dataValues);
|
||||||
|
TextDisplayEntityData.ShadowStrength.addEntityDataIfNotDefaultValue(this.shadowStrength, dataValues);
|
||||||
|
TextDisplayEntityData.Text.addEntityData(ComponentUtils.adventureToMinecraft(AdventureHelper.miniMessage().deserialize(this.text, NetworkTextReplaceContext.of(player).tagResolvers())), dataValues);
|
||||||
|
TextDisplayEntityData.LineWidth.addEntityDataIfNotDefaultValue(this.lineWidth, dataValues);
|
||||||
|
TextDisplayEntityData.BackgroundColor.addEntityDataIfNotDefaultValue(this.backgroundColor, dataValues);
|
||||||
|
TextDisplayEntityData.TextOpacity.addEntityDataIfNotDefaultValue(this.opacity, dataValues);
|
||||||
|
TextDisplayEntityData.TextDisplayMasks.addEntityDataIfNotDefaultValue(TextDisplayEntityData.encodeMask(this.hasShadow, this.isSeeThrough, this.useDefaultBackgroundColor, this.alignment), dataValues);
|
||||||
|
if (this.blockLight != -1 && this.skyLight != -1) {
|
||||||
|
TextDisplayEntityData.BrightnessOverride.addEntityData(this.blockLight << 4 | this.skyLight << 20, dataValues);
|
||||||
|
}
|
||||||
|
TextDisplayEntityData.ViewRange.addEntityDataIfNotDefaultValue((float) (this.viewRange * player.displayEntityViewDistance()), dataValues);
|
||||||
|
return dataValues;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f scale() {
|
||||||
|
return this.scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f position() {
|
||||||
|
return this.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f translation() {
|
||||||
|
return this.translation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float xRot() {
|
||||||
|
return this.xRot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float yRot() {
|
||||||
|
return this.yRot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Quaternionf rotation() {
|
||||||
|
return this.rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemDisplayContext displayContext() {
|
||||||
|
return this.displayContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Billboard billboard() {
|
||||||
|
return this.billboard;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float shadowRadius() {
|
||||||
|
return this.shadowRadius;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float shadowStrength() {
|
||||||
|
return this.shadowStrength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Function<Player, List<Object>> metadata() {
|
||||||
|
return this.metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Color glowColor() {
|
||||||
|
return this.glowColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int blockLight() {
|
||||||
|
return this.blockLight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int skyLight() {
|
||||||
|
return this.skyLight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float viewRange() {
|
||||||
|
return this.viewRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String text() {
|
||||||
|
return this.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int lineWidth() {
|
||||||
|
return this.lineWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int backgroundColor() {
|
||||||
|
return this.backgroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte opacity() {
|
||||||
|
return this.opacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasShadow() {
|
||||||
|
return this.hasShadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSeeThrough() {
|
||||||
|
return this.isSeeThrough;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean useDefaultBackgroundColor() {
|
||||||
|
return this.useDefaultBackgroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextDisplayAlignment alignment() {
|
||||||
|
return this.alignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TextDisplayFurnitureElement create(@NotNull Furniture furniture) {
|
||||||
|
return new TextDisplayFurnitureElement(furniture, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Factory implements FurnitureElementConfigFactory<TextDisplayFurnitureElement> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TextDisplayFurnitureElementConfig create(Map<String, Object> arguments) {
|
||||||
|
Map<String, Object> brightness = ResourceConfigUtils.getAsMap(arguments.getOrDefault("brightness", Map.of()), "brightness");
|
||||||
|
return new TextDisplayFurnitureElementConfig(
|
||||||
|
ResourceConfigUtils.requireNonEmptyStringOrThrow(arguments.get("text"), "warning.config.furniture.element.text_display.missing_text"),
|
||||||
|
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("scale", 1f), "scale"),
|
||||||
|
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0f), "position"),
|
||||||
|
ResourceConfigUtils.getAsVector3f(arguments.get("translation"), "translation"),
|
||||||
|
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("pitch", 0f), "pitch"),
|
||||||
|
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"),
|
||||||
|
ResourceConfigUtils.getAsQuaternionf(arguments.getOrDefault("rotation", 0f), "rotation"),
|
||||||
|
ResourceConfigUtils.getAsEnum(ResourceConfigUtils.get(arguments, "display-context", "display-transform"), ItemDisplayContext.class, ItemDisplayContext.NONE),
|
||||||
|
ResourceConfigUtils.getAsEnum(arguments.get("billboard"), Billboard.class, Billboard.FIXED),
|
||||||
|
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-radius", 0f), "shadow-radius"),
|
||||||
|
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-strength", 1f), "shadow-strength"),
|
||||||
|
Optional.ofNullable(arguments.get("glow-color")).map(it -> Color.fromStrings(it.toString().split(","))).orElse(null),
|
||||||
|
ResourceConfigUtils.getAsInt(brightness.getOrDefault("block-light", -1), "block-light"),
|
||||||
|
ResourceConfigUtils.getAsInt(brightness.getOrDefault("sky-light", -1), "sky-light"),
|
||||||
|
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("view-range", 1f), "view-range"),
|
||||||
|
ResourceConfigUtils.getAsInt(arguments.getOrDefault("line-width", 200), "line-width"),
|
||||||
|
ResourceConfigUtils.getOrDefault(arguments.get("background-color"), o -> Color.fromStrings(o.toString().split(",")).color(), 0x40000000),
|
||||||
|
(byte) ResourceConfigUtils.getAsInt(arguments.getOrDefault("text-opacity", -1), "text-opacity"),
|
||||||
|
ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("has-shadow", false), "has-shadow"),
|
||||||
|
ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("is-see-through", false), "is-see-through"),
|
||||||
|
ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("use-default-background-color", false), "use-default-background-color"),
|
||||||
|
ResourceConfigUtils.getAsEnum(arguments.get("alignment"), TextDisplayAlignment.class, TextDisplayAlignment.CENTER)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.entity.furniture.hitbox;
|
||||||
|
|
||||||
|
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitCollider;
|
||||||
|
import net.momirealms.craftengine.bukkit.entity.seat.BukkitSeat;
|
||||||
|
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.Collider;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.Furniture;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBox;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig;
|
||||||
|
import net.momirealms.craftengine.core.entity.seat.Seat;
|
||||||
|
import net.momirealms.craftengine.core.entity.seat.SeatConfig;
|
||||||
|
import net.momirealms.craftengine.core.world.Vec3d;
|
||||||
|
import net.momirealms.craftengine.core.world.World;
|
||||||
|
import net.momirealms.craftengine.core.world.collision.AABB;
|
||||||
|
import net.momirealms.sparrow.nbt.CompoundTag;
|
||||||
|
|
||||||
|
public abstract class AbstractFurnitureHitBox implements FurnitureHitBox {
|
||||||
|
protected final Furniture furniture;
|
||||||
|
protected Seat<FurnitureHitBox>[] seats;
|
||||||
|
|
||||||
|
public AbstractFurnitureHitBox(Furniture furniture, FurnitureHitBoxConfig<?> config) {
|
||||||
|
this.furniture = furniture;
|
||||||
|
this.seats = createSeats(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private Seat<FurnitureHitBox>[] createSeats(FurnitureHitBoxConfig<?> config) {
|
||||||
|
SeatConfig[] seatConfigs = config.seats();
|
||||||
|
Seat<FurnitureHitBox>[] seats = new Seat[seatConfigs.length];
|
||||||
|
for (int i = 0; i < seatConfigs.length; i++) {
|
||||||
|
seats[i] = new BukkitSeat<>(this, seatConfigs[i]);
|
||||||
|
}
|
||||||
|
return seats;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveCustomData(CompoundTag data) {
|
||||||
|
data.putString("type", "furniture");
|
||||||
|
// 用于通过座椅找到原始家具
|
||||||
|
data.putInt("entity_id", this.furniture.entityId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Seat<FurnitureHitBox>[] seats() {
|
||||||
|
return this.seats;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Collider createCollider(World world, Vec3d position, AABB ceAABB, boolean canCollide, boolean blocksBuilding, boolean canBeHitByProjectile) {
|
||||||
|
Object nmsAABB = FastNMS.INSTANCE.constructor$AABB(ceAABB.minX, ceAABB.minY, ceAABB.minZ, ceAABB.maxX, ceAABB.maxY, ceAABB.maxZ);
|
||||||
|
return new BukkitCollider(world.serverWorld(), nmsAABB, position.x, position.y, position.z, canBeHitByProjectile, canCollide, blocksBuilding);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.entity.furniture.hitbox;
|
||||||
|
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxTypes;
|
||||||
|
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||||
|
|
||||||
|
public class BukkitFurnitureHitboxTypes extends FurnitureHitBoxTypes {
|
||||||
|
|
||||||
|
public static void init() {}
|
||||||
|
|
||||||
|
static {
|
||||||
|
register(INTERACTION, InteractionFurnitureHitboxConfig.FACTORY);
|
||||||
|
register(SHULKER, ShulkerFurnitureHitboxConfig.FACTORY);
|
||||||
|
register(CUSTOM, CustomFurnitureHitboxConfig.FACTORY);
|
||||||
|
if (VersionHelper.isOrAbove1_21_6()) {
|
||||||
|
register(HAPPY_GHAST, HappyGhastFurnitureHitboxConfig.FACTORY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package net.momirealms.craftengine.bukkit.entity.furniture.hitbox;
|
|
||||||
|
|
||||||
import net.momirealms.craftengine.core.entity.furniture.HitBoxTypes;
|
|
||||||
|
|
||||||
public class BukkitHitBoxTypes extends HitBoxTypes {
|
|
||||||
|
|
||||||
public static void init() {}
|
|
||||||
|
|
||||||
static {
|
|
||||||
register(INTERACTION, InteractionHitBoxConfig.FACTORY);
|
|
||||||
register(SHULKER, ShulkerHitBoxConfig.FACTORY);
|
|
||||||
register(HAPPY_GHAST, HappyGhastHitBoxConfig.FACTORY);
|
|
||||||
register(CUSTOM, CustomHitBoxConfig.FACTORY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.entity.furniture.hitbox;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
|
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeHolders;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.Collider;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.Furniture;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart;
|
||||||
|
import net.momirealms.craftengine.core.entity.player.Player;
|
||||||
|
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||||
|
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||||
|
import net.momirealms.craftengine.core.world.Vec3d;
|
||||||
|
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||||
|
import net.momirealms.craftengine.core.world.collision.AABB;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class CustomFurnitureHitbox extends AbstractFurnitureHitBox {
|
||||||
|
private final CustomFurnitureHitboxConfig config;
|
||||||
|
private final Collider collider;
|
||||||
|
private final Object spawnPacket;
|
||||||
|
private final Object despawnPacket;
|
||||||
|
private final FurnitureHitboxPart part;
|
||||||
|
private final int entityId;
|
||||||
|
|
||||||
|
public CustomFurnitureHitbox(Furniture furniture, CustomFurnitureHitboxConfig config) {
|
||||||
|
super(furniture, config);
|
||||||
|
this.config = config;
|
||||||
|
WorldPosition position = furniture.position();
|
||||||
|
Vec3d pos = Furniture.getRelativePosition(position, config.position());
|
||||||
|
AABB aabb = AABB.makeBoundingBox(pos, config.width(), config.height());
|
||||||
|
this.collider = createCollider(furniture.world(), pos, aabb, false, config.blocksBuilding(), config.canBeHitByProjectile());
|
||||||
|
int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet();
|
||||||
|
List<Object> packets = new ArrayList<>(3);
|
||||||
|
packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||||
|
entityId, UUID.randomUUID(), position.x, position.y, position.z, 0, position.yRot,
|
||||||
|
config.entityType(), 0, CoreReflections.instance$Vec3$Zero, 0
|
||||||
|
));
|
||||||
|
packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId, config.cachedValues()));
|
||||||
|
if (VersionHelper.isOrAbove1_20_5()) {
|
||||||
|
Object attributeIns = FastNMS.INSTANCE.constructor$AttributeInstance(MAttributeHolders.SCALE, (Consumer<?>) (o) -> {});
|
||||||
|
FastNMS.INSTANCE.method$AttributeInstance$setBaseValue(attributeIns, config.scale());
|
||||||
|
packets.add(FastNMS.INSTANCE.constructor$ClientboundUpdateAttributesPacket(entityId, Collections.singletonList(attributeIns)));
|
||||||
|
}
|
||||||
|
this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets);
|
||||||
|
this.part = new FurnitureHitboxPart(entityId, aabb, pos, false);
|
||||||
|
this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(MiscUtils.init(new IntArrayList(), l -> l.add(entityId)));
|
||||||
|
this.entityId = entityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Collider> colliders() {
|
||||||
|
return List.of(this.collider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<FurnitureHitboxPart> parts() {
|
||||||
|
return List.of(this.part);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void show(Player player) {
|
||||||
|
player.sendPacket(this.spawnPacket, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void hide(Player player) {
|
||||||
|
player.sendPacket(this.despawnPacket, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void collectVirtualEntityId(Consumer<Integer> collector) {
|
||||||
|
collector.accept(this.entityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomFurnitureHitboxConfig config() {
|
||||||
|
return this.config;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.entity.furniture.hitbox;
|
||||||
|
|
||||||
|
import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData;
|
||||||
|
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries;
|
||||||
|
import net.momirealms.craftengine.bukkit.util.KeyUtils;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.Furniture;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.hitbox.AbstractFurnitureHitBoxConfig;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfigFactory;
|
||||||
|
import net.momirealms.craftengine.core.entity.seat.SeatConfig;
|
||||||
|
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
|
||||||
|
import net.momirealms.craftengine.core.util.Key;
|
||||||
|
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||||
|
import net.momirealms.craftengine.core.world.Vec3d;
|
||||||
|
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||||
|
import net.momirealms.craftengine.core.world.collision.AABB;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class CustomFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig<CustomFurnitureHitbox> {
|
||||||
|
public static final Factory FACTORY = new Factory();
|
||||||
|
private final float scale;
|
||||||
|
private final Object entityType;
|
||||||
|
private final List<Object> cachedValues = new ArrayList<>();
|
||||||
|
private final float width;
|
||||||
|
private final float height;
|
||||||
|
|
||||||
|
public CustomFurnitureHitboxConfig(SeatConfig[] seats,
|
||||||
|
Vector3f position,
|
||||||
|
boolean canUseItemOn,
|
||||||
|
boolean blocksBuilding,
|
||||||
|
boolean canBeHitByProjectile,
|
||||||
|
float width,
|
||||||
|
float height,
|
||||||
|
boolean fixed,
|
||||||
|
float scale,
|
||||||
|
Object type) {
|
||||||
|
super(seats, position, canUseItemOn, blocksBuilding, canBeHitByProjectile);
|
||||||
|
this.scale = scale;
|
||||||
|
this.entityType = type;
|
||||||
|
this.width = fixed ? width : width * scale;
|
||||||
|
this.height = fixed ? height : height * scale;
|
||||||
|
BaseEntityData.NoGravity.addEntityDataIfNotDefaultValue(true, this.cachedValues);
|
||||||
|
BaseEntityData.Silent.addEntityDataIfNotDefaultValue(true, this.cachedValues);
|
||||||
|
BaseEntityData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float scale() {
|
||||||
|
return this.scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object entityType() {
|
||||||
|
return this.entityType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Object> cachedValues() {
|
||||||
|
return this.cachedValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float width() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float height() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void prepareBoundingBox(WorldPosition targetPos, Consumer<AABB> aabbConsumer, boolean ignoreBlocksBuilding) {
|
||||||
|
if (this.blocksBuilding || ignoreBlocksBuilding) {
|
||||||
|
Vec3d relativePosition = Furniture.getRelativePosition(targetPos, this.position);
|
||||||
|
aabbConsumer.accept(AABB.makeBoundingBox(relativePosition, this.width, this.height));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomFurnitureHitbox create(Furniture furniture) {
|
||||||
|
return new CustomFurnitureHitbox(furniture, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Factory implements FurnitureHitBoxConfigFactory<CustomFurnitureHitbox> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomFurnitureHitboxConfig create(Map<String, Object> arguments) {
|
||||||
|
Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position");
|
||||||
|
float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", 1), "scale");
|
||||||
|
String type = (String) arguments.getOrDefault("entity-type", "slime");
|
||||||
|
Object nmsEntityType = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.ENTITY_TYPE, KeyUtils.toResourceLocation(Key.of(type)));
|
||||||
|
if (nmsEntityType == null) {
|
||||||
|
throw new LocalizedResourceConfigException("warning.config.furniture.hitbox.custom.invalid_entity", new IllegalArgumentException("EntityType not found: " + type), type);
|
||||||
|
}
|
||||||
|
float width;
|
||||||
|
float height;
|
||||||
|
boolean fixed;
|
||||||
|
try {
|
||||||
|
Object dimensions = CoreReflections.field$EntityType$dimensions.get(nmsEntityType);
|
||||||
|
width = CoreReflections.field$EntityDimensions$width.getFloat(dimensions);
|
||||||
|
height = CoreReflections.field$EntityDimensions$height.getFloat(dimensions);
|
||||||
|
fixed = CoreReflections.field$EntityDimensions$fixed.getBoolean(dimensions);
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
throw new RuntimeException("Failed to get dimensions for " + nmsEntityType, e);
|
||||||
|
}
|
||||||
|
boolean canUseItemOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", false), "can-use-item-on");
|
||||||
|
boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", false), "can-be-hit-by-projectile");
|
||||||
|
boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building");
|
||||||
|
return new CustomFurnitureHitboxConfig(SeatConfig.fromObj(arguments.get("seats")), position, canUseItemOn, blocksBuilding, canBeHitByProjectile, width, height, fixed, scale, nmsEntityType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
package net.momirealms.craftengine.bukkit.entity.furniture.hitbox;
|
|
||||||
|
|
||||||
import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData;
|
|
||||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
|
||||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
|
||||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeHolders;
|
|
||||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections;
|
|
||||||
import net.momirealms.craftengine.core.entity.furniture.*;
|
|
||||||
import net.momirealms.craftengine.core.entity.seat.SeatConfig;
|
|
||||||
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
|
|
||||||
import net.momirealms.craftengine.core.util.Key;
|
|
||||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
|
||||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
|
||||||
import net.momirealms.craftengine.core.world.WorldPosition;
|
|
||||||
import net.momirealms.craftengine.core.world.collision.AABB;
|
|
||||||
import org.bukkit.NamespacedKey;
|
|
||||||
import org.bukkit.Registry;
|
|
||||||
import org.bukkit.entity.EntityType;
|
|
||||||
import org.joml.Quaternionf;
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class CustomHitBoxConfig extends AbstractHitBoxConfig {
|
|
||||||
public static final Factory FACTORY = new Factory();
|
|
||||||
private final float scale;
|
|
||||||
private final EntityType entityType;
|
|
||||||
private final List<Object> cachedValues = new ArrayList<>();
|
|
||||||
|
|
||||||
public CustomHitBoxConfig(SeatConfig[] seats, Vector3f position, EntityType type, float scale, boolean blocksBuilding, boolean canBeHitByProjectile) {
|
|
||||||
super(seats, position, false, blocksBuilding, canBeHitByProjectile);
|
|
||||||
this.scale = scale;
|
|
||||||
this.entityType = type;
|
|
||||||
BaseEntityData.NoGravity.addEntityDataIfNotDefaultValue(true, this.cachedValues);
|
|
||||||
BaseEntityData.Silent.addEntityDataIfNotDefaultValue(true, this.cachedValues);
|
|
||||||
BaseEntityData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
public EntityType entityType() {
|
|
||||||
return this.entityType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float scale() {
|
|
||||||
return this.scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Key type() {
|
|
||||||
return HitBoxTypes.CUSTOM;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initPacketsAndColliders(int[] entityId, WorldPosition position, Quaternionf conjugated, BiConsumer<Object, Boolean> packets, Consumer<Collider> collider, Consumer<HitBoxPart> aabb) {
|
|
||||||
Vector3f offset = conjugated.transform(new Vector3f(position()));
|
|
||||||
try {
|
|
||||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
|
||||||
entityId[0], UUID.randomUUID(), position.x() + offset.x, position.y() + offset.y, position.z() - offset.z, 0, position.yRot(),
|
|
||||||
FastNMS.INSTANCE.method$CraftEntityType$toNMSEntityType(this.entityType), 0, CoreReflections.instance$Vec3$Zero, 0
|
|
||||||
), true);
|
|
||||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId[0], List.copyOf(this.cachedValues)), true);
|
|
||||||
if (VersionHelper.isOrAbove1_20_5() && this.scale != 1) {
|
|
||||||
Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer<?>) (o) -> {});
|
|
||||||
CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, this.scale);
|
|
||||||
packets.accept(NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityId[0], Collections.singletonList(attributeInstance)), false);
|
|
||||||
}
|
|
||||||
} catch (ReflectiveOperationException e) {
|
|
||||||
throw new RuntimeException("Failed to construct custom hitbox spawn packet", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initShapeForPlacement(double x, double y, double z, float yaw, Quaternionf conjugated, Consumer<AABB> aabbs) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int[] acquireEntityIds(Supplier<Integer> entityIdSupplier) {
|
|
||||||
return new int[] {entityIdSupplier.get()};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Factory implements HitBoxConfigFactory {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HitBoxConfig create(Map<String, Object> arguments) {
|
|
||||||
Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position");
|
|
||||||
float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", 1), "scale");
|
|
||||||
String type = (String) arguments.getOrDefault("entity-type", "slime");
|
|
||||||
EntityType entityType = Registry.ENTITY_TYPE.get(new NamespacedKey("minecraft", type));
|
|
||||||
if (entityType == null) {
|
|
||||||
throw new LocalizedResourceConfigException("warning.config.furniture.hitbox.custom.invalid_entity", new IllegalArgumentException("EntityType not found: " + type), type);
|
|
||||||
}
|
|
||||||
boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", false), "can-be-hit-by-projectile");
|
|
||||||
boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building");
|
|
||||||
return new CustomHitBoxConfig(SeatConfig.fromObj(arguments.get("seats")), position, entityType, scale, blocksBuilding, canBeHitByProjectile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.entity.furniture.hitbox;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
|
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeHolders;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.Collider;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.Furniture;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart;
|
||||||
|
import net.momirealms.craftengine.core.entity.player.Player;
|
||||||
|
import net.momirealms.craftengine.core.world.Vec3d;
|
||||||
|
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||||
|
import net.momirealms.craftengine.core.world.collision.AABB;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class HappyGhastFurnitureHitbox extends AbstractFurnitureHitBox {
|
||||||
|
private final HappyGhastFurnitureHitboxConfig config;
|
||||||
|
private final Collider collider;
|
||||||
|
private final Object despawnPacket;
|
||||||
|
private final FurnitureHitboxPart part;
|
||||||
|
private final Vec3d pos;
|
||||||
|
private final List<Object> packets;
|
||||||
|
private final int entityId;
|
||||||
|
private final float yaw;
|
||||||
|
|
||||||
|
public HappyGhastFurnitureHitbox(Furniture furniture, HappyGhastFurnitureHitboxConfig config) {
|
||||||
|
super(furniture, config);
|
||||||
|
this.config = config;
|
||||||
|
WorldPosition position = furniture.position();
|
||||||
|
this.pos = Furniture.getRelativePosition(position, config.position());
|
||||||
|
double bbSize = 4 * config.scale();
|
||||||
|
AABB aabb = AABB.makeBoundingBox(this.pos, bbSize, bbSize);
|
||||||
|
this.yaw = position.yRot;
|
||||||
|
this.entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet();
|
||||||
|
this.packets = new ArrayList<>(3);
|
||||||
|
this.packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(this.entityId, config.cachedValues()));
|
||||||
|
if (config.scale() != 1) {
|
||||||
|
Object attributeIns = FastNMS.INSTANCE.constructor$AttributeInstance(MAttributeHolders.SCALE, (Consumer<?>) (o) -> {});
|
||||||
|
FastNMS.INSTANCE.method$AttributeInstance$setBaseValue(attributeIns, config.scale());
|
||||||
|
this.packets.add(FastNMS.INSTANCE.constructor$ClientboundUpdateAttributesPacket(this.entityId, Collections.singletonList(attributeIns)));
|
||||||
|
}
|
||||||
|
this.packets.add(FastNMS.INSTANCE.constructor$ClientboundEntityPositionSyncPacket(this.entityId, this.pos.x, this.pos.y, this.pos.z, 0, position.yRot, false));
|
||||||
|
this.collider = createCollider(furniture.world(), this.pos, aabb, config.hardCollision(), config.blocksBuilding(), config.canBeHitByProjectile());
|
||||||
|
this.part = new FurnitureHitboxPart(this.entityId, aabb, this.pos, false);
|
||||||
|
this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList() {{ add(entityId); }});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Collider> colliders() {
|
||||||
|
return List.of(this.collider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<FurnitureHitboxPart> parts() {
|
||||||
|
return List.of(this.part);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void show(Player player) {
|
||||||
|
List<Object> packets = new ArrayList<>();
|
||||||
|
packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||||
|
this.entityId, UUID.randomUUID(), this.pos.x, player.y() - (this.config.scale() * 4 + 16), this.pos.z, 0, this.yaw,
|
||||||
|
MEntityTypes.HAPPY_GHAST, 0, CoreReflections.instance$Vec3$Zero, 0
|
||||||
|
));
|
||||||
|
packets.addAll(this.packets);
|
||||||
|
player.sendPacket(FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void hide(Player player) {
|
||||||
|
player.sendPacket(this.despawnPacket, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void collectVirtualEntityId(Consumer<Integer> collector) {
|
||||||
|
collector.accept(this.entityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HappyGhastFurnitureHitboxConfig config() {
|
||||||
|
return this.config;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.entity.furniture.hitbox;
|
||||||
|
|
||||||
|
import net.momirealms.craftengine.bukkit.entity.data.HappyGhastData;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.Furniture;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.hitbox.AbstractFurnitureHitBoxConfig;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfigFactory;
|
||||||
|
import net.momirealms.craftengine.core.entity.seat.SeatConfig;
|
||||||
|
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||||
|
import net.momirealms.craftengine.core.world.Vec3d;
|
||||||
|
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||||
|
import net.momirealms.craftengine.core.world.collision.AABB;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class HappyGhastFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig<HappyGhastFurnitureHitbox> {
|
||||||
|
public static final Factory FACTORY = new Factory();
|
||||||
|
private final double scale;
|
||||||
|
private final boolean hardCollision;
|
||||||
|
private final List<Object> cachedValues = new ArrayList<>(3);
|
||||||
|
|
||||||
|
public HappyGhastFurnitureHitboxConfig(SeatConfig[] seats,
|
||||||
|
Vector3f position,
|
||||||
|
boolean canUseItemOn,
|
||||||
|
boolean blocksBuilding,
|
||||||
|
boolean canBeHitByProjectile,
|
||||||
|
double scale,
|
||||||
|
boolean hardCollision) {
|
||||||
|
super(seats, position, canUseItemOn, blocksBuilding, canBeHitByProjectile);
|
||||||
|
this.scale = scale;
|
||||||
|
this.hardCollision = hardCollision;
|
||||||
|
HappyGhastData.StaysStill.addEntityDataIfNotDefaultValue(hardCollision, this.cachedValues);
|
||||||
|
HappyGhastData.MobFlags.addEntityDataIfNotDefaultValue((byte) 0x01, this.cachedValues); // NO AI
|
||||||
|
HappyGhastData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedValues); // Invisible
|
||||||
|
}
|
||||||
|
|
||||||
|
public double scale() {
|
||||||
|
return scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hardCollision() {
|
||||||
|
return hardCollision;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Object> cachedValues() {
|
||||||
|
return cachedValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HappyGhastFurnitureHitbox create(Furniture furniture) {
|
||||||
|
return new HappyGhastFurnitureHitbox(furniture, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void prepareBoundingBox(WorldPosition targetPos, Consumer<AABB> aabbConsumer, boolean ignoreBlocksBuilding) {
|
||||||
|
if (this.blocksBuilding || ignoreBlocksBuilding) {
|
||||||
|
Vec3d relativePosition = Furniture.getRelativePosition(targetPos, this.position);
|
||||||
|
aabbConsumer.accept(AABB.makeBoundingBox(relativePosition, 4 * this.scale, 4 * this.scale));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Factory implements FurnitureHitBoxConfigFactory<HappyGhastFurnitureHitbox> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FurnitureHitBoxConfig<HappyGhastFurnitureHitbox> create(Map<String, Object> arguments) {
|
||||||
|
Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0), "position");
|
||||||
|
boolean canUseItemOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", true), "can-use-item-on");
|
||||||
|
boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building");
|
||||||
|
boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", true), "can-be-hit-by-projectile");
|
||||||
|
double scale = ResourceConfigUtils.getAsDouble(arguments.getOrDefault("scale", 1), "scale");
|
||||||
|
boolean hardCollision = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("hard-collision", true), "hard-collision");
|
||||||
|
return new HappyGhastFurnitureHitboxConfig(
|
||||||
|
SeatConfig.fromObj(arguments.get("seats")),
|
||||||
|
position, canUseItemOn, blocksBuilding, canBeHitByProjectile,
|
||||||
|
scale, hardCollision
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
package net.momirealms.craftengine.bukkit.entity.furniture.hitbox;
|
|
||||||
|
|
||||||
import net.momirealms.craftengine.bukkit.entity.data.HappyGhastData;
|
|
||||||
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitCollider;
|
|
||||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
|
||||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
|
||||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeHolders;
|
|
||||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
|
|
||||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections;
|
|
||||||
import net.momirealms.craftengine.core.entity.furniture.*;
|
|
||||||
import net.momirealms.craftengine.core.entity.seat.SeatConfig;
|
|
||||||
import net.momirealms.craftengine.core.util.Key;
|
|
||||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
|
||||||
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.joml.Quaternionf;
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class HappyGhastHitBoxConfig extends AbstractHitBoxConfig {
|
|
||||||
public static final Factory FACTORY = new Factory();
|
|
||||||
private final double scale;
|
|
||||||
private final boolean hardCollision;
|
|
||||||
private final List<Object> cachedValues = new ArrayList<>();
|
|
||||||
|
|
||||||
public HappyGhastHitBoxConfig(SeatConfig[] seats, Vector3f position, double scale, boolean canUseOn, boolean blocksBuilding, boolean canBeHitByProjectile, boolean hardCollision) {
|
|
||||||
super(seats, position, canUseOn, blocksBuilding, canBeHitByProjectile);
|
|
||||||
this.scale = scale;
|
|
||||||
this.hardCollision = hardCollision;
|
|
||||||
HappyGhastData.StaysStill.addEntityDataIfNotDefaultValue(hardCollision, this.cachedValues);
|
|
||||||
HappyGhastData.MobFlags.addEntityDataIfNotDefaultValue((byte) 0x01, this.cachedValues); // NO AI
|
|
||||||
HappyGhastData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedValues); // Invisible
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Key type() {
|
|
||||||
return HitBoxTypes.HAPPY_GHAST;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double scale() {
|
|
||||||
return this.scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hardCollision() {
|
|
||||||
return this.hardCollision;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initPacketsAndColliders(int[] entityIds,
|
|
||||||
WorldPosition position,
|
|
||||||
Quaternionf conjugated,
|
|
||||||
BiConsumer<Object, Boolean> packets,
|
|
||||||
Consumer<Collider> collider,
|
|
||||||
Consumer<HitBoxPart> aabb) {
|
|
||||||
Vector3f offset = conjugated.transform(new Vector3f(position()));
|
|
||||||
try {
|
|
||||||
double x = position.x();
|
|
||||||
double y = position.y();
|
|
||||||
double z = position.z();
|
|
||||||
float yaw = position.yRot();
|
|
||||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
|
||||||
entityIds[0], UUID.randomUUID(), x + offset.x, y + offset.y, z - offset.z, 0, yaw,
|
|
||||||
MEntityTypes.HAPPY_GHAST, 0, CoreReflections.instance$Vec3$Zero, 0
|
|
||||||
), true);
|
|
||||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[0], List.copyOf(this.cachedValues)), true);
|
|
||||||
if (VersionHelper.isOrAbove1_20_5() && this.scale != 1) {
|
|
||||||
Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer<?>) (o) -> {});
|
|
||||||
CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, this.scale);
|
|
||||||
packets.accept(NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityIds[0], Collections.singletonList(attributeInstance)), false);
|
|
||||||
}
|
|
||||||
if (this.hardCollision) {
|
|
||||||
collider.accept(this.createCollider(position.world(), offset, x, y, z, entityIds[0], aabb));
|
|
||||||
}
|
|
||||||
} catch (ReflectiveOperationException e) {
|
|
||||||
throw new RuntimeException("Failed to construct custom hitbox spawn packet", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collider createCollider(World world, Vector3f offset, double x, double y, double z, int entityId, Consumer<HitBoxPart> aabb) {
|
|
||||||
AABB ceAABB = createAABB(offset, x, y, z);
|
|
||||||
Object level = world.serverWorld();
|
|
||||||
Object nmsAABB = FastNMS.INSTANCE.constructor$AABB(ceAABB.minX, ceAABB.minY, ceAABB.minZ, ceAABB.maxX, ceAABB.maxY, ceAABB.maxZ);
|
|
||||||
aabb.accept(new HitBoxPart(entityId, ceAABB, new Vec3d(x, y, z)));
|
|
||||||
return new BukkitCollider(level, nmsAABB, x, y, z, this.canBeHitByProjectile(), true, this.blocksBuilding());
|
|
||||||
}
|
|
||||||
|
|
||||||
public AABB createAABB(Vector3f offset, double x, double y, double z) {
|
|
||||||
double baseSize = 4.0 * this.scale;
|
|
||||||
double halfSize = baseSize * 0.5;
|
|
||||||
double minX = x - halfSize + offset.x();
|
|
||||||
double maxX = x + halfSize + offset.x();
|
|
||||||
double minY = y + offset.y();
|
|
||||||
double maxY = y + baseSize + offset.y();
|
|
||||||
double minZ = z - halfSize - offset.z();
|
|
||||||
double maxZ = z + halfSize - offset.z();
|
|
||||||
return new AABB(minX, minY, minZ, maxX, maxY, maxZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initShapeForPlacement(double x, double y, double z, float yaw, Quaternionf conjugated, Consumer<AABB> aabbs) {
|
|
||||||
if (!this.hardCollision) return;
|
|
||||||
Vector3f offset = conjugated.transform(new Vector3f(position()));
|
|
||||||
AABB aabb = createAABB(offset, x, y, z);
|
|
||||||
aabbs.accept(aabb);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int[] acquireEntityIds(Supplier<Integer> entityIdSupplier) {
|
|
||||||
return new int[] {entityIdSupplier.get()};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Factory implements HitBoxConfigFactory {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HitBoxConfig create(Map<String, Object> arguments) {
|
|
||||||
if (!VersionHelper.isOrAbove1_21_6()) {
|
|
||||||
throw new UnsupportedOperationException("HappyGhastHitBox is only supported on 1.21.6+");
|
|
||||||
}
|
|
||||||
double scale = ResourceConfigUtils.getAsDouble(arguments.getOrDefault("scale", 1), "scale");
|
|
||||||
boolean hardCollision = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("hard-collision", true), "hard-collision");
|
|
||||||
boolean canUseOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", true), "can-use-item-on");
|
|
||||||
boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", true), "can-be-hit-by-projectile");
|
|
||||||
boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building");
|
|
||||||
return new HappyGhastHitBoxConfig(
|
|
||||||
SeatConfig.fromObj(arguments.get("seats")),
|
|
||||||
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"),
|
|
||||||
scale, canUseOn, blocksBuilding, canBeHitByProjectile, hardCollision
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.entity.furniture.hitbox;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
|
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.Collider;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.Furniture;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart;
|
||||||
|
import net.momirealms.craftengine.core.entity.player.Player;
|
||||||
|
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||||
|
import net.momirealms.craftengine.core.world.Vec3d;
|
||||||
|
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||||
|
import net.momirealms.craftengine.core.world.collision.AABB;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class InteractionFurnitureHitbox extends AbstractFurnitureHitBox {
|
||||||
|
private final InteractionFurnitureHitboxConfig config;
|
||||||
|
private final Collider collider;
|
||||||
|
private final Object spawnPacket;
|
||||||
|
private final Object despawnPacket;
|
||||||
|
private final FurnitureHitboxPart part;
|
||||||
|
private final int entityId;
|
||||||
|
|
||||||
|
public InteractionFurnitureHitbox(Furniture furniture, InteractionFurnitureHitboxConfig config) {
|
||||||
|
super(furniture, config);
|
||||||
|
this.config = config;
|
||||||
|
WorldPosition position = furniture.position();
|
||||||
|
Vec3d pos = Furniture.getRelativePosition(position, config.position());
|
||||||
|
AABB aabb = AABB.makeBoundingBox(pos, config.size().x, config.size().y);
|
||||||
|
this.collider = createCollider(furniture.world(), pos, aabb, false, config.blocksBuilding(), config.canBeHitByProjectile());
|
||||||
|
int interactionId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet();
|
||||||
|
this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(List.of(
|
||||||
|
FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||||
|
interactionId, UUID.randomUUID(), pos.x, pos.y, pos.z, 0, position.yRot,
|
||||||
|
MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0
|
||||||
|
),
|
||||||
|
FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(interactionId, config.cachedValues())
|
||||||
|
));
|
||||||
|
this.part = new FurnitureHitboxPart(interactionId, aabb, pos, config.responsive());
|
||||||
|
this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(MiscUtils.init(new IntArrayList(), l -> l.add(interactionId)));
|
||||||
|
this.entityId = interactionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Collider> colliders() {
|
||||||
|
return List.of(this.collider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<FurnitureHitboxPart> parts() {
|
||||||
|
return List.of(this.part);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InteractionFurnitureHitboxConfig config() {
|
||||||
|
return this.config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void collectVirtualEntityId(Consumer<Integer> collector) {
|
||||||
|
collector.accept(this.entityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void show(Player player) {
|
||||||
|
player.sendPacket(this.spawnPacket, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void hide(Player player) {
|
||||||
|
player.sendPacket(this.despawnPacket, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.entity.furniture.hitbox;
|
||||||
|
|
||||||
|
import net.momirealms.craftengine.bukkit.entity.data.BaseEntityData;
|
||||||
|
import net.momirealms.craftengine.bukkit.entity.data.InteractionEntityData;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.Furniture;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.hitbox.AbstractFurnitureHitBoxConfig;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfigFactory;
|
||||||
|
import net.momirealms.craftengine.core.entity.seat.SeatConfig;
|
||||||
|
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||||
|
import net.momirealms.craftengine.core.world.Vec3d;
|
||||||
|
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||||
|
import net.momirealms.craftengine.core.world.collision.AABB;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class InteractionFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig<InteractionFurnitureHitbox> {
|
||||||
|
public static final Factory FACTORY = new Factory();
|
||||||
|
public static final InteractionFurnitureHitboxConfig DEFAULT = new InteractionFurnitureHitboxConfig();
|
||||||
|
|
||||||
|
private final Vector3f size;
|
||||||
|
private final boolean responsive;
|
||||||
|
private final boolean invisible;
|
||||||
|
private final List<Object> cachedValues = new ArrayList<>(4);
|
||||||
|
|
||||||
|
public InteractionFurnitureHitboxConfig(SeatConfig[] seats,
|
||||||
|
Vector3f position,
|
||||||
|
boolean canUseItemOn,
|
||||||
|
boolean blocksBuilding,
|
||||||
|
boolean canBeHitByProjectile,
|
||||||
|
boolean invisible,
|
||||||
|
Vector3f size,
|
||||||
|
boolean interactive) {
|
||||||
|
super(seats, position, canUseItemOn, blocksBuilding, canBeHitByProjectile);
|
||||||
|
this.size = size;
|
||||||
|
this.responsive = interactive;
|
||||||
|
this.invisible = invisible;
|
||||||
|
InteractionEntityData.Height.addEntityDataIfNotDefaultValue(size.y, cachedValues);
|
||||||
|
InteractionEntityData.Width.addEntityDataIfNotDefaultValue(size.x, cachedValues);
|
||||||
|
InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(interactive, cachedValues);
|
||||||
|
if (invisible) {
|
||||||
|
BaseEntityData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, cachedValues);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private InteractionFurnitureHitboxConfig() {
|
||||||
|
super(new SeatConfig[0], new Vector3f(), false, false, false);
|
||||||
|
this.size = new Vector3f(1);
|
||||||
|
this.responsive = true;
|
||||||
|
this.invisible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector3f size() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean responsive() {
|
||||||
|
return responsive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean invisible() {
|
||||||
|
return invisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Object> cachedValues() {
|
||||||
|
return cachedValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void prepareBoundingBox(WorldPosition targetPos, Consumer<AABB> aabbConsumer, boolean ignoreBlocksBuilding) {
|
||||||
|
if (this.blocksBuilding || ignoreBlocksBuilding) {
|
||||||
|
Vec3d relativePosition = Furniture.getRelativePosition(targetPos, this.position);
|
||||||
|
aabbConsumer.accept(AABB.makeBoundingBox(relativePosition, size.x, size.y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InteractionFurnitureHitbox create(Furniture furniture) {
|
||||||
|
return new InteractionFurnitureHitbox(furniture, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Factory implements FurnitureHitBoxConfigFactory<InteractionFurnitureHitbox> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InteractionFurnitureHitboxConfig create(Map<String, Object> arguments) {
|
||||||
|
Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0), "position");
|
||||||
|
float width;
|
||||||
|
float height;
|
||||||
|
if (arguments.containsKey("scale")) {
|
||||||
|
String[] split = arguments.get("scale").toString().split(",");
|
||||||
|
width = Float.parseFloat(split[0]);
|
||||||
|
height = Float.parseFloat(split[1]);
|
||||||
|
} else {
|
||||||
|
width = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("width", 1), "width");
|
||||||
|
height = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("height", 1), "height");
|
||||||
|
}
|
||||||
|
boolean canUseOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", false), "can-use-item-on");
|
||||||
|
boolean interactive = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("interactive", true), "interactive");
|
||||||
|
boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", false), "can-be-hit-by-projectile");
|
||||||
|
boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building");
|
||||||
|
boolean invisible = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("invisible", false), "invisible");
|
||||||
|
return new InteractionFurnitureHitboxConfig(
|
||||||
|
SeatConfig.fromObj(arguments.get("seats")),
|
||||||
|
position, canUseOn, blocksBuilding, canBeHitByProjectile, invisible,
|
||||||
|
new Vector3f(width, height, width),
|
||||||
|
interactive
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
package net.momirealms.craftengine.bukkit.entity.furniture.hitbox;
|
|
||||||
|
|
||||||
import net.momirealms.craftengine.bukkit.entity.data.InteractionEntityData;
|
|
||||||
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitCollider;
|
|
||||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
|
||||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
|
||||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
|
|
||||||
import net.momirealms.craftengine.core.entity.furniture.*;
|
|
||||||
import net.momirealms.craftengine.core.entity.seat.SeatConfig;
|
|
||||||
import net.momirealms.craftengine.core.util.Key;
|
|
||||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
|
||||||
import net.momirealms.craftengine.core.world.Vec3d;
|
|
||||||
import net.momirealms.craftengine.core.world.WorldPosition;
|
|
||||||
import net.momirealms.craftengine.core.world.collision.AABB;
|
|
||||||
import org.joml.Quaternionf;
|
|
||||||
import org.joml.Vector3f;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class InteractionHitBoxConfig extends AbstractHitBoxConfig {
|
|
||||||
public static final Factory FACTORY = new Factory();
|
|
||||||
public static final InteractionHitBoxConfig DEFAULT = new InteractionHitBoxConfig(new SeatConfig[0], new Vector3f(), new Vector3f(1,1,1), true, false, false, false);
|
|
||||||
|
|
||||||
private final Vector3f size;
|
|
||||||
private final boolean responsive;
|
|
||||||
private final List<Object> cachedValues = new ArrayList<>();
|
|
||||||
|
|
||||||
public InteractionHitBoxConfig(SeatConfig[] seats, Vector3f position, Vector3f size, boolean responsive, boolean canUseOn, boolean blocksBuilding, boolean canBeHitByProjectile) {
|
|
||||||
super(seats, position, canUseOn, blocksBuilding, canBeHitByProjectile);
|
|
||||||
this.size = size;
|
|
||||||
this.responsive = responsive;
|
|
||||||
InteractionEntityData.Height.addEntityDataIfNotDefaultValue(size.y, cachedValues);
|
|
||||||
InteractionEntityData.Width.addEntityDataIfNotDefaultValue(size.x, cachedValues);
|
|
||||||
InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(responsive, cachedValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean responsive() {
|
|
||||||
return this.responsive;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector3f size() {
|
|
||||||
return this.size;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Key type() {
|
|
||||||
return HitBoxTypes.INTERACTION;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initPacketsAndColliders(int[] entityId,
|
|
||||||
WorldPosition position,
|
|
||||||
Quaternionf conjugated,
|
|
||||||
BiConsumer<Object, Boolean> packets,
|
|
||||||
Consumer<Collider> collider,
|
|
||||||
Consumer<HitBoxPart> aabb) {
|
|
||||||
Vector3f offset = conjugated.transform(new Vector3f(position()));
|
|
||||||
double x = position.x();
|
|
||||||
double y = position.y();
|
|
||||||
double z = position.z();
|
|
||||||
float yaw = position.yRot();
|
|
||||||
Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y, z - offset.z);
|
|
||||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
|
||||||
entityId[0], UUID.randomUUID(), vec3d.x, vec3d.y, vec3d.z, 0, yaw,
|
|
||||||
MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0
|
|
||||||
), true);
|
|
||||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId[0], List.copyOf(this.cachedValues)), true);
|
|
||||||
aabb.accept(new HitBoxPart(entityId[0], AABB.fromInteraction(vec3d, this.size.x, this.size.y), vec3d));
|
|
||||||
if (blocksBuilding() || this.canBeHitByProjectile()) {
|
|
||||||
AABB ceAABB = AABB.fromInteraction(vec3d, this.size.x, this.size.y);
|
|
||||||
Object nmsAABB = FastNMS.INSTANCE.constructor$AABB(ceAABB.minX, ceAABB.minY, ceAABB.minZ, ceAABB.maxX, ceAABB.maxY, ceAABB.maxZ);
|
|
||||||
collider.accept(new BukkitCollider(position.world().serverWorld(), nmsAABB, x, y, z, this.canBeHitByProjectile(), false, this.blocksBuilding()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initShapeForPlacement(double x, double y, double z, float yaw, Quaternionf conjugated, Consumer<AABB> aabbs) {
|
|
||||||
if (blocksBuilding()) {
|
|
||||||
Vector3f offset = conjugated.transform(new Vector3f(position()));
|
|
||||||
AABB ceAABB = AABB.fromInteraction(new Vec3d(x + offset.x, y + offset.y, z - offset.z), this.size.x, this.size.y);
|
|
||||||
aabbs.accept(ceAABB);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int[] acquireEntityIds(Supplier<Integer> entityIdSupplier) {
|
|
||||||
return new int[] {entityIdSupplier.get()};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Factory implements HitBoxConfigFactory {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HitBoxConfig create(Map<String, Object> arguments) {
|
|
||||||
Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position");
|
|
||||||
float width;
|
|
||||||
float height;
|
|
||||||
if (arguments.containsKey("scale")) {
|
|
||||||
String[] split = arguments.get("scale").toString().split(",");
|
|
||||||
width = Float.parseFloat(split[0]);
|
|
||||||
height = Float.parseFloat(split[1]);
|
|
||||||
} else {
|
|
||||||
width = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("width", 1), "width");
|
|
||||||
height = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("height", 1), "height");
|
|
||||||
}
|
|
||||||
boolean canUseOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", false), "can-use-item-on");
|
|
||||||
boolean interactive = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("interactive", true), "interactive");
|
|
||||||
boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", false), "can-be-hit-by-projectile");
|
|
||||||
boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building");
|
|
||||||
return new InteractionHitBoxConfig(
|
|
||||||
SeatConfig.fromObj(arguments.get("seats")),
|
|
||||||
position,
|
|
||||||
new Vector3f(width, height, width),
|
|
||||||
interactive, canUseOn, blocksBuilding, canBeHitByProjectile
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.entity.furniture.hitbox;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
|
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeHolders;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.Collider;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.Furniture;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart;
|
||||||
|
import net.momirealms.craftengine.core.entity.player.Player;
|
||||||
|
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||||
|
import net.momirealms.craftengine.core.util.QuaternionUtils;
|
||||||
|
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||||
|
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||||
|
import org.joml.Quaternionf;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class ShulkerFurnitureHitbox extends AbstractFurnitureHitBox {
|
||||||
|
private final ShulkerFurnitureHitboxConfig config;
|
||||||
|
private final List<FurnitureHitboxPart> parts;
|
||||||
|
private final List<Collider> colliders;
|
||||||
|
private final Object spawnPacket;
|
||||||
|
private final Object despawnPacket;
|
||||||
|
private final int[] entityIds;
|
||||||
|
|
||||||
|
public ShulkerFurnitureHitbox(Furniture furniture, ShulkerFurnitureHitboxConfig config) {
|
||||||
|
super(furniture, config);
|
||||||
|
this.config = config;
|
||||||
|
this.entityIds = acquireEntityIds(CoreReflections.instance$Entity$ENTITY_COUNTER::incrementAndGet);
|
||||||
|
WorldPosition position = furniture.position();
|
||||||
|
Quaternionf conjugated = QuaternionUtils.toQuaternionf(0f, (float) Math.toRadians(180 - position.yRot()), 0f).conjugate();
|
||||||
|
Vector3f offset = conjugated.transform(new Vector3f(config.position()));
|
||||||
|
double x = position.x();
|
||||||
|
double y = position.y();
|
||||||
|
double z = position.z();
|
||||||
|
float yaw = position.yRot();
|
||||||
|
double originalY = y + offset.y;
|
||||||
|
double integerPart = Math.floor(originalY);
|
||||||
|
double fractionalPart = originalY - integerPart;
|
||||||
|
double processedY = (fractionalPart >= 0.5) ? integerPart + 1 : originalY;
|
||||||
|
List<Object> packets = new ArrayList<>();
|
||||||
|
List<Collider> colliders = new ArrayList<>();
|
||||||
|
List<FurnitureHitboxPart> parts = new ArrayList<>();
|
||||||
|
|
||||||
|
packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||||
|
entityIds[0], UUID.randomUUID(), x + offset.x, originalY, z - offset.z, 0, yaw,
|
||||||
|
MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0
|
||||||
|
));
|
||||||
|
packets.add(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||||
|
entityIds[1], UUID.randomUUID(), x + offset.x, processedY, z - offset.z, 0, yaw,
|
||||||
|
MEntityTypes.SHULKER, 0, CoreReflections.instance$Vec3$Zero, 0
|
||||||
|
));
|
||||||
|
packets.add(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], config.cachedShulkerValues()));
|
||||||
|
packets.add(FastNMS.INSTANCE.constructor$ClientboundSetPassengersPacket(entityIds[0], entityIds[1]));
|
||||||
|
|
||||||
|
// fix some special occasions
|
||||||
|
if (originalY != processedY) {
|
||||||
|
double deltaY = originalY - processedY;
|
||||||
|
short ya = (short) (deltaY * 8192);
|
||||||
|
try {
|
||||||
|
packets.add(NetworkReflections.constructor$ClientboundMoveEntityPacket$Pos.newInstance(
|
||||||
|
entityIds[1], (short) 0, ya, (short) 0, true
|
||||||
|
));
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
CraftEngine.instance().logger().warn("Failed to construct ClientboundMoveEntityPacket$Pos", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (VersionHelper.isOrAbove1_20_5() && config.scale() != 1) {
|
||||||
|
Object attributeIns = FastNMS.INSTANCE.constructor$AttributeInstance(MAttributeHolders.SCALE, (Consumer<?>) (o) -> {});
|
||||||
|
FastNMS.INSTANCE.method$AttributeInstance$setBaseValue(attributeIns, config.scale());
|
||||||
|
packets.add(FastNMS.INSTANCE.constructor$ClientboundUpdateAttributesPacket(this.entityIds[1], Collections.singletonList(attributeIns)));
|
||||||
|
}
|
||||||
|
config.spawner().accept(entityIds, position.world(), x, y, z, yaw, offset, packets::add, colliders::add, parts::add);
|
||||||
|
this.parts = parts;
|
||||||
|
this.colliders = colliders;
|
||||||
|
this.spawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets);
|
||||||
|
this.despawnPacket = FastNMS.INSTANCE.constructor$ClientboundRemoveEntitiesPacket(new IntArrayList(entityIds));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Collider> colliders() {
|
||||||
|
return this.colliders;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<FurnitureHitboxPart> parts() {
|
||||||
|
return this.parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void collectVirtualEntityId(Consumer<Integer> collector) {
|
||||||
|
for (int entityId : entityIds) {
|
||||||
|
collector.accept(entityId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void show(Player player) {
|
||||||
|
player.sendPacket(this.spawnPacket, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void hide(Player player) {
|
||||||
|
player.sendPacket(this.despawnPacket, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ShulkerFurnitureHitboxConfig config() {
|
||||||
|
return this.config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] acquireEntityIds(Supplier<Integer> entityIdSupplier) {
|
||||||
|
if (config.interactionEntity()) {
|
||||||
|
if (config.direction().stepY() != 0) {
|
||||||
|
// 展示实体 // 潜影贝 // 交互实体
|
||||||
|
return new int[] {entityIdSupplier.get(), entityIdSupplier.get(), entityIdSupplier.get()};
|
||||||
|
} else {
|
||||||
|
// 展示实体 // 潜影贝 // 交互实体1 // 交互实体2
|
||||||
|
return new int[] {entityIdSupplier.get(), entityIdSupplier.get(), entityIdSupplier.get(), entityIdSupplier.get()};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 展示实体 // 潜影贝
|
||||||
|
return new int[] {entityIdSupplier.get(), entityIdSupplier.get()};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,13 +5,18 @@ import net.momirealms.craftengine.bukkit.entity.data.ShulkerData;
|
|||||||
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitCollider;
|
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitCollider;
|
||||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeHolders;
|
|
||||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
|
||||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections;
|
|
||||||
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
|
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
|
||||||
import net.momirealms.craftengine.core.entity.furniture.*;
|
import net.momirealms.craftengine.core.entity.furniture.Collider;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.Furniture;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.hitbox.AbstractFurnitureHitBoxConfig;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfigFactory;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitboxPart;
|
||||||
import net.momirealms.craftengine.core.entity.seat.SeatConfig;
|
import net.momirealms.craftengine.core.entity.seat.SeatConfig;
|
||||||
import net.momirealms.craftengine.core.util.*;
|
import net.momirealms.craftengine.core.util.Direction;
|
||||||
|
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||||
|
import net.momirealms.craftengine.core.util.QuaternionUtils;
|
||||||
|
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||||
import net.momirealms.craftengine.core.world.Vec3d;
|
import net.momirealms.craftengine.core.world.Vec3d;
|
||||||
import net.momirealms.craftengine.core.world.World;
|
import net.momirealms.craftengine.core.world.World;
|
||||||
import net.momirealms.craftengine.core.world.WorldPosition;
|
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||||
@@ -19,30 +24,39 @@ import net.momirealms.craftengine.core.world.collision.AABB;
|
|||||||
import org.joml.Quaternionf;
|
import org.joml.Quaternionf;
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class ShulkerHitBoxConfig extends AbstractHitBoxConfig {
|
public class ShulkerFurnitureHitboxConfig extends AbstractFurnitureHitBoxConfig<ShulkerFurnitureHitbox> {
|
||||||
public static final Factory FACTORY = new Factory();
|
public static final Factory FACTORY = new Factory();
|
||||||
// 1.20.6+
|
|
||||||
private final float scale;
|
private final float scale;
|
||||||
private final byte peek;
|
private final byte peek;
|
||||||
private final boolean interactive;
|
private final boolean interactive;
|
||||||
private final boolean interactionEntity;
|
private final boolean interactionEntity;
|
||||||
private final Direction direction;
|
private final Direction direction;
|
||||||
private final List<Object> cachedShulkerValues = new ArrayList<>();
|
|
||||||
private final DirectionalShulkerSpawner spawner;
|
private final DirectionalShulkerSpawner spawner;
|
||||||
|
private final List<Object> cachedShulkerValues = new ArrayList<>(6);
|
||||||
private final AABBCreator aabbCreator;
|
private final AABBCreator aabbCreator;
|
||||||
|
|
||||||
public ShulkerHitBoxConfig(SeatConfig[] seats, Vector3f position, Direction direction, float scale, byte peek, boolean interactionEntity, boolean interactive, boolean canUseOn, boolean blocksBuilding, boolean canBeHitByProjectile) {
|
public ShulkerFurnitureHitboxConfig(SeatConfig[] seats,
|
||||||
super(seats, position, canUseOn, blocksBuilding, canBeHitByProjectile);
|
Vector3f position,
|
||||||
this.direction = direction;
|
boolean canUseItemOn,
|
||||||
|
boolean blocksBuilding,
|
||||||
|
boolean canBeHitByProjectile,
|
||||||
|
float scale,
|
||||||
|
byte peek,
|
||||||
|
boolean interactive,
|
||||||
|
boolean interactionEntity,
|
||||||
|
Direction direction) {
|
||||||
|
super(seats, position, canUseItemOn, blocksBuilding, canBeHitByProjectile);
|
||||||
this.scale = scale;
|
this.scale = scale;
|
||||||
this.peek = peek;
|
this.peek = peek;
|
||||||
this.interactive = interactive;
|
this.interactive = interactive;
|
||||||
this.interactionEntity = interactionEntity;
|
this.interactionEntity = interactionEntity;
|
||||||
|
this.direction = direction;
|
||||||
|
|
||||||
ShulkerData.Peek.addEntityDataIfNotDefaultValue(peek, this.cachedShulkerValues);
|
ShulkerData.Peek.addEntityDataIfNotDefaultValue(peek, this.cachedShulkerValues);
|
||||||
ShulkerData.Color.addEntityDataIfNotDefaultValue((byte) 0, this.cachedShulkerValues);
|
ShulkerData.Color.addEntityDataIfNotDefaultValue((byte) 0, this.cachedShulkerValues);
|
||||||
@@ -51,9 +65,10 @@ public class ShulkerHitBoxConfig extends AbstractHitBoxConfig {
|
|||||||
ShulkerData.MobFlags.addEntityDataIfNotDefaultValue((byte) 0x01, this.cachedShulkerValues); // NO AI
|
ShulkerData.MobFlags.addEntityDataIfNotDefaultValue((byte) 0x01, this.cachedShulkerValues); // NO AI
|
||||||
ShulkerData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedShulkerValues); // Invisible
|
ShulkerData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, this.cachedShulkerValues); // Invisible
|
||||||
|
|
||||||
float shulkerHeight = (getPhysicalPeek(peek * 0.01F) + 1) * scale;
|
|
||||||
List<Object> cachedInteractionValues = new ArrayList<>();
|
List<Object> cachedInteractionValues = new ArrayList<>();
|
||||||
if (this.direction == Direction.UP) {
|
InteractionEntityData.SharedFlags.addEntityDataIfNotDefaultValue((byte) 0x20, cachedInteractionValues);
|
||||||
|
float shulkerHeight = (getPhysicalPeek(peek * 0.01F) + 1) * scale;
|
||||||
|
if (direction == Direction.UP) {
|
||||||
InteractionEntityData.Height.addEntityDataIfNotDefaultValue(shulkerHeight + 0.01f, cachedInteractionValues);
|
InteractionEntityData.Height.addEntityDataIfNotDefaultValue(shulkerHeight + 0.01f, cachedInteractionValues);
|
||||||
InteractionEntityData.Width.addEntityDataIfNotDefaultValue(scale + 0.005f, cachedInteractionValues);
|
InteractionEntityData.Width.addEntityDataIfNotDefaultValue(scale + 0.005f, cachedInteractionValues);
|
||||||
InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(interactive, cachedInteractionValues);
|
InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(interactive, cachedInteractionValues);
|
||||||
@@ -63,32 +78,28 @@ public class ShulkerHitBoxConfig extends AbstractHitBoxConfig {
|
|||||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||||
entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f, z - offset.z, 0, yaw,
|
entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f, z - offset.z, 0, yaw,
|
||||||
MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0
|
MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0
|
||||||
), true);
|
));
|
||||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true);
|
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)));
|
||||||
if (canUseOn) {
|
Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y, z - offset.z);
|
||||||
Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y, z - offset.z);
|
aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.makeBoundingBox(vec3d, scale, shulkerHeight), vec3d, interactive));
|
||||||
aabb.accept(new HitBoxPart(entityIds[2], AABB.fromInteraction(vec3d, scale, shulkerHeight), vec3d));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.aabbCreator = (x, y, z, yaw, offset) -> createAABB(Direction.UP, offset, x, y, z);
|
this.aabbCreator = (x, y, z, yaw, offset) -> createAABB(Direction.UP, offset, x, y, z);
|
||||||
} else if (this.direction == Direction.DOWN) {
|
} else if (direction == Direction.DOWN) {
|
||||||
InteractionEntityData.Height.addEntityDataIfNotDefaultValue(shulkerHeight + 0.01f, cachedInteractionValues);
|
InteractionEntityData.Height.addEntityDataIfNotDefaultValue(shulkerHeight + 0.01f, cachedInteractionValues);
|
||||||
InteractionEntityData.Width.addEntityDataIfNotDefaultValue(scale + 0.005f, cachedInteractionValues);
|
InteractionEntityData.Width.addEntityDataIfNotDefaultValue(scale + 0.005f, cachedInteractionValues);
|
||||||
InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(interactive, cachedInteractionValues);
|
InteractionEntityData.Responsive.addEntityDataIfNotDefaultValue(interactive, cachedInteractionValues);
|
||||||
this.spawner = (entityIds, world, x, y, z, yaw, offset, packets, collider, aabb) -> {
|
this.spawner = (entityIds, world, x, y, z, yaw, offset, packets, collider, aabb) -> {
|
||||||
collider.accept(this.createCollider(Direction.DOWN, world, offset, x, y, z, entityIds[1], aabb));
|
collider.accept(this.createCollider(Direction.DOWN, world, offset, x, y, z, entityIds[1], aabb));
|
||||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.of(ShulkerData.AttachFace.createEntityDataIfNotDefaultValue(CoreReflections.instance$Direction$UP))), false);
|
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.of(ShulkerData.AttachFace.createEntityDataIfNotDefaultValue(CoreReflections.instance$Direction$UP))));
|
||||||
if (interactionEntity) {
|
if (interactionEntity) {
|
||||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||||
entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f - shulkerHeight + scale, z - offset.z, 0, yaw,
|
entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f - shulkerHeight + scale, z - offset.z, 0, yaw,
|
||||||
MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0
|
MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0
|
||||||
), true);
|
));
|
||||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true);
|
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)));
|
||||||
if (canUseOn) {
|
Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y - shulkerHeight + scale, z - offset.z);
|
||||||
Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y - shulkerHeight + scale, z - offset.z);
|
aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.makeBoundingBox(vec3d, scale, shulkerHeight), vec3d, interactive));
|
||||||
aabb.accept(new HitBoxPart(entityIds[2], AABB.fromInteraction(vec3d, scale, shulkerHeight), vec3d));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.aabbCreator = (x, y, z, yaw, offset) -> createAABB(Direction.DOWN, offset, x, y, z);
|
this.aabbCreator = (x, y, z, yaw, offset) -> createAABB(Direction.DOWN, offset, x, y, z);
|
||||||
@@ -100,27 +111,25 @@ public class ShulkerHitBoxConfig extends AbstractHitBoxConfig {
|
|||||||
Direction shulkerAnchor = getOriginalDirection(direction, Direction.fromYaw(yaw));
|
Direction shulkerAnchor = getOriginalDirection(direction, Direction.fromYaw(yaw));
|
||||||
Direction shulkerDirection = shulkerAnchor.opposite();
|
Direction shulkerDirection = shulkerAnchor.opposite();
|
||||||
collider.accept(this.createCollider(shulkerDirection, world, offset, x, y, z, entityIds[1], aabb));
|
collider.accept(this.createCollider(shulkerDirection, world, offset, x, y, z, entityIds[1], aabb));
|
||||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.of(ShulkerData.AttachFace.createEntityDataIfNotDefaultValue(DirectionUtils.toNMSDirection(shulkerAnchor)))), false);
|
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.of(ShulkerData.AttachFace.createEntityDataIfNotDefaultValue(DirectionUtils.toNMSDirection(shulkerAnchor)))));
|
||||||
if (interactionEntity) {
|
if (interactionEntity) {
|
||||||
// first interaction
|
// first interaction
|
||||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||||
entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f, z - offset.z, 0, yaw,
|
entityIds[2], UUID.randomUUID(), x + offset.x, y + offset.y - 0.005f, z - offset.z, 0, yaw,
|
||||||
MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0
|
MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0
|
||||||
), true);
|
));
|
||||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true);
|
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)));
|
||||||
// second interaction
|
// second interaction
|
||||||
double distance = shulkerHeight - scale;
|
double distance = shulkerHeight - scale;
|
||||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||||
entityIds[3], UUID.randomUUID(), x + offset.x + shulkerDirection.stepX() * distance, y + offset.y - 0.005f, z - offset.z + shulkerDirection.stepZ() * distance, 0, yaw,
|
entityIds[3], UUID.randomUUID(), x + offset.x + shulkerDirection.stepX() * distance, y + offset.y - 0.005f, z - offset.z + shulkerDirection.stepZ() * distance, 0, yaw,
|
||||||
MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0
|
MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0
|
||||||
), true);
|
));
|
||||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[3], List.copyOf(cachedInteractionValues)), true);
|
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[3], List.copyOf(cachedInteractionValues)));
|
||||||
if (canUseOn) {
|
Vec3d vec3d1 = new Vec3d(x + offset.x, y + offset.y, z - offset.z);
|
||||||
Vec3d vec3d1 = new Vec3d(x + offset.x, y + offset.y, z - offset.z);
|
Vec3d vec3d2 = new Vec3d(x + offset.x + shulkerDirection.stepX() * distance, y + offset.y, z - offset.z + shulkerDirection.stepZ() * distance);
|
||||||
Vec3d vec3d2 = new Vec3d(x + offset.x + shulkerDirection.stepX() * distance, y + offset.y, z - offset.z + shulkerDirection.stepZ() * distance);
|
aabb.accept(new FurnitureHitboxPart(entityIds[2], AABB.makeBoundingBox(vec3d1, scale, scale), vec3d1, interactive));
|
||||||
aabb.accept(new HitBoxPart(entityIds[2], AABB.fromInteraction(vec3d1, scale, scale), vec3d1));
|
aabb.accept(new FurnitureHitboxPart(entityIds[3], AABB.makeBoundingBox(vec3d2, scale, scale), vec3d2, interactive));
|
||||||
aabb.accept(new HitBoxPart(entityIds[3], AABB.fromInteraction(vec3d2, scale, scale), vec3d2));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.aabbCreator = (x, y, z, yaw, offset) -> {
|
this.aabbCreator = (x, y, z, yaw, offset) -> {
|
||||||
@@ -131,16 +140,86 @@ public class ShulkerHitBoxConfig extends AbstractHitBoxConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collider createCollider(Direction direction, World world, Vector3f offset, double x, double y, double z, int entityId, Consumer<HitBoxPart> aabb) {
|
public static float getPhysicalPeek(float peek) {
|
||||||
|
return 0.5F - MiscUtils.sin((0.5F + peek) * 3.1415927F) * 0.5F;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void prepareBoundingBox(WorldPosition targetPos, Consumer<AABB> aabbConsumer, boolean ignoreBlocksBuilding) {
|
||||||
|
if (this.blocksBuilding || ignoreBlocksBuilding) {
|
||||||
|
Quaternionf conjugated = QuaternionUtils.toQuaternionf(0f, (float) Math.toRadians(180 - targetPos.yRot()), 0f).conjugate();
|
||||||
|
Vector3f offset = conjugated.transform(new Vector3f(position()));
|
||||||
|
aabbConsumer.accept(this.aabbCreator.create(targetPos.x, targetPos.y, targetPos.z, targetPos.yRot, offset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public float scale() {
|
||||||
|
return this.scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte peek() {
|
||||||
|
return this.peek;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean interactive() {
|
||||||
|
return this.interactive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean interactionEntity() {
|
||||||
|
return this.interactionEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Direction direction() {
|
||||||
|
return this.direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DirectionalShulkerSpawner spawner() {
|
||||||
|
return this.spawner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Object> cachedShulkerValues() {
|
||||||
|
return this.cachedShulkerValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ShulkerFurnitureHitbox create(Furniture furniture) {
|
||||||
|
return new ShulkerFurnitureHitbox(furniture, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface AABBCreator {
|
||||||
|
|
||||||
|
AABB create(double x, double y, double z, float yaw, Vector3f offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface DirectionalShulkerSpawner {
|
||||||
|
|
||||||
|
void accept(int[] entityIds,
|
||||||
|
World world,
|
||||||
|
double x,
|
||||||
|
double y,
|
||||||
|
double z,
|
||||||
|
float yaw,
|
||||||
|
Vector3f offset,
|
||||||
|
Consumer<Object> packets,
|
||||||
|
Consumer<Collider> collider,
|
||||||
|
Consumer<FurnitureHitboxPart> aabb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collider createCollider(Direction direction, World world,
|
||||||
|
Vector3f offset, double x, double y, double z,
|
||||||
|
int entityId,
|
||||||
|
Consumer<FurnitureHitboxPart> aabb) {
|
||||||
AABB ceAABB = createAABB(direction, offset, x, y, z);
|
AABB ceAABB = createAABB(direction, offset, x, y, z);
|
||||||
Object level = world.serverWorld();
|
Object level = world.serverWorld();
|
||||||
Object nmsAABB = FastNMS.INSTANCE.constructor$AABB(ceAABB.minX, ceAABB.minY, ceAABB.minZ, ceAABB.maxX, ceAABB.maxY, ceAABB.maxZ);
|
Object nmsAABB = FastNMS.INSTANCE.constructor$AABB(ceAABB.minX, ceAABB.minY, ceAABB.minZ, ceAABB.maxX, ceAABB.maxY, ceAABB.maxZ);
|
||||||
aabb.accept(new HitBoxPart(entityId, ceAABB, new Vec3d(x, y, z)));
|
aabb.accept(new FurnitureHitboxPart(entityId, ceAABB, new Vec3d(x, y, z), false));
|
||||||
return new BukkitCollider(level, nmsAABB, x, y, z, this.canBeHitByProjectile(), true, this.blocksBuilding());
|
return new BukkitCollider(level, nmsAABB, x, y, z, this.canBeHitByProjectile(), true, this.blocksBuilding());
|
||||||
}
|
}
|
||||||
|
|
||||||
public AABB createAABB(Direction direction, Vector3f offset, double x, double y, double z) {
|
public AABB createAABB(Direction direction, Vector3f relativePos, double x, double y, double z) {
|
||||||
float peek = getPhysicalPeek(this.peek() * 0.01F);
|
float peek = getPhysicalPeek(this.peek * 0.01F);
|
||||||
double x1 = -this.scale * 0.5;
|
double x1 = -this.scale * 0.5;
|
||||||
double y1 = 0.0;
|
double y1 = 0.0;
|
||||||
double z1 = -this.scale * 0.5;
|
double z1 = -this.scale * 0.5;
|
||||||
@@ -166,156 +245,15 @@ public class ShulkerHitBoxConfig extends AbstractHitBoxConfig {
|
|||||||
} else if (dz < 0) {
|
} else if (dz < 0) {
|
||||||
z1 += dz;
|
z1 += dz;
|
||||||
}
|
}
|
||||||
double minX = x + x1 + offset.x();
|
double minX = x + x1 + relativePos.x();
|
||||||
double maxX = x + x2 + offset.x();
|
double maxX = x + x2 + relativePos.x();
|
||||||
double minY = y + y1 + offset.y();
|
double minY = y + y1 + relativePos.y();
|
||||||
double maxY = y + y2 + offset.y();
|
double maxY = y + y2 + relativePos.y();
|
||||||
double minZ = z + z1 - offset.z();
|
double minZ = z + z1 - relativePos.z();
|
||||||
double maxZ = z + z2 - offset.z();
|
double maxZ = z + z2 - relativePos.z();
|
||||||
return new AABB(minX, minY, minZ, maxX, maxY, maxZ);
|
return new AABB(minX, minY, minZ, maxX, maxY, maxZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static float getPhysicalPeek(float peek) {
|
|
||||||
return 0.5F - MiscUtils.sin((0.5F + peek) * 3.1415927F) * 0.5F;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean interactionEntity() {
|
|
||||||
return interactionEntity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean interactive() {
|
|
||||||
return interactive;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Direction direction() {
|
|
||||||
return direction;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte peek() {
|
|
||||||
return peek;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float scale() {
|
|
||||||
return scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Key type() {
|
|
||||||
return HitBoxTypes.SHULKER;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initPacketsAndColliders(int[] entityIds,
|
|
||||||
WorldPosition position,
|
|
||||||
Quaternionf conjugated,
|
|
||||||
BiConsumer<Object, Boolean> packets,
|
|
||||||
Consumer<Collider> collider,
|
|
||||||
Consumer<HitBoxPart> aabb) {
|
|
||||||
Vector3f offset = conjugated.transform(new Vector3f(position()));
|
|
||||||
try {
|
|
||||||
double x = position.x();
|
|
||||||
double y = position.y();
|
|
||||||
double z = position.z();
|
|
||||||
float yaw = position.yRot();
|
|
||||||
double originalY = y + offset.y;
|
|
||||||
double integerPart = Math.floor(originalY);
|
|
||||||
double fractionalPart = originalY - integerPart;
|
|
||||||
double processedY = (fractionalPart >= 0.5) ? integerPart + 1 : originalY;
|
|
||||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
|
||||||
entityIds[0], UUID.randomUUID(), x + offset.x, originalY, z - offset.z, 0, yaw,
|
|
||||||
MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0
|
|
||||||
), false);
|
|
||||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
|
||||||
entityIds[1], UUID.randomUUID(), x + offset.x, processedY, z - offset.z, 0, yaw,
|
|
||||||
MEntityTypes.SHULKER, 0, CoreReflections.instance$Vec3$Zero, 0
|
|
||||||
), false);
|
|
||||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[1], List.copyOf(this.cachedShulkerValues)), false);
|
|
||||||
// add passengers
|
|
||||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetPassengersPacket(entityIds[0], entityIds[1]), false);
|
|
||||||
// fix some special occasions
|
|
||||||
if (originalY != processedY) {
|
|
||||||
double deltaY = originalY - processedY;
|
|
||||||
short ya = (short) (deltaY * 8192);
|
|
||||||
packets.accept(NetworkReflections.constructor$ClientboundMoveEntityPacket$Pos.newInstance(
|
|
||||||
entityIds[1], (short) 0, ya, (short) 0, true
|
|
||||||
), false);
|
|
||||||
}
|
|
||||||
// set shulker scale
|
|
||||||
if (VersionHelper.isOrAbove1_20_5() && this.scale != 1) {
|
|
||||||
Object attributeInstance = CoreReflections.constructor$AttributeInstance.newInstance(MAttributeHolders.SCALE, (Consumer<?>) (o) -> {});
|
|
||||||
CoreReflections.method$AttributeInstance$setBaseValue.invoke(attributeInstance, this.scale);
|
|
||||||
packets.accept(NetworkReflections.constructor$ClientboundUpdateAttributesPacket0.newInstance(entityIds[1], Collections.singletonList(attributeInstance)), false);
|
|
||||||
}
|
|
||||||
this.spawner.accept(entityIds, position.world(), x, y, z, yaw, offset, packets, collider, aabb);
|
|
||||||
} catch (ReflectiveOperationException e) {
|
|
||||||
throw new RuntimeException("Failed to construct shulker hitbox spawn packet", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initShapeForPlacement(double x, double y, double z, float yaw, Quaternionf conjugated, Consumer<AABB> aabbs) {
|
|
||||||
Vector3f offset = conjugated.transform(new Vector3f(position()));
|
|
||||||
aabbs.accept(this.aabbCreator.create(x, y, z, yaw, offset));
|
|
||||||
}
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
interface DirectionalShulkerSpawner {
|
|
||||||
|
|
||||||
void accept(int[] entityIds,
|
|
||||||
World world,
|
|
||||||
double x,
|
|
||||||
double y,
|
|
||||||
double z,
|
|
||||||
float yaw,
|
|
||||||
Vector3f offset,
|
|
||||||
BiConsumer<Object, Boolean> packets,
|
|
||||||
Consumer<Collider> collider,
|
|
||||||
Consumer<HitBoxPart> aabb);
|
|
||||||
}
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
interface AABBCreator {
|
|
||||||
|
|
||||||
AABB create(double x, double y, double z, float yaw, Vector3f offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int[] acquireEntityIds(Supplier<Integer> entityIdSupplier) {
|
|
||||||
if (this.interactionEntity) {
|
|
||||||
if (this.direction.stepY() != 0) {
|
|
||||||
// 展示实体 // 潜影贝 // 交互实体
|
|
||||||
return new int[] {entityIdSupplier.get(), entityIdSupplier.get(), entityIdSupplier.get()};
|
|
||||||
} else {
|
|
||||||
// 展示实体 // 潜影贝 // 交互实体1 // 交互实体2
|
|
||||||
return new int[] {entityIdSupplier.get(), entityIdSupplier.get(), entityIdSupplier.get(), entityIdSupplier.get()};
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 展示实体 // 潜影贝
|
|
||||||
return new int[] {entityIdSupplier.get(), entityIdSupplier.get()};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Factory implements HitBoxConfigFactory {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HitBoxConfig create(Map<String, Object> arguments) {
|
|
||||||
Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position");
|
|
||||||
float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", "1"), "scale");
|
|
||||||
byte peek = (byte) ResourceConfigUtils.getAsInt(arguments.getOrDefault("peek", 0), "peek");
|
|
||||||
Direction directionEnum = Optional.ofNullable(arguments.get("direction")).map(it -> Direction.valueOf(it.toString().toUpperCase(Locale.ENGLISH))).orElse(Direction.UP);
|
|
||||||
boolean interactive = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("interactive", true), "interactive");
|
|
||||||
boolean interactionEntity = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("interaction-entity", true), "interaction-entity");
|
|
||||||
boolean canUseItemOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", true), "can-use-item-on");
|
|
||||||
boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", true), "can-be-hit-by-projectile");
|
|
||||||
boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building");
|
|
||||||
return new ShulkerHitBoxConfig(
|
|
||||||
SeatConfig.fromObj(arguments.get("seats")),
|
|
||||||
position, directionEnum,
|
|
||||||
scale, peek, interactionEntity, interactive, canUseItemOn, blocksBuilding, canBeHitByProjectile
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Direction getOriginalDirection(Direction newDirection, Direction oldDirection) {
|
public static Direction getOriginalDirection(Direction newDirection, Direction oldDirection) {
|
||||||
switch (newDirection) {
|
switch (newDirection) {
|
||||||
case NORTH -> {
|
case NORTH -> {
|
||||||
@@ -357,4 +295,26 @@ public class ShulkerHitBoxConfig extends AbstractHitBoxConfig {
|
|||||||
default -> throw new IllegalStateException("Unexpected value: " + newDirection);
|
default -> throw new IllegalStateException("Unexpected value: " + newDirection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class Factory implements FurnitureHitBoxConfigFactory<ShulkerFurnitureHitbox> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ShulkerFurnitureHitboxConfig create(Map<String, Object> arguments) {
|
||||||
|
Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", 0), "position");
|
||||||
|
float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", 1), "scale");
|
||||||
|
byte peek = (byte) ResourceConfigUtils.getAsInt(arguments.getOrDefault("peek", 0), "peek");
|
||||||
|
Direction directionEnum = ResourceConfigUtils.getAsEnum(arguments.get("direction"), Direction.class, Direction.UP);
|
||||||
|
boolean interactive = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("interactive", true), "interactive");
|
||||||
|
boolean interactionEntity = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("interaction-entity", true), "interaction-entity");
|
||||||
|
boolean canUseItemOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", true), "can-use-item-on");
|
||||||
|
boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", true), "can-be-hit-by-projectile");
|
||||||
|
boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building");
|
||||||
|
return new ShulkerFurnitureHitboxConfig(
|
||||||
|
SeatConfig.fromObj(arguments.get("seats")),
|
||||||
|
position,
|
||||||
|
canUseItemOn, blocksBuilding, canBeHitByProjectile,
|
||||||
|
scale, peek, interactive, interactionEntity, directionEnum
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -61,6 +61,7 @@ public class BukkitSeatManager implements SeatManager, Listener {
|
|||||||
@Override
|
@Override
|
||||||
public void delayedInit() {
|
public void delayedInit() {
|
||||||
Bukkit.getPluginManager().registerEvents(this.dismountListener, this.plugin.javaPlugin());
|
Bukkit.getPluginManager().registerEvents(this.dismountListener, this.plugin.javaPlugin());
|
||||||
|
Bukkit.getPluginManager().registerEvents(this, this.plugin.javaPlugin());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ public class AxeItemBehavior extends ItemBehavior {
|
|||||||
|
|
||||||
// resend swing if it's not interactable on client side
|
// resend swing if it's not interactable on client side
|
||||||
if (!InteractUtils.isInteractable(
|
if (!InteractUtils.isInteractable(
|
||||||
bukkitPlayer, BlockStateUtils.fromBlockData(customState.vanillaBlockState().literalObject()),
|
bukkitPlayer, BlockStateUtils.fromBlockData(customState.visualBlockState().literalObject()),
|
||||||
context.getHitResult(), item
|
context.getHitResult(), item
|
||||||
) || player.isSecondaryUseActive()) {
|
) || player.isSecondaryUseActive()) {
|
||||||
player.swingHand(context.getHand());
|
player.swingHand(context.getHand());
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ public class BlockItemBehavior extends BlockBoundItemBehavior {
|
|||||||
} else {
|
} else {
|
||||||
ImmutableBlockState customState = optionalCustomState.get();
|
ImmutableBlockState customState = optionalCustomState.get();
|
||||||
// custom block
|
// custom block
|
||||||
if (!AdventureModeUtils.canPlace(context.getItem(), context.getLevel(), againstPos, Config.simplifyAdventurePlaceCheck() ? customState.vanillaBlockState().literalObject() : againstBlockState)) {
|
if (!AdventureModeUtils.canPlace(context.getItem(), context.getLevel(), againstPos, Config.simplifyAdventurePlaceCheck() ? customState.visualBlockState().literalObject() : againstBlockState)) {
|
||||||
return InteractionResult.FAIL;
|
return InteractionResult.FAIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ public class BukkitItemBehaviors extends ItemBehaviors {
|
|||||||
public static final Key BLOCK_ITEM = Key.from("craftengine:block_item");
|
public static final Key BLOCK_ITEM = Key.from("craftengine:block_item");
|
||||||
public static final Key ON_LIQUID_BLOCK_ITEM = Key.from("craftengine:liquid_collision_block_item");
|
public static final Key ON_LIQUID_BLOCK_ITEM = Key.from("craftengine:liquid_collision_block_item");
|
||||||
public static final Key FURNITURE_ITEM = Key.from("craftengine:furniture_item");
|
public static final Key FURNITURE_ITEM = Key.from("craftengine:furniture_item");
|
||||||
|
public static final Key ON_LIQUID_FURNITURE_ITEM = Key.from("craftengine:liquid_collision_furniture_item");
|
||||||
public static final Key FLINT_AND_STEEL_ITEM = Key.from("craftengine:flint_and_steel_item");
|
public static final Key FLINT_AND_STEEL_ITEM = Key.from("craftengine:flint_and_steel_item");
|
||||||
public static final Key COMPOSTABLE_ITEM = Key.from("craftengine:compostable_item");
|
public static final Key COMPOSTABLE_ITEM = Key.from("craftengine:compostable_item");
|
||||||
public static final Key AXE_ITEM = Key.from("craftengine:axe_item");
|
public static final Key AXE_ITEM = Key.from("craftengine:axe_item");
|
||||||
@@ -21,6 +22,7 @@ public class BukkitItemBehaviors extends ItemBehaviors {
|
|||||||
register(BLOCK_ITEM, BlockItemBehavior.FACTORY);
|
register(BLOCK_ITEM, BlockItemBehavior.FACTORY);
|
||||||
register(ON_LIQUID_BLOCK_ITEM, LiquidCollisionBlockItemBehavior.FACTORY);
|
register(ON_LIQUID_BLOCK_ITEM, LiquidCollisionBlockItemBehavior.FACTORY);
|
||||||
register(FURNITURE_ITEM, FurnitureItemBehavior.FACTORY);
|
register(FURNITURE_ITEM, FurnitureItemBehavior.FACTORY);
|
||||||
|
register(ON_LIQUID_FURNITURE_ITEM, LiquidCollisionFurnitureItemBehavior.FACTORY);
|
||||||
register(FLINT_AND_STEEL_ITEM, FlintAndSteelItemBehavior.FACTORY);
|
register(FLINT_AND_STEEL_ITEM, FlintAndSteelItemBehavior.FACTORY);
|
||||||
register(COMPOSTABLE_ITEM, CompostableItemBehavior.FACTORY);
|
register(COMPOSTABLE_ITEM, CompostableItemBehavior.FACTORY);
|
||||||
register(AXE_ITEM, AxeItemBehavior.FACTORY);
|
register(AXE_ITEM, AxeItemBehavior.FACTORY);
|
||||||
|
|||||||
@@ -77,10 +77,10 @@ public class FlintAndSteelItemBehavior extends ItemBehavior {
|
|||||||
// 点击对象为自定义方块
|
// 点击对象为自定义方块
|
||||||
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId);
|
ImmutableBlockState immutableBlockState = BukkitBlockManager.instance().getImmutableBlockStateUnsafe(stateId);
|
||||||
// 原版外观也可燃
|
// 原版外观也可燃
|
||||||
if (BlockStateUtils.isBurnable(immutableBlockState.vanillaBlockState().literalObject())) {
|
if (BlockStateUtils.isBurnable(immutableBlockState.visualBlockState().literalObject())) {
|
||||||
return InteractionResult.PASS;
|
return InteractionResult.PASS;
|
||||||
}
|
}
|
||||||
BlockData vanillaBlockState = BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().literalObject());
|
BlockData vanillaBlockState = BlockStateUtils.fromBlockData(immutableBlockState.visualBlockState().literalObject());
|
||||||
// 点击的是方块上面,则只需要判断shift和可交互
|
// 点击的是方块上面,则只需要判断shift和可交互
|
||||||
if (direction == Direction.UP) {
|
if (direction == Direction.UP) {
|
||||||
// 客户端层面必须可交互
|
// 客户端层面必须可交互
|
||||||
|
|||||||
@@ -6,13 +6,10 @@ import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture;
|
|||||||
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager;
|
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager;
|
||||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||||
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
|
|
||||||
import net.momirealms.craftengine.bukkit.util.EventUtils;
|
import net.momirealms.craftengine.bukkit.util.EventUtils;
|
||||||
import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
||||||
import net.momirealms.craftengine.core.entity.furniture.AnchorType;
|
import net.momirealms.craftengine.core.entity.furniture.*;
|
||||||
import net.momirealms.craftengine.core.entity.furniture.CustomFurniture;
|
import net.momirealms.craftengine.core.entity.furniture.hitbox.FurnitureHitBoxConfig;
|
||||||
import net.momirealms.craftengine.core.entity.furniture.FurnitureExtraData;
|
|
||||||
import net.momirealms.craftengine.core.entity.furniture.HitBoxConfig;
|
|
||||||
import net.momirealms.craftengine.core.entity.player.InteractionResult;
|
import net.momirealms.craftengine.core.entity.player.InteractionResult;
|
||||||
import net.momirealms.craftengine.core.entity.player.Player;
|
import net.momirealms.craftengine.core.entity.player.Player;
|
||||||
import net.momirealms.craftengine.core.item.Item;
|
import net.momirealms.craftengine.core.item.Item;
|
||||||
@@ -27,28 +24,48 @@ import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
|
|||||||
import net.momirealms.craftengine.core.plugin.context.event.EventTrigger;
|
import net.momirealms.craftengine.core.plugin.context.event.EventTrigger;
|
||||||
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
|
import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextParameters;
|
||||||
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
|
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
|
||||||
|
import net.momirealms.craftengine.core.plugin.logger.Debugger;
|
||||||
import net.momirealms.craftengine.core.util.*;
|
import net.momirealms.craftengine.core.util.*;
|
||||||
import net.momirealms.craftengine.core.world.Vec3d;
|
import net.momirealms.craftengine.core.world.Vec3d;
|
||||||
|
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||||
import net.momirealms.craftengine.core.world.collision.AABB;
|
import net.momirealms.craftengine.core.world.collision.AABB;
|
||||||
|
import net.momirealms.sparrow.nbt.CompoundTag;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
import java.util.function.Predicate;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
public class FurnitureItemBehavior extends ItemBehavior {
|
public class FurnitureItemBehavior extends ItemBehavior {
|
||||||
public static final Factory FACTORY = new Factory();
|
public static final Factory FACTORY = new Factory();
|
||||||
|
protected static final Set<String> ALLOWED_ANCHOR_TYPES = Set.of("wall", "ceiling", "ground");
|
||||||
private final Key id;
|
private final Key id;
|
||||||
|
private final Map<AnchorType, Rule> rules;
|
||||||
|
private final boolean ignorePlacer;
|
||||||
|
private final boolean ignoreEntities;
|
||||||
|
|
||||||
public FurnitureItemBehavior(Key id) {
|
public FurnitureItemBehavior(Key id, Map<AnchorType, Rule> rules, boolean ignorePlacer, boolean ignoreEntities) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
this.rules = rules;
|
||||||
|
this.ignorePlacer = ignorePlacer;
|
||||||
|
this.ignoreEntities = ignoreEntities;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Key furnitureId() {
|
public Key furnitureId() {
|
||||||
return id;
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<AnchorType, Rule> rules() {
|
||||||
|
return this.rules;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean ignorePlacer() {
|
||||||
|
return this.ignorePlacer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean ignoreEntities() {
|
||||||
|
return this.ignoreEntities;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -62,7 +79,6 @@ public class FurnitureItemBehavior extends ItemBehavior {
|
|||||||
CraftEngine.instance().logger().warn("Furniture " + this.id + " not found");
|
CraftEngine.instance().logger().warn("Furniture " + this.id + " not found");
|
||||||
return InteractionResult.FAIL;
|
return InteractionResult.FAIL;
|
||||||
}
|
}
|
||||||
CustomFurniture customFurniture = optionalCustomFurniture.get();
|
|
||||||
|
|
||||||
Direction clickedFace = context.getClickedFace();
|
Direction clickedFace = context.getClickedFace();
|
||||||
AnchorType anchorType = switch (clickedFace) {
|
AnchorType anchorType = switch (clickedFace) {
|
||||||
@@ -71,78 +87,98 @@ public class FurnitureItemBehavior extends ItemBehavior {
|
|||||||
case DOWN -> AnchorType.CEILING;
|
case DOWN -> AnchorType.CEILING;
|
||||||
};
|
};
|
||||||
|
|
||||||
CustomFurniture.Placement placement = customFurniture.getPlacement(anchorType);
|
CustomFurniture customFurniture = optionalCustomFurniture.get();
|
||||||
if (placement == null) {
|
FurnitureVariant variant = customFurniture.getVariant(anchorType.variantName());
|
||||||
|
if (variant == null) {
|
||||||
return InteractionResult.FAIL;
|
return InteractionResult.FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rule rule = this.rules.get(anchorType);
|
||||||
|
if (rule == null) {
|
||||||
|
rule = Rule.DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
Player player = context.getPlayer();
|
Player player = context.getPlayer();
|
||||||
// todo adventure check
|
|
||||||
if (player != null && player.isAdventureMode()) {
|
if (player != null && player.isAdventureMode()) {
|
||||||
return InteractionResult.FAIL;
|
return InteractionResult.FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3d clickedPosition = context.getClickLocation();
|
Vec3d clickedPosition = context.getClickLocation();
|
||||||
|
|
||||||
// trigger event
|
|
||||||
org.bukkit.entity.Player bukkitPlayer = player != null ? (org.bukkit.entity.Player) player.platformPlayer() : null;
|
|
||||||
World world = (World) context.getLevel().platformWorld();
|
|
||||||
|
|
||||||
// get position and rotation for placement
|
// get position and rotation for placement
|
||||||
Vec3d finalPlacePosition;
|
Vec3d finalPlacePosition;
|
||||||
double furnitureYaw;
|
double furnitureYaw;
|
||||||
if (anchorType == AnchorType.WALL) {
|
if (anchorType == AnchorType.WALL) {
|
||||||
furnitureYaw = Direction.getYaw(clickedFace);
|
furnitureYaw = Direction.getYaw(clickedFace);
|
||||||
if (clickedFace == Direction.EAST || clickedFace == Direction.WEST) {
|
if (clickedFace == Direction.EAST || clickedFace == Direction.WEST) {
|
||||||
Pair<Double, Double> xz = placement.alignmentRule().apply(Pair.of(clickedPosition.y(), clickedPosition.z()));
|
Pair<Double, Double> xz = rule.alignmentRule().apply(Pair.of(clickedPosition.y(), clickedPosition.z()));
|
||||||
finalPlacePosition = new Vec3d(clickedPosition.x(), xz.left(), xz.right());
|
finalPlacePosition = new Vec3d(clickedPosition.x(), xz.left(), xz.right());
|
||||||
} else {
|
} else {
|
||||||
Pair<Double, Double> xz = placement.alignmentRule().apply(Pair.of(clickedPosition.x(), clickedPosition.y()));
|
Pair<Double, Double> xz = rule.alignmentRule().apply(Pair.of(clickedPosition.x(), clickedPosition.y()));
|
||||||
finalPlacePosition = new Vec3d(xz.left(), xz.right(), clickedPosition.z());
|
finalPlacePosition = new Vec3d(xz.left(), xz.right(), clickedPosition.z());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
furnitureYaw = placement.rotationRule().apply(180 + (player != null ? player.yRot() : 0));
|
furnitureYaw = rule.rotationRule().apply(180 + (player != null ? player.yRot() : 0));
|
||||||
Pair<Double, Double> xz = placement.alignmentRule().apply(Pair.of(clickedPosition.x(), clickedPosition.z()));
|
Pair<Double, Double> xz = rule.alignmentRule().apply(Pair.of(clickedPosition.x(), clickedPosition.z()));
|
||||||
finalPlacePosition = new Vec3d(xz.left(), clickedPosition.y(), xz.right());
|
finalPlacePosition = new Vec3d(xz.left(), clickedPosition.y(), xz.right());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// trigger event
|
||||||
|
org.bukkit.entity.Player bukkitPlayer = player != null ? (org.bukkit.entity.Player) player.platformPlayer() : null;
|
||||||
|
World world = (World) context.getLevel().platformWorld();
|
||||||
Location furnitureLocation = new Location(world, finalPlacePosition.x(), finalPlacePosition.y(), finalPlacePosition.z(), (float) furnitureYaw, 0);
|
Location furnitureLocation = new Location(world, finalPlacePosition.x(), finalPlacePosition.y(), finalPlacePosition.z(), (float) furnitureYaw, 0);
|
||||||
|
WorldPosition furniturePos = LocationUtils.toWorldPosition(furnitureLocation);
|
||||||
List<AABB> aabbs = new ArrayList<>();
|
List<AABB> aabbs = new ArrayList<>();
|
||||||
for (HitBoxConfig hitBoxConfig : placement.hitBoxConfigs()) {
|
// 收集阻挡的碰撞箱
|
||||||
hitBoxConfig.initShapeForPlacement(finalPlacePosition.x(), finalPlacePosition.y(), finalPlacePosition.z(), (float) furnitureYaw, QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - furnitureYaw), 0).conjugate(), aabbs::add);
|
for (FurnitureHitBoxConfig<?> hitBoxConfig : variant.hitBoxConfigs()) {
|
||||||
|
hitBoxConfig.prepareBoundingBox(furniturePos, aabbs::add, false);
|
||||||
}
|
}
|
||||||
|
// 检查方块、实体阻挡
|
||||||
if (!aabbs.isEmpty()) {
|
if (!aabbs.isEmpty()) {
|
||||||
if (!FastNMS.INSTANCE.checkEntityCollision(context.getLevel().serverWorld(), aabbs.stream().map(it -> FastNMS.INSTANCE.constructor$AABB(it.minX, it.minY, it.minZ, it.maxX, it.maxY, it.maxZ)).toList())) {
|
Predicate<Object> entityPredicate;
|
||||||
|
if (this.ignoreEntities) {
|
||||||
|
entityPredicate = (o) -> false;
|
||||||
|
} else if (this.ignorePlacer) {
|
||||||
|
entityPredicate = player != null ? (o) -> o != player.serverPlayer() : (o) -> true;
|
||||||
|
} else {
|
||||||
|
entityPredicate = (o) -> true;
|
||||||
|
}
|
||||||
|
if (!FastNMS.INSTANCE.checkEntityCollision(context.getLevel().serverWorld(), aabbs.stream().map(it -> FastNMS.INSTANCE.constructor$AABB(it.minX, it.minY, it.minZ, it.maxX, it.maxY, it.maxZ)).toList(), entityPredicate)) {
|
||||||
|
if (player != null && player.enableFurnitureDebug() && VersionHelper.isPaper()) {
|
||||||
|
player.playSound(Key.of("minecraft:entity.villager.no"));
|
||||||
|
Key flame = Key.of("flame");
|
||||||
|
for (AABB aabb : aabbs) {
|
||||||
|
List<Vec3d> edgePoints = aabb.getEdgePoints(0.125);
|
||||||
|
for (Vec3d edgePoint : edgePoints) {
|
||||||
|
player.playParticle(flame, edgePoint.x(), edgePoint.y(), edgePoint.z());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return InteractionResult.FAIL;
|
return InteractionResult.FAIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 检查其他插件兼容性
|
||||||
if (!BukkitCraftEngine.instance().antiGriefProvider().canPlace(bukkitPlayer, furnitureLocation)) {
|
if (!BukkitCraftEngine.instance().antiGriefProvider().canPlace(bukkitPlayer, furnitureLocation)) {
|
||||||
return InteractionResult.FAIL;
|
return InteractionResult.FAIL;
|
||||||
}
|
}
|
||||||
|
// 触发尝试放置的事件
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
FurnitureAttemptPlaceEvent attemptPlaceEvent = new FurnitureAttemptPlaceEvent(bukkitPlayer, customFurniture, anchorType, furnitureLocation.clone(),
|
FurnitureAttemptPlaceEvent attemptPlaceEvent = new FurnitureAttemptPlaceEvent(bukkitPlayer, customFurniture, variant, furnitureLocation.clone(), context.getHand(), world.getBlockAt(context.getClickedPos().x(), context.getClickedPos().y(), context.getClickedPos().z()));
|
||||||
DirectionUtils.toBlockFace(clickedFace), context.getHand(), world.getBlockAt(context.getClickedPos().x(), context.getClickedPos().y(), context.getClickedPos().z()));
|
|
||||||
if (EventUtils.fireAndCheckCancel(attemptPlaceEvent)) {
|
if (EventUtils.fireAndCheckCancel(attemptPlaceEvent)) {
|
||||||
return InteractionResult.FAIL;
|
return InteractionResult.FAIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item<?> item = context.getItem();
|
Item<?> item = context.getItem();
|
||||||
// 不可能
|
|
||||||
if (ItemUtils.isEmpty(item)) return InteractionResult.FAIL;
|
if (ItemUtils.isEmpty(item)) return InteractionResult.FAIL;
|
||||||
|
// 获取家具物品的一些属性
|
||||||
BukkitFurniture bukkitFurniture = BukkitFurnitureManager.instance().place(
|
FurnitureDataAccessor dataAccessor = FurnitureDataAccessor.of(new CompoundTag());
|
||||||
furnitureLocation.clone(), customFurniture,
|
dataAccessor.setVariant(variant.name());
|
||||||
FurnitureExtraData.builder()
|
dataAccessor.setItem(item.copyWithCount(1));
|
||||||
.item(item.copyWithCount(1))
|
dataAccessor.setDyedColor(item.dyedColor().orElse(null));
|
||||||
.anchorType(anchorType)
|
dataAccessor.setFireworkExplosionColors(item.fireworkExplosion().map(explosion -> explosion.colors().toIntArray()).orElse(null));
|
||||||
.dyedColor(item.dyedColor().orElse(null))
|
// 放置家具
|
||||||
.fireworkExplosionColors(item.fireworkExplosion().map(explosion -> explosion.colors().toIntArray()).orElse(null))
|
BukkitFurniture bukkitFurniture = BukkitFurnitureManager.instance().place(furnitureLocation.clone(), customFurniture, dataAccessor, false);
|
||||||
.build(), false);
|
// 触发放置事件
|
||||||
|
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
FurniturePlaceEvent placeEvent = new FurniturePlaceEvent(bukkitPlayer, bukkitFurniture, furnitureLocation, context.getHand());
|
FurniturePlaceEvent placeEvent = new FurniturePlaceEvent(bukkitPlayer, bukkitFurniture, furnitureLocation, context.getHand());
|
||||||
if (EventUtils.fireAndCheckCancel(placeEvent)) {
|
if (EventUtils.fireAndCheckCancel(placeEvent)) {
|
||||||
@@ -150,7 +186,7 @@ public class FurnitureItemBehavior extends ItemBehavior {
|
|||||||
return InteractionResult.FAIL;
|
return InteractionResult.FAIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 触发ce事件
|
||||||
Cancellable dummy = Cancellable.dummy();
|
Cancellable dummy = Cancellable.dummy();
|
||||||
PlayerOptionalContext functionContext = PlayerOptionalContext.of(player, ContextHolder.builder()
|
PlayerOptionalContext functionContext = PlayerOptionalContext.of(player, ContextHolder.builder()
|
||||||
.withParameter(DirectContextParameters.FURNITURE, bukkitFurniture)
|
.withParameter(DirectContextParameters.FURNITURE, bukkitFurniture)
|
||||||
@@ -163,14 +199,13 @@ public class FurnitureItemBehavior extends ItemBehavior {
|
|||||||
if (dummy.isCancelled()) {
|
if (dummy.isCancelled()) {
|
||||||
return InteractionResult.SUCCESS_AND_CANCEL;
|
return InteractionResult.SUCCESS_AND_CANCEL;
|
||||||
}
|
}
|
||||||
|
// 后续处理
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
if (!player.canInstabuild()) {
|
if (!player.canInstabuild()) {
|
||||||
item.count(item.count() - 1);
|
item.count(item.count() - 1);
|
||||||
}
|
}
|
||||||
player.swingHand(context.getHand());
|
player.swingHand(context.getHand());
|
||||||
}
|
}
|
||||||
|
|
||||||
context.getLevel().playBlockSound(finalPlacePosition, customFurniture.settings().sounds().placeSound());
|
context.getLevel().playBlockSound(finalPlacePosition, customFurniture.settings().sounds().placeSound());
|
||||||
return InteractionResult.SUCCESS;
|
return InteractionResult.SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -183,17 +218,62 @@ public class FurnitureItemBehavior extends ItemBehavior {
|
|||||||
if (id == null) {
|
if (id == null) {
|
||||||
throw new LocalizedResourceConfigException("warning.config.item.behavior.furniture.missing_furniture", new IllegalArgumentException("Missing required parameter 'furniture' for furniture_item behavior"));
|
throw new LocalizedResourceConfigException("warning.config.item.behavior.furniture.missing_furniture", new IllegalArgumentException("Missing required parameter 'furniture' for furniture_item behavior"));
|
||||||
}
|
}
|
||||||
|
Map<String, Object> rulesMap = ResourceConfigUtils.getAsMapOrNull(arguments.get("rules"), "rules");
|
||||||
|
Key furnitureId;
|
||||||
if (id instanceof Map<?,?> map) {
|
if (id instanceof Map<?,?> map) {
|
||||||
|
Map<String, Object> furnitureSection;
|
||||||
if (map.containsKey(key.toString())) {
|
if (map.containsKey(key.toString())) {
|
||||||
// 防呆
|
// 防呆
|
||||||
BukkitFurnitureManager.instance().parser().addPendingConfigSection(new PendingConfigSection(pack, path, node, key, MiscUtils.castToMap(map.get(key.toString()), false)));
|
furnitureSection = MiscUtils.castToMap(map.get(key.toString()), false);
|
||||||
|
BukkitFurnitureManager.instance().parser().addPendingConfigSection(new PendingConfigSection(pack, path, node, key, furnitureSection));
|
||||||
} else {
|
} else {
|
||||||
BukkitFurnitureManager.instance().parser().addPendingConfigSection(new PendingConfigSection(pack, path, node, key, MiscUtils.castToMap(map, false)));
|
furnitureSection = MiscUtils.castToMap(map, false);
|
||||||
|
BukkitFurnitureManager.instance().parser().addPendingConfigSection(new PendingConfigSection(pack, path, node, key, furnitureSection));
|
||||||
|
}
|
||||||
|
furnitureId = key;
|
||||||
|
// 兼容老版本
|
||||||
|
if (rulesMap == null) {
|
||||||
|
Map<String, Object> placementSection = ResourceConfigUtils.getAsMapOrNull(furnitureSection.get("placement"), "placement");
|
||||||
|
if (placementSection != null) {
|
||||||
|
rulesMap = new HashMap<>();
|
||||||
|
for (Map.Entry<String, Object> entry : placementSection.entrySet()) {
|
||||||
|
if (entry.getValue() instanceof Map<?, ?> innerMap) {
|
||||||
|
if (innerMap.containsKey("rules")) {
|
||||||
|
Map<String, Object> rules = ResourceConfigUtils.getAsMap(innerMap.get("rules"), "rules");
|
||||||
|
if (ALLOWED_ANCHOR_TYPES.contains(entry.getKey())) {
|
||||||
|
rulesMap.put(entry.getKey(), rules);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return new FurnitureItemBehavior(key);
|
|
||||||
} else {
|
} else {
|
||||||
return new FurnitureItemBehavior(Key.of(id.toString()));
|
furnitureId = Key.of(id.toString());
|
||||||
}
|
}
|
||||||
|
Map<AnchorType, Rule> rules = new EnumMap<>(AnchorType.class);
|
||||||
|
if (rulesMap != null) {
|
||||||
|
for (Map.Entry<String, Object> entry : rulesMap.entrySet()) {
|
||||||
|
try {
|
||||||
|
AnchorType type = AnchorType.valueOf(entry.getKey().toUpperCase(Locale.ROOT));
|
||||||
|
Map<String, Object> ruleSection = MiscUtils.castToMap(entry.getValue(), true);
|
||||||
|
rules.put(type, new Rule(
|
||||||
|
ResourceConfigUtils.getAsEnum(ruleSection.get("alignment"), AlignmentRule.class, AlignmentRule.ANY),
|
||||||
|
ResourceConfigUtils.getAsEnum(ruleSection.get("rotation"), RotationRule.class, RotationRule.ANY)
|
||||||
|
));
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
Debugger.FURNITURE.debug(() -> "Invalid anchor type: " + entry.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new FurnitureItemBehavior(furnitureId, rules,
|
||||||
|
ResourceConfigUtils.getAsBoolean(arguments.get("ignore-placer"), "ignore-placer"),
|
||||||
|
ResourceConfigUtils.getAsBoolean(arguments.get("ignore-entities"), "ignore-entities")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public record Rule(AlignmentRule alignmentRule, RotationRule rotationRule) {
|
||||||
|
public static final Rule DEFAULT = new Rule(AlignmentRule.ANY, RotationRule.ANY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,157 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.item.behavior;
|
||||||
|
|
||||||
|
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager;
|
||||||
|
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids;
|
||||||
|
import net.momirealms.craftengine.bukkit.util.DirectionUtils;
|
||||||
|
import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.AlignmentRule;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.AnchorType;
|
||||||
|
import net.momirealms.craftengine.core.entity.furniture.RotationRule;
|
||||||
|
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
||||||
|
import net.momirealms.craftengine.core.entity.player.InteractionResult;
|
||||||
|
import net.momirealms.craftengine.core.entity.player.Player;
|
||||||
|
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
|
||||||
|
import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory;
|
||||||
|
import net.momirealms.craftengine.core.item.context.UseOnContext;
|
||||||
|
import net.momirealms.craftengine.core.pack.Pack;
|
||||||
|
import net.momirealms.craftengine.core.pack.PendingConfigSection;
|
||||||
|
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||||
|
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
|
||||||
|
import net.momirealms.craftengine.core.plugin.logger.Debugger;
|
||||||
|
import net.momirealms.craftengine.core.util.Direction;
|
||||||
|
import net.momirealms.craftengine.core.util.Key;
|
||||||
|
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||||
|
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||||
|
import net.momirealms.craftengine.core.world.BlockHitResult;
|
||||||
|
import net.momirealms.craftengine.core.world.BlockPos;
|
||||||
|
import net.momirealms.craftengine.core.world.Vec3d;
|
||||||
|
import net.momirealms.craftengine.core.world.World;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class LiquidCollisionFurnitureItemBehavior extends FurnitureItemBehavior {
|
||||||
|
public static final Factory FACTORY = new Factory();
|
||||||
|
private final List<String> liquidTypes;
|
||||||
|
private final boolean sourceOnly;
|
||||||
|
|
||||||
|
public LiquidCollisionFurnitureItemBehavior(Key id, Map<AnchorType, Rule> rules, boolean ignorePlacer, boolean ignoreEntities, boolean sourceOnly, List<String> liquidTypes) {
|
||||||
|
super(id, rules, ignorePlacer, ignoreEntities);
|
||||||
|
this.liquidTypes = liquidTypes;
|
||||||
|
this.sourceOnly = sourceOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InteractionResult useOnBlock(UseOnContext context) {
|
||||||
|
return use(context.getLevel(), context.getPlayer(), context.getHand());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InteractionResult use(World world, @Nullable Player player, InteractionHand hand) {
|
||||||
|
try {
|
||||||
|
if (player == null) return InteractionResult.FAIL;
|
||||||
|
Object blockHitResult = CoreReflections.method$Item$getPlayerPOVHitResult.invoke(null, world.serverWorld(), player.serverPlayer(), CoreReflections.instance$ClipContext$Fluid$ANY);
|
||||||
|
Object blockPos = FastNMS.INSTANCE.field$BlockHitResult$blockPos(blockHitResult);
|
||||||
|
BlockPos above = new BlockPos(FastNMS.INSTANCE.field$Vec3i$x(blockPos), FastNMS.INSTANCE.field$Vec3i$y(blockPos), FastNMS.INSTANCE.field$Vec3i$z(blockPos));
|
||||||
|
Direction direction = DirectionUtils.fromNMSDirection(FastNMS.INSTANCE.field$BlockHitResul$direction(blockHitResult));
|
||||||
|
boolean miss = FastNMS.INSTANCE.field$BlockHitResul$miss(blockHitResult);
|
||||||
|
Vec3d hitPos = LocationUtils.fromVec(CoreReflections.field$HitResult$location.get(blockHitResult));
|
||||||
|
Object fluidType = FastNMS.INSTANCE.method$FluidState$getType(FastNMS.INSTANCE.method$BlockGetter$getFluidState(world.serverWorld(), blockPos));
|
||||||
|
if (fluidType == MFluids.EMPTY) {
|
||||||
|
return InteractionResult.PASS;
|
||||||
|
}
|
||||||
|
String liquid = null;
|
||||||
|
if (fluidType == MFluids.LAVA) {
|
||||||
|
liquid = "lava";
|
||||||
|
} else if (fluidType == MFluids.WATER) {
|
||||||
|
liquid = "water";
|
||||||
|
} else if (fluidType == MFluids.FLOWING_LAVA) {
|
||||||
|
if (this.sourceOnly) return InteractionResult.PASS;
|
||||||
|
liquid = "lava";
|
||||||
|
} else if (fluidType == MFluids.FLOWING_WATER) {
|
||||||
|
if (this.sourceOnly) return InteractionResult.PASS;
|
||||||
|
liquid = "water";
|
||||||
|
}
|
||||||
|
if (!this.liquidTypes.contains(liquid)) {
|
||||||
|
return InteractionResult.PASS;
|
||||||
|
}
|
||||||
|
if (miss) {
|
||||||
|
return super.useOnBlock(new UseOnContext(player, hand, BlockHitResult.miss(hitPos, direction, above)));
|
||||||
|
} else {
|
||||||
|
boolean inside = CoreReflections.field$BlockHitResult$inside.getBoolean(blockHitResult);
|
||||||
|
return super.useOnBlock(new UseOnContext(player, hand, new BlockHitResult(hitPos, direction, above, inside)));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
CraftEngine.instance().logger().warn("Error handling use", e);
|
||||||
|
return InteractionResult.FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Factory implements ItemBehaviorFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemBehavior create(Pack pack, Path path, String node, Key key, Map<String, Object> arguments) {
|
||||||
|
Object id = arguments.get("furniture");
|
||||||
|
if (id == null) {
|
||||||
|
throw new LocalizedResourceConfigException("warning.config.item.behavior.furniture.missing_furniture", new IllegalArgumentException("Missing required parameter 'furniture' for furniture_item behavior"));
|
||||||
|
}
|
||||||
|
Map<String, Object> rulesMap = ResourceConfigUtils.getAsMapOrNull(arguments.get("rules"), "rules");
|
||||||
|
Key furnitureId;
|
||||||
|
if (id instanceof Map<?,?> map) {
|
||||||
|
Map<String, Object> furnitureSection;
|
||||||
|
if (map.containsKey(key.toString())) {
|
||||||
|
// 防呆
|
||||||
|
furnitureSection = MiscUtils.castToMap(map.get(key.toString()), false);
|
||||||
|
BukkitFurnitureManager.instance().parser().addPendingConfigSection(new PendingConfigSection(pack, path, node, key, furnitureSection));
|
||||||
|
} else {
|
||||||
|
furnitureSection = MiscUtils.castToMap(map, false);
|
||||||
|
BukkitFurnitureManager.instance().parser().addPendingConfigSection(new PendingConfigSection(pack, path, node, key, furnitureSection));
|
||||||
|
}
|
||||||
|
furnitureId = key;
|
||||||
|
// 兼容老版本
|
||||||
|
if (rulesMap == null) {
|
||||||
|
Map<String, Object> placementSection = ResourceConfigUtils.getAsMapOrNull(furnitureSection.get("placement"), "placement");
|
||||||
|
if (placementSection != null) {
|
||||||
|
rulesMap = new HashMap<>();
|
||||||
|
for (Map.Entry<String, Object> entry : placementSection.entrySet()) {
|
||||||
|
if (entry.getValue() instanceof Map<?, ?> innerMap) {
|
||||||
|
if (innerMap.containsKey("rules")) {
|
||||||
|
Map<String, Object> rules = ResourceConfigUtils.getAsMap(innerMap.get("rules"), "rules");
|
||||||
|
if (ALLOWED_ANCHOR_TYPES.contains(entry.getKey())) {
|
||||||
|
rulesMap.put(entry.getKey(), rules);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
furnitureId = Key.of(id.toString());
|
||||||
|
}
|
||||||
|
Map<AnchorType, Rule> rules = new EnumMap<>(AnchorType.class);
|
||||||
|
if (rulesMap != null) {
|
||||||
|
for (Map.Entry<String, Object> entry : rulesMap.entrySet()) {
|
||||||
|
try {
|
||||||
|
AnchorType type = AnchorType.valueOf(entry.getKey().toUpperCase(Locale.ROOT));
|
||||||
|
Map<String, Object> ruleSection = MiscUtils.castToMap(entry.getValue(), true);
|
||||||
|
rules.put(type, new Rule(
|
||||||
|
ResourceConfigUtils.getAsEnum(ruleSection.get("alignment"), AlignmentRule.class, AlignmentRule.ANY),
|
||||||
|
ResourceConfigUtils.getAsEnum(ruleSection.get("rotation"), RotationRule.class, RotationRule.ANY)
|
||||||
|
));
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
Debugger.FURNITURE.debug(() -> "Invalid anchor type: " + entry.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new LiquidCollisionFurnitureItemBehavior(furnitureId, rules,
|
||||||
|
ResourceConfigUtils.getAsBoolean(arguments.get("ignore-placer"), "ignore-placer"),
|
||||||
|
ResourceConfigUtils.getAsBoolean(arguments.get("ignore-entities"), "ignore-entities"),
|
||||||
|
ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("source-only", true), "source-only"),
|
||||||
|
MiscUtils.getAsStringList(arguments.get("liquid-type"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,22 +3,21 @@ package net.momirealms.craftengine.bukkit.item.listener;
|
|||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
|
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
|
||||||
import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks;
|
import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks;
|
||||||
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
|
||||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections;
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections;
|
||||||
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||||
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
|
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
|
||||||
import net.momirealms.craftengine.bukkit.util.ItemStackUtils;
|
|
||||||
import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
import net.momirealms.craftengine.bukkit.util.LocationUtils;
|
||||||
import net.momirealms.craftengine.core.block.CustomBlock;
|
import net.momirealms.craftengine.core.block.CustomBlock;
|
||||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||||
import net.momirealms.craftengine.core.block.UpdateOption;
|
import net.momirealms.craftengine.core.block.UpdateOption;
|
||||||
import net.momirealms.craftengine.core.block.properties.Property;
|
import net.momirealms.craftengine.core.block.properties.Property;
|
||||||
|
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
||||||
import net.momirealms.craftengine.core.item.Item;
|
import net.momirealms.craftengine.core.item.Item;
|
||||||
|
import net.momirealms.craftengine.core.item.ItemKeys;
|
||||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
@@ -44,12 +43,10 @@ public class DebugStickListener implements Listener {
|
|||||||
public void onUseDebugStick(PlayerInteractEvent event) {
|
public void onUseDebugStick(PlayerInteractEvent event) {
|
||||||
Block clickedBlock = event.getClickedBlock();
|
Block clickedBlock = event.getClickedBlock();
|
||||||
if (clickedBlock == null) return;
|
if (clickedBlock == null) return;
|
||||||
ItemStack itemInHand = event.getItem();
|
|
||||||
if (ItemStackUtils.isEmpty(itemInHand)) return;
|
|
||||||
Material material = itemInHand.getType();
|
|
||||||
if (material != Material.DEBUG_STICK) return;
|
|
||||||
Player bukkitPlayer = event.getPlayer();
|
Player bukkitPlayer = event.getPlayer();
|
||||||
BukkitServerPlayer player = BukkitAdaptors.adapt(bukkitPlayer);
|
BukkitServerPlayer player = BukkitAdaptors.adapt(bukkitPlayer);
|
||||||
|
Item<ItemStack> itemInHand = player.getItemInHand(event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND);
|
||||||
|
if (!itemInHand.vanillaId().equals(ItemKeys.DEBUG_STICK)) return;
|
||||||
if (!(player.canInstabuild() && player.hasPermission("minecraft.debugstick")) && !player.hasPermission("minecraft.debugstick.always")) {
|
if (!(player.canInstabuild() && player.hasPermission("minecraft.debugstick")) && !player.hasPermission("minecraft.debugstick.always")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -73,8 +70,7 @@ public class DebugStickListener implements Listener {
|
|||||||
ComponentUtils.adventureToMinecraft(Component.translatable("item.minecraft.debug_stick.empty").arguments(Component.text(blockId))), true);
|
ComponentUtils.adventureToMinecraft(Component.translatable("item.minecraft.debug_stick.empty").arguments(Component.text(blockId))), true);
|
||||||
player.sendPacket(systemChatPacket, false);
|
player.sendPacket(systemChatPacket, false);
|
||||||
} else {
|
} else {
|
||||||
Item<ItemStack> wrapped = BukkitItemManager.instance().wrap(itemInHand);
|
Object storedData = itemInHand.getJavaTag("craftengine:debug_stick_state");
|
||||||
Object storedData = wrapped.getJavaTag("craftengine:debug_stick_state");
|
|
||||||
if (storedData == null) storedData = new HashMap<>();
|
if (storedData == null) storedData = new HashMap<>();
|
||||||
if (storedData instanceof Map<?,?> map) {
|
if (storedData instanceof Map<?,?> map) {
|
||||||
Map<String, Object> data = new HashMap<>(MiscUtils.castToMap(map, false));
|
Map<String, Object> data = new HashMap<>(MiscUtils.castToMap(map, false));
|
||||||
@@ -96,7 +92,7 @@ public class DebugStickListener implements Listener {
|
|||||||
} else {
|
} else {
|
||||||
currentProperty = getRelative(properties, currentProperty, player.isSecondaryUseActive());
|
currentProperty = getRelative(properties, currentProperty, player.isSecondaryUseActive());
|
||||||
data.put(blockId, currentProperty.name());
|
data.put(blockId, currentProperty.name());
|
||||||
wrapped.setTag(data, "craftengine:debug_stick_state");
|
itemInHand.setTag(data, "craftengine:debug_stick_state");
|
||||||
Object systemChatPacket = NetworkReflections.constructor$ClientboundSystemChatPacket.newInstance(
|
Object systemChatPacket = NetworkReflections.constructor$ClientboundSystemChatPacket.newInstance(
|
||||||
ComponentUtils.adventureToMinecraft(Component.translatable("item.minecraft.debug_stick.select")
|
ComponentUtils.adventureToMinecraft(Component.translatable("item.minecraft.debug_stick.select")
|
||||||
.arguments(
|
.arguments(
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import net.momirealms.craftengine.core.entity.player.InteractionResult;
|
|||||||
import net.momirealms.craftengine.core.item.CustomItem;
|
import net.momirealms.craftengine.core.item.CustomItem;
|
||||||
import net.momirealms.craftengine.core.item.Item;
|
import net.momirealms.craftengine.core.item.Item;
|
||||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||||
|
import net.momirealms.craftengine.core.item.ItemSettings;
|
||||||
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
|
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
|
||||||
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
|
import net.momirealms.craftengine.core.item.context.BlockPlaceContext;
|
||||||
import net.momirealms.craftengine.core.item.context.UseOnContext;
|
import net.momirealms.craftengine.core.item.context.UseOnContext;
|
||||||
@@ -54,20 +55,16 @@ import org.bukkit.event.enchantment.PrepareItemEnchantEvent;
|
|||||||
import org.bukkit.event.entity.EntityDamageEvent;
|
import org.bukkit.event.entity.EntityDamageEvent;
|
||||||
import org.bukkit.event.entity.EntityPickupItemEvent;
|
import org.bukkit.event.entity.EntityPickupItemEvent;
|
||||||
import org.bukkit.event.entity.FoodLevelChangeEvent;
|
import org.bukkit.event.entity.FoodLevelChangeEvent;
|
||||||
|
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||||
import org.bukkit.event.player.PlayerDropItemEvent;
|
import org.bukkit.event.player.PlayerDropItemEvent;
|
||||||
import org.bukkit.event.player.PlayerInteractEntityEvent;
|
import org.bukkit.event.player.PlayerInteractEntityEvent;
|
||||||
import org.bukkit.event.player.PlayerInteractEvent;
|
import org.bukkit.event.player.PlayerInteractEvent;
|
||||||
import org.bukkit.event.player.PlayerItemConsumeEvent;
|
import org.bukkit.event.player.PlayerItemConsumeEvent;
|
||||||
import org.bukkit.inventory.EnchantingInventory;
|
import org.bukkit.inventory.*;
|
||||||
import org.bukkit.inventory.EquipmentSlot;
|
|
||||||
import org.bukkit.inventory.Inventory;
|
|
||||||
import org.bukkit.inventory.ItemStack;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
public class ItemEventListener implements Listener {
|
public class ItemEventListener implements Listener {
|
||||||
private final BukkitCraftEngine plugin;
|
private final BukkitCraftEngine plugin;
|
||||||
@@ -167,7 +164,7 @@ public class ItemEventListener implements Listener {
|
|||||||
|
|
||||||
// fix client side issues
|
// fix client side issues
|
||||||
if (action.isRightClick() && hitResult != null &&
|
if (action.isRightClick() && hitResult != null &&
|
||||||
InteractUtils.canPlaceVisualBlock(player, BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().literalObject()), hitResult, itemInHand)) {
|
InteractUtils.canPlaceVisualBlock(player, BlockStateUtils.fromBlockData(immutableBlockState.visualBlockState().literalObject()), hitResult, itemInHand)) {
|
||||||
player.updateInventory();
|
player.updateInventory();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,13 +269,13 @@ public class ItemEventListener implements Listener {
|
|||||||
if (immutableBlockState != null) {
|
if (immutableBlockState != null) {
|
||||||
// client won't have sounds if the clientside block is interactable
|
// client won't have sounds if the clientside block is interactable
|
||||||
// so we should check and resend sounds on BlockPlaceEvent
|
// so we should check and resend sounds on BlockPlaceEvent
|
||||||
BlockData craftBlockData = BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().literalObject());
|
BlockData craftBlockData = BlockStateUtils.fromBlockData(immutableBlockState.visualBlockState().literalObject());
|
||||||
if (InteractUtils.isInteractable(player, craftBlockData, hitResult, itemInHand)) {
|
if (InteractUtils.isInteractable(player, craftBlockData, hitResult, itemInHand)) {
|
||||||
if (!serverPlayer.isSecondaryUseActive()) {
|
if (!serverPlayer.isSecondaryUseActive()) {
|
||||||
serverPlayer.setResendSound();
|
serverPlayer.setResendSound();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (BlockStateUtils.isReplaceable(immutableBlockState.customBlockState().literalObject()) && !BlockStateUtils.isReplaceable(immutableBlockState.vanillaBlockState().literalObject())) {
|
if (BlockStateUtils.isReplaceable(immutableBlockState.customBlockState().literalObject()) && !BlockStateUtils.isReplaceable(immutableBlockState.visualBlockState().literalObject())) {
|
||||||
serverPlayer.setResendSwing();
|
serverPlayer.setResendSwing();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -426,7 +423,7 @@ public class ItemEventListener implements Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOW)
|
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
||||||
public void onConsumeItem(PlayerItemConsumeEvent event) {
|
public void onConsumeItem(PlayerItemConsumeEvent event) {
|
||||||
ItemStack consumedItem = event.getItem();
|
ItemStack consumedItem = event.getItem();
|
||||||
if (ItemStackUtils.isEmpty(consumedItem)) return;
|
if (ItemStackUtils.isEmpty(consumedItem)) return;
|
||||||
@@ -435,9 +432,11 @@ public class ItemEventListener implements Listener {
|
|||||||
if (optionalCustomItem.isEmpty()) {
|
if (optionalCustomItem.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
|
||||||
Cancellable cancellable = Cancellable.of(event::isCancelled, event::setCancelled);
|
Cancellable cancellable = Cancellable.of(event::isCancelled, event::setCancelled);
|
||||||
CustomItem<ItemStack> customItem = optionalCustomItem.get();
|
CustomItem<ItemStack> customItem = optionalCustomItem.get();
|
||||||
PlayerOptionalContext context = PlayerOptionalContext.of(BukkitAdaptors.adapt(event.getPlayer()), ContextHolder.builder()
|
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
|
||||||
.withParameter(DirectContextParameters.ITEM_IN_HAND, wrapped)
|
.withParameter(DirectContextParameters.ITEM_IN_HAND, wrapped)
|
||||||
.withParameter(DirectContextParameters.EVENT, cancellable)
|
.withParameter(DirectContextParameters.EVENT, cancellable)
|
||||||
.withParameter(DirectContextParameters.HAND, event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND)
|
.withParameter(DirectContextParameters.HAND, event.getHand() == EquipmentSlot.HAND ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND)
|
||||||
@@ -448,11 +447,19 @@ public class ItemEventListener implements Listener {
|
|||||||
}
|
}
|
||||||
if (event.getPlayer().getGameMode() != GameMode.CREATIVE) {
|
if (event.getPlayer().getGameMode() != GameMode.CREATIVE) {
|
||||||
Key replacement = customItem.settings().consumeReplacement();
|
Key replacement = customItem.settings().consumeReplacement();
|
||||||
if (replacement == null) {
|
if (wrapped.count() == 1) {
|
||||||
event.setReplacement(null);
|
if (replacement != null) {
|
||||||
|
ItemStack replacementItem = this.plugin.itemManager().buildItemStack(replacement, serverPlayer);
|
||||||
|
event.setReplacement(replacementItem);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ItemStack replacementItem = this.plugin.itemManager().buildItemStack(replacement, BukkitAdaptors.adapt(event.getPlayer()));
|
// fixme 如何取消堆叠数量>1的物品的默认replacement
|
||||||
event.setReplacement(replacementItem);
|
if (replacement != null) {
|
||||||
|
Item<ItemStack> replacementItem = this.plugin.itemManager().createWrappedItem(replacement, serverPlayer);
|
||||||
|
if (replacementItem != null) {
|
||||||
|
PlayerUtils.giveItem(serverPlayer, 1, replacementItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -620,4 +627,114 @@ public class ItemEventListener implements Listener {
|
|||||||
}
|
}
|
||||||
event.setCurrentItem((ItemStack) result.finalItem().getItem());
|
event.setCurrentItem((ItemStack) result.finalItem().getItem());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("DuplicatedCode")
|
||||||
|
@EventHandler(ignoreCancelled = true)
|
||||||
|
public void onPlayerDeath(PlayerDeathEvent event) {
|
||||||
|
BukkitItemManager instance = BukkitItemManager.instance();
|
||||||
|
|
||||||
|
// 处理损毁物品
|
||||||
|
if (event.getKeepInventory()) {
|
||||||
|
if (!instance.featureFlag$destroyOnDeathChance()) return;
|
||||||
|
|
||||||
|
Random random = ThreadLocalRandom.current();
|
||||||
|
PlayerInventory inventory = event.getPlayer().getInventory();
|
||||||
|
for (ItemStack item : inventory.getContents()) {
|
||||||
|
if (item == null) continue;
|
||||||
|
|
||||||
|
Optional<CustomItem<ItemStack>> optional = instance.wrap(item).getCustomItem();
|
||||||
|
if (optional.isEmpty()) continue;
|
||||||
|
|
||||||
|
CustomItem<ItemStack> customItem = optional.get();
|
||||||
|
ItemSettings settings = customItem.settings();
|
||||||
|
float destroyChance = settings.destroyOnDeathChance();
|
||||||
|
if (destroyChance <= 0f) continue;
|
||||||
|
|
||||||
|
int totalAmount = item.getAmount();
|
||||||
|
int destroyCount = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < totalAmount; i++) {
|
||||||
|
float rand = random.nextFloat();
|
||||||
|
// 判断是否损毁
|
||||||
|
if (destroyChance > 0f && rand < destroyChance) {
|
||||||
|
destroyCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (destroyCount != 0) {
|
||||||
|
item.setAmount(totalAmount - destroyCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 处理保留 + 损毁物品
|
||||||
|
else {
|
||||||
|
if (!instance.featureFlag$keepOnDeathChance() && !instance.featureFlag$destroyOnDeathChance()) return;
|
||||||
|
Random random = ThreadLocalRandom.current();
|
||||||
|
|
||||||
|
List<ItemStack> itemsToKeep = event.getItemsToKeep();
|
||||||
|
List<ItemStack> itemsToDrop = event.getDrops();
|
||||||
|
|
||||||
|
Iterator<ItemStack> iterator = itemsToDrop.iterator();
|
||||||
|
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
ItemStack item = iterator.next();
|
||||||
|
Optional<CustomItem<ItemStack>> optional = instance.wrap(item).getCustomItem();
|
||||||
|
if (optional.isEmpty()) continue;
|
||||||
|
|
||||||
|
CustomItem<ItemStack> customItem = optional.get();
|
||||||
|
ItemSettings settings = customItem.settings();
|
||||||
|
|
||||||
|
float destroyChance = settings.destroyOnDeathChance();
|
||||||
|
float keepChance = settings.keepOnDeathChance();
|
||||||
|
|
||||||
|
// 如果没有效果,跳过
|
||||||
|
if (destroyChance <= 0f && keepChance <= 0f) continue;
|
||||||
|
|
||||||
|
int totalAmount = item.getAmount();
|
||||||
|
|
||||||
|
int keepCount = 0;
|
||||||
|
int destroyCount = 0;
|
||||||
|
int dropCount = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < totalAmount; i++) {
|
||||||
|
float rand = random.nextFloat();
|
||||||
|
|
||||||
|
// 先判断是否损毁
|
||||||
|
if (destroyChance > 0f && rand < destroyChance) {
|
||||||
|
destroyCount++;
|
||||||
|
}
|
||||||
|
// 然后判断是否保留(在未损毁的物品中)
|
||||||
|
else if (keepChance > 0f && rand < (destroyChance + keepChance)) {
|
||||||
|
keepCount++;
|
||||||
|
}
|
||||||
|
// 否则掉落
|
||||||
|
else {
|
||||||
|
dropCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理结果
|
||||||
|
if (destroyCount == totalAmount) {
|
||||||
|
iterator.remove();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keepCount == 0 && dropCount == 0) {
|
||||||
|
// 实际上不会发生这种情况
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keepCount > 0) {
|
||||||
|
ItemStack keepItem = item.clone();
|
||||||
|
keepItem.setAmount(keepCount);
|
||||||
|
itemsToKeep.add(keepItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dropCount > 0) {
|
||||||
|
item.setAmount(dropCount);
|
||||||
|
} else {
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -407,8 +407,12 @@ public class RecipeEventListener implements Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean hasResult = true;
|
boolean hasResult = true;
|
||||||
|
|
||||||
int realDurabilityPerItem = (int) (repairItem.amount() + repairItem.percent() * maxDamage);
|
int realDurabilityPerItem = (int) (repairItem.amount() + repairItem.percent() * maxDamage);
|
||||||
|
if (realDurabilityPerItem == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int consumeMaxAmount = damage / realDurabilityPerItem + 1;
|
int consumeMaxAmount = damage / realDurabilityPerItem + 1;
|
||||||
int actualConsumedAmount = Math.min(consumeMaxAmount, wrappedSecond.count());
|
int actualConsumedAmount = Math.min(consumeMaxAmount, wrappedSecond.count());
|
||||||
int actualRepairAmount = actualConsumedAmount * realDurabilityPerItem;
|
int actualRepairAmount = actualConsumedAmount * realDurabilityPerItem;
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ public class BukkitVanillaLootManager extends AbstractVanillaLootManager impleme
|
|||||||
|
|
||||||
public class VanillaLootParser extends IdSectionConfigParser {
|
public class VanillaLootParser extends IdSectionConfigParser {
|
||||||
public static final String[] CONFIG_SECTION_NAME = new String[] {"vanilla-loots", "vanilla-loot"};
|
public static final String[] CONFIG_SECTION_NAME = new String[] {"vanilla-loots", "vanilla-loot"};
|
||||||
|
private int count;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int loadingSequence() {
|
public int loadingSequence() {
|
||||||
@@ -104,6 +105,16 @@ public class BukkitVanillaLootManager extends AbstractVanillaLootManager impleme
|
|||||||
return CONFIG_SECTION_NAME;
|
return CONFIG_SECTION_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int count() {
|
||||||
|
return this.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preProcess() {
|
||||||
|
this.count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void parseSection(Pack pack, Path path, String node, Key id, Map<String, Object> section) {
|
public void parseSection(Pack pack, Path path, String node, Key id, Map<String, Object> section) {
|
||||||
String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(section.get("type"), "warning.config.vanilla_loot.missing_type");
|
String type = ResourceConfigUtils.requireNonEmptyStringOrThrow(section.get("type"), "warning.config.vanilla_loot.missing_type");
|
||||||
@@ -147,6 +158,7 @@ public class BukkitVanillaLootManager extends AbstractVanillaLootManager impleme
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData;
|
|||||||
import net.momirealms.craftengine.core.pack.obfuscation.ObfA;
|
import net.momirealms.craftengine.core.pack.obfuscation.ObfA;
|
||||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||||
|
import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
|
||||||
import net.momirealms.craftengine.core.util.Base64Utils;
|
import net.momirealms.craftengine.core.util.Base64Utils;
|
||||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
@@ -94,7 +95,7 @@ public class BukkitPackManager extends AbstractPackManager implements Listener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!Config.sendPackOnUpload()) return;
|
if (!Config.sendPackOnUpload()) return;
|
||||||
CraftEngine.instance().logger().info("Completed uploading resource pack");
|
CraftEngine.instance().logger().info(TranslationManager.instance().translateLog("info.resource_pack.upload"));
|
||||||
for (BukkitServerPlayer player : this.plugin.networkManager().onlineUsers()) {
|
for (BukkitServerPlayer player : this.plugin.networkManager().onlineUsers()) {
|
||||||
sendResourcePack(player);
|
sendResourcePack(player);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
|
|||||||
import net.momirealms.craftengine.bukkit.block.behavior.BukkitBlockBehaviors;
|
import net.momirealms.craftengine.bukkit.block.behavior.BukkitBlockBehaviors;
|
||||||
import net.momirealms.craftengine.bukkit.block.entity.renderer.element.BukkitBlockEntityElementConfigs;
|
import net.momirealms.craftengine.bukkit.block.entity.renderer.element.BukkitBlockEntityElementConfigs;
|
||||||
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager;
|
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager;
|
||||||
import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.BukkitHitBoxTypes;
|
import net.momirealms.craftengine.bukkit.entity.furniture.element.BukkitFurnitureElementConfigs;
|
||||||
|
import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.BukkitFurnitureHitboxTypes;
|
||||||
import net.momirealms.craftengine.bukkit.entity.projectile.BukkitProjectileManager;
|
import net.momirealms.craftengine.bukkit.entity.projectile.BukkitProjectileManager;
|
||||||
import net.momirealms.craftengine.bukkit.entity.seat.BukkitSeatManager;
|
import net.momirealms.craftengine.bukkit.entity.seat.BukkitSeatManager;
|
||||||
import net.momirealms.craftengine.bukkit.font.BukkitFontManager;
|
import net.momirealms.craftengine.bukkit.font.BukkitFontManager;
|
||||||
@@ -26,6 +27,7 @@ import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
|||||||
import net.momirealms.craftengine.bukkit.sound.BukkitSoundManager;
|
import net.momirealms.craftengine.bukkit.sound.BukkitSoundManager;
|
||||||
import net.momirealms.craftengine.bukkit.util.EventUtils;
|
import net.momirealms.craftengine.bukkit.util.EventUtils;
|
||||||
import net.momirealms.craftengine.bukkit.world.BukkitWorldManager;
|
import net.momirealms.craftengine.bukkit.world.BukkitWorldManager;
|
||||||
|
import net.momirealms.craftengine.bukkit.world.score.BukkitTeamManager;
|
||||||
import net.momirealms.craftengine.core.item.ItemManager;
|
import net.momirealms.craftengine.core.item.ItemManager;
|
||||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||||
import net.momirealms.craftengine.core.plugin.classpath.ClassPathAppender;
|
import net.momirealms.craftengine.core.plugin.classpath.ClassPathAppender;
|
||||||
@@ -160,8 +162,9 @@ public class BukkitCraftEngine extends CraftEngine {
|
|||||||
super.onPluginLoad();
|
super.onPluginLoad();
|
||||||
BukkitBlockBehaviors.init();
|
BukkitBlockBehaviors.init();
|
||||||
BukkitItemBehaviors.init();
|
BukkitItemBehaviors.init();
|
||||||
BukkitHitBoxTypes.init();
|
BukkitFurnitureHitboxTypes.init();
|
||||||
BukkitBlockEntityElementConfigs.init();
|
BukkitBlockEntityElementConfigs.init();
|
||||||
|
BukkitFurnitureElementConfigs.init();
|
||||||
// 初始化 onload 阶段的兼容性
|
// 初始化 onload 阶段的兼容性
|
||||||
super.compatibilityManager().onLoad();
|
super.compatibilityManager().onLoad();
|
||||||
// 创建网络管理器
|
// 创建网络管理器
|
||||||
@@ -190,6 +193,8 @@ public class BukkitCraftEngine extends CraftEngine {
|
|||||||
super.seatManager = new BukkitSeatManager(this);
|
super.seatManager = new BukkitSeatManager(this);
|
||||||
// 初始化家具管理器
|
// 初始化家具管理器
|
||||||
super.furnitureManager = new BukkitFurnitureManager(this);
|
super.furnitureManager = new BukkitFurnitureManager(this);
|
||||||
|
// 初始化队伍管理器
|
||||||
|
super.teamManager = new BukkitTeamManager(this);
|
||||||
// 注册默认的parser
|
// 注册默认的parser
|
||||||
this.registerDefaultParsers();
|
this.registerDefaultParsers();
|
||||||
// 完成加载
|
// 完成加载
|
||||||
@@ -369,6 +374,11 @@ public class BukkitCraftEngine extends CraftEngine {
|
|||||||
return (BukkitFontManager) fontManager;
|
return (BukkitFontManager) fontManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BukkitWorldManager worldManager() {
|
||||||
|
return (BukkitWorldManager) worldManager;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||||
@Override
|
@Override
|
||||||
public void saveResource(String resourcePath) {
|
public void saveResource(String resourcePath) {
|
||||||
|
|||||||
@@ -1,24 +1,18 @@
|
|||||||
package net.momirealms.craftengine.bukkit.plugin;
|
package net.momirealms.craftengine.bukkit.plugin;
|
||||||
|
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
|
||||||
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
|
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
|
||||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
|
||||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistryOps;
|
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistryOps;
|
||||||
import net.momirealms.craftengine.bukkit.util.ParticleUtils;
|
import net.momirealms.craftengine.bukkit.util.ParticleUtils;
|
||||||
import net.momirealms.craftengine.bukkit.world.particle.BukkitParticleType;
|
import net.momirealms.craftengine.bukkit.world.particle.BukkitParticleType;
|
||||||
import net.momirealms.craftengine.core.plugin.Platform;
|
import net.momirealms.craftengine.core.plugin.Platform;
|
||||||
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
|
|
||||||
import net.momirealms.craftengine.core.util.Key;
|
import net.momirealms.craftengine.core.util.Key;
|
||||||
import net.momirealms.craftengine.core.world.World;
|
import net.momirealms.craftengine.core.world.World;
|
||||||
import net.momirealms.craftengine.core.world.particle.ParticleType;
|
import net.momirealms.craftengine.core.world.particle.ParticleType;
|
||||||
import net.momirealms.sparrow.nbt.CompoundTag;
|
|
||||||
import net.momirealms.sparrow.nbt.Tag;
|
import net.momirealms.sparrow.nbt.Tag;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Particle;
|
import org.bukkit.Particle;
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class BukkitPlatform implements Platform {
|
public class BukkitPlatform implements Platform {
|
||||||
private final BukkitCraftEngine plugin;
|
private final BukkitCraftEngine plugin;
|
||||||
|
|
||||||
@@ -31,34 +25,11 @@ public class BukkitPlatform implements Platform {
|
|||||||
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), command);
|
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), command);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public Object snbtToJava(String nbt) {
|
|
||||||
try {
|
|
||||||
Object tag = FastNMS.INSTANCE.method$TagParser$parseCompoundFully("{\"root\":" + nbt + "}");
|
|
||||||
Map<String, Object> map = (Map<String, Object>) MRegistryOps.NBT.convertTo(MRegistryOps.JAVA, tag);
|
|
||||||
return map.get("root");
|
|
||||||
} catch (CommandSyntaxException e) {
|
|
||||||
throw new LocalizedResourceConfigException("warning.config.type.snbt.invalid_syntax", e, nbt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Tag jsonToSparrowNBT(JsonElement json) {
|
public Tag jsonToSparrowNBT(JsonElement json) {
|
||||||
return MRegistryOps.JSON.convertTo(MRegistryOps.SPARROW_NBT, json);
|
return MRegistryOps.JSON.convertTo(MRegistryOps.SPARROW_NBT, json);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Tag snbtToSparrowNBT(String nbt) {
|
|
||||||
try {
|
|
||||||
Object tag = FastNMS.INSTANCE.method$TagParser$parseCompoundFully("{\"root\":" + nbt + "}");
|
|
||||||
CompoundTag map = (CompoundTag) MRegistryOps.NBT.convertTo(MRegistryOps.SPARROW_NBT, tag);
|
|
||||||
return map.get("root");
|
|
||||||
} catch (CommandSyntaxException e) {
|
|
||||||
throw new LocalizedResourceConfigException("warning.config.type.snbt.invalid_syntax", e, nbt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Tag javaToSparrowNBT(Object object) {
|
public Tag javaToSparrowNBT(Object object) {
|
||||||
return MRegistryOps.JAVA.convertTo(MRegistryOps.SPARROW_NBT, object);
|
return MRegistryOps.JAVA.convertTo(MRegistryOps.SPARROW_NBT, object);
|
||||||
|
|||||||
@@ -42,12 +42,16 @@ public class BukkitCommandManager extends AbstractCommandManager<CommandSender>
|
|||||||
new SearchUsageAdminCommand(this, plugin),
|
new SearchUsageAdminCommand(this, plugin),
|
||||||
new TestCommand(this, plugin),
|
new TestCommand(this, plugin),
|
||||||
new SetLocaleCommand(this, plugin),
|
new SetLocaleCommand(this, plugin),
|
||||||
|
new SetDisplayEntityViewDistanceScaleCommand(this, plugin),
|
||||||
|
new SetEntityCullingDistanceScaleCommand(this, plugin),
|
||||||
|
new ToggleEntityCullingCommand(this, plugin),
|
||||||
new UnsetLocaleCommand(this, plugin),
|
new UnsetLocaleCommand(this, plugin),
|
||||||
new DebugGetBlockStateRegistryIdCommand(this, plugin),
|
new DebugGetBlockStateRegistryIdCommand(this, plugin),
|
||||||
new DebugGetBlockInternalIdCommand(this, plugin),
|
new DebugGetBlockInternalIdCommand(this, plugin),
|
||||||
new DebugAppearanceStateUsageCommand(this, plugin),
|
new DebugAppearanceStateUsageCommand(this, plugin),
|
||||||
new DebugClearCooldownCommand(this, plugin),
|
new DebugClearCooldownCommand(this, plugin),
|
||||||
new DebugEntityIdCommand(this, plugin),
|
new DebugEntityIdCommand(this, plugin),
|
||||||
|
new DebugFurnitureCommand(this, plugin),
|
||||||
new DebugRealStateUsageCommand(this, plugin),
|
new DebugRealStateUsageCommand(this, plugin),
|
||||||
new DebugItemDataCommand(this, plugin),
|
new DebugItemDataCommand(this, plugin),
|
||||||
new DebugSetBlockCommand(this, plugin),
|
new DebugSetBlockCommand(this, plugin),
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ public class DebugCleanCacheCommand extends BukkitCommandFeature<CommandSender>
|
|||||||
Set<BlockStateWrapper> ids = new HashSet<>();
|
Set<BlockStateWrapper> ids = new HashSet<>();
|
||||||
for (CustomBlock customBlock : instance.loadedBlocks().values()) {
|
for (CustomBlock customBlock : instance.loadedBlocks().values()) {
|
||||||
for (ImmutableBlockState state : customBlock.variantProvider().states()) {
|
for (ImmutableBlockState state : customBlock.variantProvider().states()) {
|
||||||
ids.add(state.vanillaBlockState());
|
ids.add(state.visualBlockState());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VisualBlockStateAllocator visualBlockStateAllocator = instance.blockParser().visualBlockStateAllocator();
|
VisualBlockStateAllocator visualBlockStateAllocator = instance.blockParser().visualBlockStateAllocator();
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.plugin.command.feature;
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||||
|
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||||
|
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.incendo.cloud.Command;
|
||||||
|
|
||||||
|
public class DebugFurnitureCommand extends BukkitCommandFeature<CommandSender> {
|
||||||
|
|
||||||
|
public DebugFurnitureCommand(CraftEngineCommandManager<CommandSender> commandManager, CraftEngine plugin) {
|
||||||
|
super(commandManager, plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Command.Builder<? extends CommandSender> assembleCommand(org.incendo.cloud.CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
|
||||||
|
return builder
|
||||||
|
.senderType(Player.class)
|
||||||
|
.handler(context -> {
|
||||||
|
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(context.sender());
|
||||||
|
boolean b = !serverPlayer.enableFurnitureDebug();
|
||||||
|
serverPlayer.setEnableFurnitureDebug(b);
|
||||||
|
serverPlayer.sendMessage(Component.text("Furniture Debug Mode: ").append(Component.text(b ? "ON" : "OFF").color(b ? NamedTextColor.GREEN : NamedTextColor.RED)), false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFeatureID() {
|
||||||
|
return "debug_furniture";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ import org.incendo.cloud.parser.standard.StringParser;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
@@ -139,7 +140,7 @@ public class DebugGenerateInternalAssetsCommand extends BukkitCommandFeature<Com
|
|||||||
private void collectListJson(Path folder, String prefix, Consumer<String> callback) {
|
private void collectListJson(Path folder, String prefix, Consumer<String> callback) {
|
||||||
try (InputStream inputStream = Files.newInputStream(folder.resolve("_list.json"))) {
|
try (InputStream inputStream = Files.newInputStream(folder.resolve("_list.json"))) {
|
||||||
String s = prefix.isEmpty() ? "" : (prefix + "/");
|
String s = prefix.isEmpty() ? "" : (prefix + "/");
|
||||||
JsonObject listJson = JsonParser.parseReader(new InputStreamReader(inputStream)).getAsJsonObject();
|
JsonObject listJson = JsonParser.parseReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)).getAsJsonObject();
|
||||||
JsonArray fileList = listJson.getAsJsonArray("files");
|
JsonArray fileList = listJson.getAsJsonArray("files");
|
||||||
for (JsonElement element : fileList) {
|
for (JsonElement element : fileList) {
|
||||||
if (element instanceof JsonPrimitive primitive) {
|
if (element instanceof JsonPrimitive primitive) {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture;
|
|||||||
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager;
|
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager;
|
||||||
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||||
import net.momirealms.craftengine.bukkit.util.KeyUtils;
|
import net.momirealms.craftengine.bukkit.util.KeyUtils;
|
||||||
import net.momirealms.craftengine.core.entity.furniture.AnchorType;
|
|
||||||
import net.momirealms.craftengine.core.entity.furniture.CustomFurniture;
|
import net.momirealms.craftengine.core.entity.furniture.CustomFurniture;
|
||||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||||
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
|
||||||
@@ -19,10 +18,11 @@ import org.incendo.cloud.bukkit.parser.NamespacedKeyParser;
|
|||||||
import org.incendo.cloud.bukkit.parser.location.LocationParser;
|
import org.incendo.cloud.bukkit.parser.location.LocationParser;
|
||||||
import org.incendo.cloud.context.CommandContext;
|
import org.incendo.cloud.context.CommandContext;
|
||||||
import org.incendo.cloud.context.CommandInput;
|
import org.incendo.cloud.context.CommandInput;
|
||||||
import org.incendo.cloud.parser.standard.EnumParser;
|
import org.incendo.cloud.parser.standard.StringParser;
|
||||||
import org.incendo.cloud.suggestion.Suggestion;
|
import org.incendo.cloud.suggestion.Suggestion;
|
||||||
import org.incendo.cloud.suggestion.SuggestionProvider;
|
import org.incendo.cloud.suggestion.SuggestionProvider;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
@@ -42,7 +42,16 @@ public class DebugSpawnFurnitureCommand extends BukkitCommandFeature<CommandSend
|
|||||||
return CompletableFuture.completedFuture(plugin().furnitureManager().cachedSuggestions());
|
return CompletableFuture.completedFuture(plugin().furnitureManager().cachedSuggestions());
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.optional("anchor-type", EnumParser.enumParser(AnchorType.class))
|
.optional("variant", StringParser.stringComponent().suggestionProvider(new SuggestionProvider<>() {
|
||||||
|
@Override
|
||||||
|
public @NonNull CompletableFuture<? extends @NonNull Iterable<? extends @NonNull Suggestion>> suggestionsFuture(@NonNull CommandContext<Object> context, @NonNull CommandInput input) {
|
||||||
|
NamespacedKey namespacedKey = context.get("id");
|
||||||
|
Key id = KeyUtils.namespacedKey2Key(namespacedKey);
|
||||||
|
BukkitFurnitureManager furnitureManager = BukkitFurnitureManager.instance();
|
||||||
|
Optional<CustomFurniture> optionalCustomFurniture = furnitureManager.furnitureById(id);
|
||||||
|
return optionalCustomFurniture.<CompletableFuture<? extends Iterable<? extends Suggestion>>>map(config -> CompletableFuture.completedFuture(config.variants().keySet().stream().map(Suggestion::suggestion).toList())).orElseGet(() -> CompletableFuture.completedFuture(List.of()));
|
||||||
|
}
|
||||||
|
}))
|
||||||
.flag(FlagKeys.SILENT_FLAG)
|
.flag(FlagKeys.SILENT_FLAG)
|
||||||
.handler(context -> {
|
.handler(context -> {
|
||||||
NamespacedKey namespacedKey = context.get("id");
|
NamespacedKey namespacedKey = context.get("id");
|
||||||
@@ -54,9 +63,9 @@ public class DebugSpawnFurnitureCommand extends BukkitCommandFeature<CommandSend
|
|||||||
}
|
}
|
||||||
Location location = context.get("location");
|
Location location = context.get("location");
|
||||||
CustomFurniture customFurniture = optionalCustomFurniture.get();
|
CustomFurniture customFurniture = optionalCustomFurniture.get();
|
||||||
AnchorType anchorType = (AnchorType) context.optional("anchor-type").orElse(customFurniture.getAnyAnchorType());
|
String variant = (String) context.optional("variant").orElse(customFurniture.anyVariantName());
|
||||||
boolean playSound = context.flags().hasFlag("silent");
|
boolean playSound = context.flags().hasFlag("silent");
|
||||||
CraftEngineFurniture.place(location, customFurniture, anchorType, playSound);
|
CraftEngineFurniture.place(location, customFurniture, variant, playSound);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.plugin.command.feature;
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||||
|
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 org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.incendo.cloud.Command;
|
||||||
|
import org.incendo.cloud.bukkit.parser.PlayerParser;
|
||||||
|
import org.incendo.cloud.parser.standard.DoubleParser;
|
||||||
|
|
||||||
|
public class SetDisplayEntityViewDistanceScaleCommand extends BukkitCommandFeature<CommandSender> {
|
||||||
|
|
||||||
|
public SetDisplayEntityViewDistanceScaleCommand(CraftEngineCommandManager<CommandSender> commandManager, CraftEngine plugin) {
|
||||||
|
super(commandManager, plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Command.Builder<? extends CommandSender> assembleCommand(org.incendo.cloud.CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
|
||||||
|
return builder
|
||||||
|
.flag(FlagKeys.SILENT_FLAG)
|
||||||
|
.required("player", PlayerParser.playerParser())
|
||||||
|
.required("scale", DoubleParser.doubleParser(0.125, 8))
|
||||||
|
.handler(context -> {
|
||||||
|
Player player = context.get("player");
|
||||||
|
double scale = context.get("scale");
|
||||||
|
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
|
||||||
|
serverPlayer.setDisplayEntityViewDistanceScale(scale);
|
||||||
|
handleFeedback(context, MessageConstants.COMMAND_DISPLAY_ENTITY_VIEW_DISTANCE_SCALE_SET_SUCCESS, Component.text(scale), Component.text(player.getName()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFeatureID() {
|
||||||
|
return "set_display_entity_view_distance_scale";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.plugin.command.feature;
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||||
|
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 org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.incendo.cloud.Command;
|
||||||
|
import org.incendo.cloud.bukkit.parser.PlayerParser;
|
||||||
|
import org.incendo.cloud.parser.standard.DoubleParser;
|
||||||
|
|
||||||
|
public class SetEntityCullingDistanceScaleCommand extends BukkitCommandFeature<CommandSender> {
|
||||||
|
|
||||||
|
public SetEntityCullingDistanceScaleCommand(CraftEngineCommandManager<CommandSender> commandManager, CraftEngine plugin) {
|
||||||
|
super(commandManager, plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Command.Builder<? extends CommandSender> assembleCommand(org.incendo.cloud.CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
|
||||||
|
return builder
|
||||||
|
.flag(FlagKeys.SILENT_FLAG)
|
||||||
|
.required("player", PlayerParser.playerParser())
|
||||||
|
.required("scale", DoubleParser.doubleParser(0.125, 8))
|
||||||
|
.handler(context -> {
|
||||||
|
Player player = context.get("player");
|
||||||
|
double scale = context.get("scale");
|
||||||
|
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
|
||||||
|
serverPlayer.setEntityCullingDistanceScale(scale);
|
||||||
|
handleFeedback(context, MessageConstants.COMMAND_ENTITY_CULLING_DISTANCE_SCALE_SET_SUCCESS, Component.text(scale), Component.text(player.getName()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFeatureID() {
|
||||||
|
return "set_entity_culling_distance_scale";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package net.momirealms.craftengine.bukkit.plugin.command.feature;
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
|
||||||
|
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||||
|
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.config.Config;
|
||||||
|
import net.momirealms.craftengine.core.plugin.locale.MessageConstants;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.incendo.cloud.Command;
|
||||||
|
import org.incendo.cloud.bukkit.parser.PlayerParser;
|
||||||
|
import org.incendo.cloud.parser.standard.BooleanParser;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class ToggleEntityCullingCommand extends BukkitCommandFeature<CommandSender> {
|
||||||
|
|
||||||
|
public ToggleEntityCullingCommand(CraftEngineCommandManager<CommandSender> commandManager, CraftEngine plugin) {
|
||||||
|
super(commandManager, plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Command.Builder<? extends CommandSender> assembleCommand(org.incendo.cloud.CommandManager<CommandSender> manager, Command.Builder<CommandSender> builder) {
|
||||||
|
return builder
|
||||||
|
.flag(FlagKeys.SILENT_FLAG)
|
||||||
|
.required("player", PlayerParser.playerParser())
|
||||||
|
.optional("state", BooleanParser.booleanParser())
|
||||||
|
.handler(context -> {
|
||||||
|
if (!Config.enableEntityCulling()) {
|
||||||
|
plugin().senderFactory().wrap(context.sender()).sendMessage(Component.text("Entity culling is not enabled on this server").color(NamedTextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Player player = context.get("player");
|
||||||
|
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
|
||||||
|
Optional<Boolean> state = context.optional("state");
|
||||||
|
boolean isEnabled = serverPlayer.enableEntityCulling();
|
||||||
|
if (state.isPresent()) {
|
||||||
|
serverPlayer.setEnableEntityCulling(state.get());
|
||||||
|
handleFeedback(context, MessageConstants.COMMAND_TOGGLE_ENTITY_CULLING_SUCCESS, Component.text(state.get()), Component.text(player.getName()));
|
||||||
|
} else {
|
||||||
|
serverPlayer.setEnableEntityCulling(!isEnabled);
|
||||||
|
handleFeedback(context, MessageConstants.COMMAND_TOGGLE_ENTITY_CULLING_SUCCESS, Component.text(!isEnabled), Component.text(player.getName()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFeatureID() {
|
||||||
|
return "toggle_entity_culling";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -283,10 +283,10 @@ public final class WorldStorageInjector {
|
|||||||
if (Config.enableLightSystem()) {
|
if (Config.enableLightSystem()) {
|
||||||
if (previousImmutableBlockState.isEmpty()) {
|
if (previousImmutableBlockState.isEmpty()) {
|
||||||
// 原版块到自定义块,只需要判断新块是否和客户端视觉一致
|
// 原版块到自定义块,只需要判断新块是否和客户端视觉一致
|
||||||
updateLight(holder, newImmutableBlockState.vanillaBlockState().literalObject(), newState, x, y, z);
|
updateLight(holder, newImmutableBlockState.visualBlockState().literalObject(), newState, x, y, z);
|
||||||
} else {
|
} else {
|
||||||
// 自定义块到自定义块
|
// 自定义块到自定义块
|
||||||
updateLight$complex(holder, newImmutableBlockState.vanillaBlockState().literalObject(), newState, previousState, x, y, z);
|
updateLight$complex(holder, newImmutableBlockState.visualBlockState().literalObject(), newState, previousState, x, y, z);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -311,7 +311,7 @@ public final class WorldStorageInjector {
|
|||||||
}
|
}
|
||||||
if (Config.enableLightSystem()) {
|
if (Config.enableLightSystem()) {
|
||||||
// 自定义块到原版块,只需要判断旧块是否和客户端一直
|
// 自定义块到原版块,只需要判断旧块是否和客户端一直
|
||||||
BlockStateWrapper wrapper = previous.vanillaBlockState();
|
BlockStateWrapper wrapper = previous.visualBlockState();
|
||||||
if (wrapper != null) {
|
if (wrapper != null) {
|
||||||
updateLight(holder, wrapper.literalObject(), previousState, x, y, z);
|
updateLight(holder, wrapper.literalObject(), previousState, x, y, z);
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user