9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-20 15:39:22 +00:00

Merge pull request #434 from Xiao-MoMi/dev

0.0.65
This commit is contained in:
XiaoMoMi
2025-11-02 02:26:59 +08:00
committed by GitHub
750 changed files with 18875 additions and 8669 deletions

View File

@@ -29,18 +29,18 @@ subprojects {
filesMatching(arrayListOf("commands.yml", "config.yml")) { filesMatching(arrayListOf("commands.yml", "config.yml")) {
expand( expand(
Pair("project_version", rootProject.properties["project_version"]), Pair("project_version", rootProject.properties["project_version"]!!),
Pair("config_version", rootProject.properties["config_version"]), Pair("config_version", rootProject.properties["config_version"]!!),
Pair("lang_version", rootProject.properties["lang_version"]) Pair("lang_version", rootProject.properties["lang_version"]!!)
) )
} }
} }
} }
fun versionBanner() = project.providers.exec { fun versionBanner(): String = project.providers.exec {
commandLine("git", "rev-parse", "--short=8", "HEAD") commandLine("git", "rev-parse", "--short=8", "HEAD")
}.standardOutput.asText.map { it.trim() }.getOrElse("Unknown") }.standardOutput.asText.map { it.trim() }.getOrElse("Unknown")
fun builder() = project.providers.exec { fun builder(): String = project.providers.exec {
commandLine("git", "config", "user.name") commandLine("git", "config", "user.name")
}.standardOutput.asText.map { it.trim() }.getOrElse("Unknown") }.standardOutput.asText.map { it.trim() }.getOrElse("Unknown")

View File

@@ -80,7 +80,7 @@ tasks.withType<JavaCompile> {
} }
artifacts { artifacts {
archives(tasks.shadowJar) implementation(tasks.shadowJar)
} }
tasks { tasks {

View File

@@ -17,6 +17,7 @@ repositories {
maven("https://repo.hiusers.com/releases") // zaphkiel maven("https://repo.hiusers.com/releases") // zaphkiel
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
} }
dependencies { dependencies {
@@ -41,6 +42,8 @@ dependencies {
// MMOItems // MMOItems
compileOnly("net.Indyuce:MMOItems-API:6.10-SNAPSHOT") compileOnly("net.Indyuce:MMOItems-API:6.10-SNAPSHOT")
compileOnly("io.lumine:MythicLib-dist:1.6.2-SNAPSHOT") compileOnly("io.lumine:MythicLib-dist:1.6.2-SNAPSHOT")
// Nexo
compileOnly("com.nexomc:nexo:1.13.0")
// LuckPerms // LuckPerms
compileOnly("net.luckperms:api:5.4") compileOnly("net.luckperms:api:5.4")
// viaversion // viaversion
@@ -63,6 +66,8 @@ dependencies {
compileOnly("com.github.Zrips:Jobs:v5.2.2.3") compileOnly("com.github.Zrips:Jobs:v5.2.2.3")
// CustomFishing // CustomFishing
compileOnly("net.momirealms:custom-fishing:2.3.3") compileOnly("net.momirealms:custom-fishing:2.3.3")
// CustomNameplates
compileOnly("net.momirealms:custom-nameplates:3.0.33")
// eco // eco
compileOnly("com.willfp:eco:6.70.1") compileOnly("com.willfp:eco:6.70.1")
compileOnly("com.willfp:EcoJobs:3.56.1") compileOnly("com.willfp:EcoJobs:3.56.1")

View File

@@ -1,5 +1,6 @@
package net.momirealms.craftengine.bukkit.compatibility; package net.momirealms.craftengine.bukkit.compatibility;
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.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;
@@ -16,6 +17,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.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;
import net.momirealms.craftengine.bukkit.font.BukkitFontManager; import net.momirealms.craftengine.bukkit.font.BukkitFontManager;
@@ -28,9 +30,12 @@ import net.momirealms.craftengine.core.loot.LootConditions;
import net.momirealms.craftengine.core.plugin.compatibility.CompatibilityManager; import net.momirealms.craftengine.core.plugin.compatibility.CompatibilityManager;
import net.momirealms.craftengine.core.plugin.compatibility.LevelerProvider; import net.momirealms.craftengine.core.plugin.compatibility.LevelerProvider;
import net.momirealms.craftengine.core.plugin.compatibility.ModelProvider; import net.momirealms.craftengine.core.plugin.compatibility.ModelProvider;
import net.momirealms.craftengine.core.plugin.compatibility.TagResolverProvider;
import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.Config;
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.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;
import net.momirealms.craftengine.core.world.WorldManager; import net.momirealms.craftengine.core.world.WorldManager;
@@ -43,6 +48,8 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
private final BukkitCraftEngine plugin; private final BukkitCraftEngine plugin;
private final Map<String, ModelProvider> modelProviders; private final Map<String, ModelProvider> modelProviders;
private final Map<String, LevelerProvider> levelerProviders; private final Map<String, LevelerProvider> levelerProviders;
private final Map<String, TagResolverProvider> tagResolverProviders;
private TagResolverProvider[] tagResolverProviderArray = null;
private boolean hasPlaceholderAPI; private boolean hasPlaceholderAPI;
public BukkitCompatibilityManager(BukkitCraftEngine plugin) { public BukkitCompatibilityManager(BukkitCraftEngine plugin) {
@@ -52,6 +59,7 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
"BetterModel", BetterModelModel::new "BetterModel", BetterModelModel::new
)); ));
this.levelerProviders = new HashMap<>(); this.levelerProviders = new HashMap<>();
this.tagResolverProviders = new HashMap<>();
} }
@Override @Override
@@ -146,6 +154,12 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
new QuickShopItemExpressionHandler(this.plugin).register(); new QuickShopItemExpressionHandler(this.plugin).register();
logHook("QuickShop-Hikari"); logHook("QuickShop-Hikari");
} }
if (this.isPluginEnabled("CustomNameplates")) {
registerTagResolverProvider(new CustomNameplateProviders.Background());
registerTagResolverProvider(new CustomNameplateProviders.Nameplate());
registerTagResolverProvider(new CustomNameplateProviders.Bubble());
logHook("CustomNameplates");
}
} }
@Override @Override
@@ -158,6 +172,13 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
this.levelerProviders.put(plugin, provider); this.levelerProviders.put(plugin, provider);
} }
@Override
public void registerTagResolverProvider(TagResolverProvider provider) {
this.tagResolverProviders.put(provider.name(), provider);
this.tagResolverProviderArray = this.tagResolverProviders.values().toArray(new TagResolverProvider[0]);
FormattedLine.Companion.resetWithCustomResolvers(new ArrayList<>(this.tagResolverProviders.keySet()));
}
private void logHook(String plugin) { private void logHook(String plugin) {
this.plugin.logger().info("[Compatibility] " + plugin + " hooked"); this.plugin.logger().info("[Compatibility] " + plugin + " hooked");
} }
@@ -291,6 +312,10 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
itemManager.registerExternalItemSource(new SlimefunSource()); itemManager.registerExternalItemSource(new SlimefunSource());
logHook("Slimefun"); logHook("Slimefun");
} }
if (this.isPluginEnabled("Nexo")) {
itemManager.registerExternalItemSource(new NexoItemSource());
logHook("Nexo");
}
} }
private Plugin getPlugin(String name) { private Plugin getPlugin(String name) {
@@ -314,7 +339,9 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
@Override @Override
public String parse(Player player, String text) { public String parse(Player player, String text) {
return PlaceholderAPIUtils.parse((org.bukkit.entity.Player) player.platformPlayer(), text); return player == null
? PlaceholderAPIUtils.parse(null, text)
: PlaceholderAPIUtils.parse((org.bukkit.entity.Player) player.platformPlayer(), text);
} }
@Override @Override
@@ -326,4 +353,15 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
public int getPlayerProtocolVersion(UUID uuid) { public int getPlayerProtocolVersion(UUID uuid) {
return ViaVersionUtils.getPlayerProtocolVersion(uuid); return ViaVersionUtils.getPlayerProtocolVersion(uuid);
} }
@Override
public TagResolver[] createExternalTagResolvers(Context context) {
if (this.tagResolverProviderArray == null) return null;
int length = this.tagResolverProviderArray.length;
TagResolver[] resolvers = new TagResolver[length];
for (int i = 0; i < length; i++) {
resolvers[i] = this.tagResolverProviderArray[i].getTagResolver(context);
}
return resolvers;
}
} }

View File

@@ -0,0 +1,29 @@
package net.momirealms.craftengine.bukkit.compatibility.item;
import com.nexomc.nexo.api.NexoItems;
import com.nexomc.nexo.items.ItemBuilder;
import net.momirealms.craftengine.core.item.ExternalItemSource;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
public class NexoItemSource implements ExternalItemSource<ItemStack> {
@Override
public String plugin() {
return "nexo";
}
@Nullable
@Override
public ItemStack build(String id, ItemBuildContext context) {
ItemBuilder itemBuilder = NexoItems.itemFromId(id);
if (itemBuilder == null) return null;
return itemBuilder.build();
}
@Override
public String id(ItemStack item) {
return NexoItems.idFromItem(item);
}
}

View File

@@ -1,7 +1,6 @@
package net.momirealms.craftengine.bukkit.compatibility.quickshop; package net.momirealms.craftengine.bukkit.compatibility.quickshop;
import com.ghostchu.quickshop.api.QuickShopAPI; import com.ghostchu.quickshop.api.QuickShopAPI;
import com.ghostchu.quickshop.api.event.QSConfigurationReloadEvent;
import com.ghostchu.quickshop.api.registry.BuiltInRegistry; import com.ghostchu.quickshop.api.registry.BuiltInRegistry;
import com.ghostchu.quickshop.api.registry.Registry; import com.ghostchu.quickshop.api.registry.Registry;
import com.ghostchu.quickshop.api.registry.builtin.itemexpression.ItemExpressionHandler; import com.ghostchu.quickshop.api.registry.builtin.itemexpression.ItemExpressionHandler;
@@ -9,9 +8,6 @@ import com.ghostchu.quickshop.api.registry.builtin.itemexpression.ItemExpression
import net.momirealms.craftengine.bukkit.api.CraftEngineItems; import net.momirealms.craftengine.bukkit.api.CraftEngineItems;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.Key;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;

View File

@@ -0,0 +1,47 @@
package net.momirealms.craftengine.bukkit.compatibility.tag;
import net.kyori.adventure.text.minimessage.Context;
import net.kyori.adventure.text.minimessage.ParsingException;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.context.PlayerContext;
import net.momirealms.craftengine.core.util.AdventureHelper;
import net.momirealms.customnameplates.api.CustomNameplatesAPI;
import net.momirealms.customnameplates.api.feature.background.Background;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
public class BackgroundTag implements TagResolver {
private final net.momirealms.craftengine.core.plugin.context.Context context;
public BackgroundTag(net.momirealms.craftengine.core.plugin.context.Context context) {
this.context = context;
}
@Override
public @Nullable Tag resolve(@NotNull String name, @NotNull ArgumentQueue arguments, @NotNull Context ctx) throws ParsingException {
if (!this.has(name)) {
return null;
}
String id = arguments.popOr("No background id provided").toString();
Optional<Background> background = CustomNameplatesAPI.getInstance().getBackground(id);
if (background.isEmpty()) {
return null;
}
double left = arguments.popOr("No argument left provided").asDouble().orElseThrow(() -> ctx.newException("Invalid argument number", arguments));
double right = arguments.popOr("No argument right provided").asDouble().orElseThrow(() -> ctx.newException("Invalid argument number", arguments));
String content = arguments.popOr("No argument content provided").toString();
String parsed = this.context instanceof PlayerContext playerContext ? CraftEngine.instance().compatibilityManager().parse(playerContext.player(), content) : CraftEngine.instance().compatibilityManager().parse(null, content);
String textWithImage = CustomNameplatesAPI.getInstance().createTextWithImage(parsed, background.get(), (float) left, (float) right);
return Tag.selfClosingInserting(AdventureHelper.miniMessage().deserialize(textWithImage, this.context.tagResolvers()));
}
@Override
public boolean has(@NotNull String name) {
return "background".equals(name);
}
}

View File

@@ -0,0 +1,47 @@
package net.momirealms.craftengine.bukkit.compatibility.tag;
import net.kyori.adventure.text.minimessage.Context;
import net.kyori.adventure.text.minimessage.ParsingException;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.context.PlayerContext;
import net.momirealms.craftengine.core.util.AdventureHelper;
import net.momirealms.customnameplates.api.CustomNameplatesAPI;
import net.momirealms.customnameplates.api.feature.bubble.Bubble;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
public class BubbleTag implements TagResolver {
private final net.momirealms.craftengine.core.plugin.context.Context context;
public BubbleTag(net.momirealms.craftengine.core.plugin.context.Context context) {
this.context = context;
}
@Override
public @Nullable Tag resolve(@NotNull String name, @NotNull ArgumentQueue arguments, @NotNull Context ctx) throws ParsingException {
if (!this.has(name)) {
return null;
}
String id = arguments.popOr("No bubble id provided").toString();
Optional<Bubble> bubble = CustomNameplatesAPI.getInstance().getBubble(id);
if (bubble.isEmpty()) {
return null;
}
double left = arguments.popOr("No argument left provided").asDouble().orElseThrow(() -> ctx.newException("Invalid argument number", arguments));
double right = arguments.popOr("No argument right provided").asDouble().orElseThrow(() -> ctx.newException("Invalid argument number", arguments));
String content = arguments.popOr("No argument content provided").toString();
String parsed = this.context instanceof PlayerContext playerContext ? CraftEngine.instance().compatibilityManager().parse(playerContext.player(), content) : CraftEngine.instance().compatibilityManager().parse(null, content);
String textWithImage = CustomNameplatesAPI.getInstance().createTextWithImage(parsed, bubble.get(), (float) left, (float) right);
return Tag.selfClosingInserting(AdventureHelper.miniMessage().deserialize(textWithImage, this.context.tagResolvers()));
}
@Override
public boolean has(@NotNull String name) {
return "bubble".equals(name);
}
}

View File

@@ -0,0 +1,44 @@
package net.momirealms.craftengine.bukkit.compatibility.tag;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import net.momirealms.craftengine.core.plugin.compatibility.TagResolverProvider;
import net.momirealms.craftengine.core.plugin.context.Context;
public class CustomNameplateProviders {
public static class Background implements TagResolverProvider {
@Override
public String name() {
return "background";
}
@Override
public TagResolver getTagResolver(Context context) {
return new BackgroundTag(context);
}
}
public static class Nameplate implements TagResolverProvider {
@Override
public String name() {
return "nameplate";
}
@Override
public TagResolver getTagResolver(Context context) {
return new NameplateTag(context);
}
}
public static class Bubble implements TagResolverProvider {
@Override
public String name() {
return "bubble";
}
@Override
public TagResolver getTagResolver(Context context) {
return new BubbleTag(context);
}
}
}

View File

@@ -0,0 +1,47 @@
package net.momirealms.craftengine.bukkit.compatibility.tag;
import net.kyori.adventure.text.minimessage.Context;
import net.kyori.adventure.text.minimessage.ParsingException;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.ArgumentQueue;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.context.PlayerContext;
import net.momirealms.craftengine.core.util.AdventureHelper;
import net.momirealms.customnameplates.api.CustomNameplatesAPI;
import net.momirealms.customnameplates.api.feature.nameplate.Nameplate;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
public class NameplateTag implements TagResolver {
private final net.momirealms.craftengine.core.plugin.context.Context context;
public NameplateTag(net.momirealms.craftengine.core.plugin.context.Context context) {
this.context = context;
}
@Override
public @Nullable Tag resolve(@NotNull String name, @NotNull ArgumentQueue arguments, @NotNull Context ctx) throws ParsingException {
if (!this.has(name)) {
return null;
}
String id = arguments.popOr("No nameplate id provided").toString();
Optional<Nameplate> nameplate = CustomNameplatesAPI.getInstance().getNameplate(id);
if (nameplate.isEmpty()) {
return null;
}
double left = arguments.popOr("No argument left provided").asDouble().orElseThrow(() -> ctx.newException("Invalid argument number", arguments));
double right = arguments.popOr("No argument right provided").asDouble().orElseThrow(() -> ctx.newException("Invalid argument number", arguments));
String content = arguments.popOr("No argument content provided").toString();
String parsed = this.context instanceof PlayerContext playerContext ? CraftEngine.instance().compatibilityManager().parse(playerContext.player(), content) : CraftEngine.instance().compatibilityManager().parse(null, content);
String textWithImage = CustomNameplatesAPI.getInstance().createTextWithImage(parsed, nameplate.get(), (float) left, (float) right);
return Tag.selfClosingInserting(AdventureHelper.miniMessage().deserialize(textWithImage, this.context.tagResolvers()));
}
@Override
public boolean has(@NotNull String name) {
return "nameplate".equals(name);
}
}

View File

@@ -29,5 +29,5 @@ tasks.withType<JavaCompile> {
} }
artifacts { artifacts {
archives(tasks.shadowJar) implementation(tasks.shadowJar)
} }

View File

@@ -7,7 +7,7 @@ import org.bukkit.event.Listener;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
public class DismountListener1_20 implements Listener { public final class DismountListener1_20 implements Listener {
private final BiConsumer<Player, Entity> consumer; private final BiConsumer<Player, Entity> consumer;
public DismountListener1_20(BiConsumer<Player, Entity> consumer) { public DismountListener1_20(BiConsumer<Player, Entity> consumer) {

View File

@@ -8,7 +8,8 @@ import org.bukkit.entity.Player;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
public class LegacyAttributeUtils { public final class LegacyAttributeUtils {
private LegacyAttributeUtils() {}
public static void setMaxHealth(ArmorStand entity) { public static void setMaxHealth(ArmorStand entity) {
Objects.requireNonNull(entity.getAttribute(Attribute.GENERIC_MAX_HEALTH)).setBaseValue(0.01); Objects.requireNonNull(entity.getAttribute(Attribute.GENERIC_MAX_HEALTH)).setBaseValue(0.01);

View File

@@ -4,7 +4,8 @@ import com.mojang.authlib.GameProfile;
import java.util.UUID; import java.util.UUID;
public class LegacyAuthLibUtils { public final class LegacyAuthLibUtils {
private LegacyAuthLibUtils() {}
public static String getName(GameProfile profile) { public static String getName(GameProfile profile) {
return profile.getName(); return profile.getName();

View File

@@ -8,7 +8,8 @@ import org.bukkit.event.entity.CreatureSpawnEvent;
import java.util.function.Consumer; import java.util.function.Consumer;
public class LegacyEntityUtils { public final class LegacyEntityUtils {
private LegacyEntityUtils() {}
public static Entity spawnEntity(World world, Location loc, EntityType type, Consumer<Entity> function) { public static Entity spawnEntity(World world, Location loc, EntityType type, Consumer<Entity> function) {
return world.spawnEntity(loc, type, CreatureSpawnEvent.SpawnReason.CUSTOM, function::accept); return world.spawnEntity(loc, type, CreatureSpawnEvent.SpawnReason.CUSTOM, function::accept);

View File

@@ -1,14 +1,18 @@
package net.momirealms.craftengine.bukkit.util; package net.momirealms.craftengine.bukkit.util;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryAction;
import org.bukkit.event.inventory.InventoryEvent; import org.bukkit.event.inventory.InventoryEvent;
import org.bukkit.event.inventory.PrepareAnvilEvent; import org.bukkit.event.inventory.PrepareAnvilEvent;
import org.bukkit.inventory.AnvilInventory; import org.bukkit.inventory.AnvilInventory;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView; import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.Merchant;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public class LegacyInventoryUtils { public final class LegacyInventoryUtils {
private LegacyInventoryUtils() {}
public static Inventory getTopInventory(Player player) { public static Inventory getTopInventory(Player player) {
return player.getOpenInventory().getTopInventory(); return player.getOpenInventory().getTopInventory();
@@ -67,7 +71,20 @@ public class LegacyInventoryUtils {
player.openWorkbench(null, true); player.openWorkbench(null, true);
} }
public static void openMerchant(Player player, Merchant merchant) {
player.openMerchant(merchant, true);
}
@SuppressWarnings("deprecation")
public static Merchant createMerchant() {
return Bukkit.createMerchant("Villager");
}
public static Player getPlayerFromInventoryEvent(InventoryEvent event) { public static Player getPlayerFromInventoryEvent(InventoryEvent event) {
return (Player) event.getView().getPlayer(); return (Player) event.getView().getPlayer();
} }
public static boolean isHotBarSwapAndReadd(InventoryAction action) {
return action == InventoryAction.HOTBAR_MOVE_AND_READD;
}
} }

View File

@@ -56,7 +56,7 @@ bukkit {
} }
artifacts { artifacts {
archives(tasks.shadowJar) implementation(tasks.shadowJar)
} }
tasks { tasks {

View File

@@ -77,6 +77,9 @@ paper {
register("ViaVersion") { required = false } register("ViaVersion") { required = false }
register("QuickShop-Hikari") { required = false } register("QuickShop-Hikari") { required = false }
// external tag
register("CustomNameplates") { required = false }
// external models // external models
register("ModelEngine") { required = false } register("ModelEngine") { required = false }
register("BetterModel") { required = false } register("BetterModel") { required = false }
@@ -90,6 +93,7 @@ paper {
register("HeadDatabase") { required = false } register("HeadDatabase") { required = false }
register("SX-Item") { required = false } register("SX-Item") { required = false }
register("Slimefun") { required = false } register("Slimefun") { required = false }
register("Nexo") { required = false }
// leveler // leveler
register("AuraSkills") { required = false } register("AuraSkills") { required = false }
@@ -126,11 +130,12 @@ paper {
register("PreciousStones") { required = false } register("PreciousStones") { required = false }
register("hClaims") { required = false } register("hClaims") { required = false }
register("Factions") { required = false } register("Factions") { required = false }
register("NoBuildPlus") { required = false }
} }
} }
artifacts { artifacts {
archives(tasks.shadowJar) implementation(tasks.shadowJar)
} }
tasks { tasks {

View File

@@ -17,7 +17,7 @@ import java.lang.reflect.Method;
import java.util.Objects; import java.util.Objects;
@SuppressWarnings("UnstableApiUsage") @SuppressWarnings("UnstableApiUsage")
public class PaperCraftEngineBootstrap implements PluginBootstrap { public final class PaperCraftEngineBootstrap implements PluginBootstrap {
private static final Class<?> clazz$PluginProviderContext = PluginProviderContext.class; private static final Class<?> clazz$PluginProviderContext = PluginProviderContext.class;
private static final Class<?> clazz$ComponentLogger = Objects.requireNonNull( private static final Class<?> clazz$ComponentLogger = Objects.requireNonNull(
ReflectionUtils.getClazz( ReflectionUtils.getClazz(
@@ -29,7 +29,8 @@ public class PaperCraftEngineBootstrap implements PluginBootstrap {
clazz$PluginProviderContext, clazz$ComponentLogger, new String[] { "getLogger" } clazz$PluginProviderContext, clazz$ComponentLogger, new String[] { "getLogger" }
) )
); );
protected BukkitCraftEngine plugin;
BukkitCraftEngine plugin;
@Override @Override
public void bootstrap(@NotNull BootstrapContext context) { public void bootstrap(@NotNull BootstrapContext context) {

View File

@@ -2,7 +2,7 @@ package net.momirealms.craftengine.bukkit.plugin;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
public class PaperCraftEnginePlugin extends JavaPlugin { public final class PaperCraftEnginePlugin extends JavaPlugin {
private final PaperCraftEngineBootstrap bootstrap; private final PaperCraftEngineBootstrap bootstrap;
public PaperCraftEnginePlugin(PaperCraftEngineBootstrap bootstrap) { public PaperCraftEnginePlugin(PaperCraftEngineBootstrap bootstrap) {

View File

@@ -22,7 +22,7 @@ import net.momirealms.craftengine.core.util.VersionHelper;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.*; import java.util.*;
public class BukkitAdvancementManager extends AbstractAdvancementManager { public final class BukkitAdvancementManager extends AbstractAdvancementManager {
private final BukkitCraftEngine plugin; private final BukkitCraftEngine plugin;
private final AdvancementParser advancementParser; private final AdvancementParser advancementParser;
private final Map<Key, JsonElement> advancements = new HashMap<>(); private final Map<Key, JsonElement> advancements = new HashMap<>();

View File

@@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.api;
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture; 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.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;
@@ -18,6 +19,7 @@ import net.momirealms.craftengine.core.plugin.context.parameter.DirectContextPar
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 net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.sparrow.nbt.CompoundTag;
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;
@@ -159,8 +161,7 @@ public final class CraftEngineFurniture {
* @return is seat or not * @return is seat or not
*/ */
public static boolean isSeat(@NotNull Entity entity) { public static boolean isSeat(@NotNull Entity entity) {
Integer baseEntityId = entity.getPersistentDataContainer().get(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER); return entity.getPersistentDataContainer().has(BukkitSeatManager.SEAT_KEY);
return baseEntityId != null;
} }
/** /**
@@ -182,9 +183,12 @@ public final class CraftEngineFurniture {
*/ */
@Nullable @Nullable
public static BukkitFurniture getLoadedFurnitureBySeat(@NotNull Entity seat) { public static BukkitFurniture getLoadedFurnitureBySeat(@NotNull Entity seat) {
Integer baseEntityId = seat.getPersistentDataContainer().get(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER); if (isSeat(seat)) {
if (baseEntityId == null) return null; CompoundTag seatExtraData = BukkitSeatManager.instance().getSeatExtraData(seat);
return BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(baseEntityId); int entityId = seatExtraData.getInt("entity_id");
BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entityId);
}
return null;
} }
/** /**

View File

@@ -21,7 +21,7 @@ import java.nio.file.Path;
* if you need the cache to recognize updates. * if you need the cache to recognize updates.
* </p> * </p>
*/ */
public class AsyncResourcePackCacheEvent extends Event { public final class AsyncResourcePackCacheEvent extends Event {
private static final HandlerList HANDLER_LIST = new HandlerList(); private static final HandlerList HANDLER_LIST = new HandlerList();
private final PackCacheData cacheData; private final PackCacheData cacheData;

View File

@@ -6,7 +6,7 @@ import org.jetbrains.annotations.NotNull;
import java.nio.file.Path; import java.nio.file.Path;
public class AsyncResourcePackGenerateEvent extends Event { public final class AsyncResourcePackGenerateEvent extends Event {
private static final HandlerList HANDLER_LIST = new HandlerList(); private static final HandlerList HANDLER_LIST = new HandlerList();
private final Path generatedPackPath; private final Path generatedPackPath;
private final Path zipFilePath; private final Path zipFilePath;

View File

@@ -5,7 +5,7 @@ import org.bukkit.event.Event;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class CraftEngineReloadEvent extends Event { public final class CraftEngineReloadEvent extends Event {
private static final HandlerList HANDLER_LIST = new HandlerList(); private static final HandlerList HANDLER_LIST = new HandlerList();
private final BukkitCraftEngine plugin; private final BukkitCraftEngine plugin;
private static boolean firstFlag = true; private static boolean firstFlag = true;

View File

@@ -12,7 +12,7 @@ import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent; import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class CustomBlockAttemptPlaceEvent extends PlayerEvent implements Cancellable { public final class CustomBlockAttemptPlaceEvent extends PlayerEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList(); private static final HandlerList HANDLER_LIST = new HandlerList();
private boolean cancelled; private boolean cancelled;
private final CustomBlock customBlock; private final CustomBlock customBlock;

View File

@@ -10,7 +10,7 @@ import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent; import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class CustomBlockBreakEvent extends PlayerEvent implements Cancellable { public final class CustomBlockBreakEvent extends PlayerEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList(); private static final HandlerList HANDLER_LIST = new HandlerList();
private boolean cancelled; private boolean cancelled;
private final CustomBlock customBlock; private final CustomBlock customBlock;

View File

@@ -15,7 +15,7 @@ import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public class CustomBlockInteractEvent extends PlayerEvent implements Cancellable { public final class CustomBlockInteractEvent extends PlayerEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList(); private static final HandlerList HANDLER_LIST = new HandlerList();
private boolean cancelled; private boolean cancelled;
private final CustomBlock customBlock; private final CustomBlock customBlock;

View File

@@ -11,7 +11,7 @@ import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent; import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class CustomBlockPlaceEvent extends PlayerEvent implements Cancellable { public final class CustomBlockPlaceEvent extends PlayerEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList(); private static final HandlerList HANDLER_LIST = new HandlerList();
private final CustomBlock customBlock; private final CustomBlock customBlock;
private final ImmutableBlockState state; private final ImmutableBlockState state;

View File

@@ -8,7 +8,7 @@ import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent; import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class FurnitureAttemptBreakEvent extends PlayerEvent implements Cancellable { public final class FurnitureAttemptBreakEvent extends PlayerEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList(); private static final HandlerList HANDLER_LIST = new HandlerList();
private boolean cancelled; private boolean cancelled;
private final BukkitFurniture furniture; private final BukkitFurniture furniture;

View File

@@ -12,7 +12,7 @@ import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent; import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class FurnitureAttemptPlaceEvent extends PlayerEvent implements Cancellable { public final class FurnitureAttemptPlaceEvent extends PlayerEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList(); private static final HandlerList HANDLER_LIST = new HandlerList();
private boolean cancelled; private boolean cancelled;
private final CustomFurniture furniture; private final CustomFurniture furniture;

View File

@@ -8,7 +8,7 @@ import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent; import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class FurnitureBreakEvent extends PlayerEvent implements Cancellable { public final class FurnitureBreakEvent extends PlayerEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList(); private static final HandlerList HANDLER_LIST = new HandlerList();
private boolean cancelled; private boolean cancelled;
private final BukkitFurniture furniture; private final BukkitFurniture furniture;

View File

@@ -1,6 +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.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;
@@ -9,21 +10,29 @@ import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent; import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class FurnitureInteractEvent extends PlayerEvent implements Cancellable { public final class FurnitureInteractEvent extends PlayerEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList(); private static final HandlerList HANDLER_LIST = new HandlerList();
private boolean cancelled; private boolean cancelled;
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;
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) {
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;
}
@NotNull
public HitBox hitBox() {
return hitBox;
} }
@NotNull @NotNull

View File

@@ -9,7 +9,7 @@ import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent; import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class FurniturePlaceEvent extends PlayerEvent implements Cancellable { public final class FurniturePlaceEvent extends PlayerEvent implements Cancellable {
private static final HandlerList HANDLER_LIST = new HandlerList(); private static final HandlerList HANDLER_LIST = new HandlerList();
private final Location location; private final Location location;
private final BukkitFurniture furniture; private final BukkitFurniture furniture;

View File

@@ -6,6 +6,9 @@ import net.momirealms.craftengine.bukkit.block.behavior.UnsafeCompositeBlockBeha
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.injector.BlockGenerator; import net.momirealms.craftengine.bukkit.plugin.injector.BlockGenerator;
import net.momirealms.craftengine.bukkit.plugin.network.BukkitNetworkManager;
import net.momirealms.craftengine.bukkit.plugin.network.payload.PayloadHelper;
import net.momirealms.craftengine.bukkit.plugin.network.payload.protocol.VisualBlockStatePacket;
import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.*; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.*;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
@@ -18,15 +21,17 @@ import net.momirealms.craftengine.core.block.parser.BlockStateParser;
import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.loot.LootTable;
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.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.Context;
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.function.Function; import net.momirealms.craftengine.core.plugin.context.function.Function;
import net.momirealms.craftengine.core.plugin.logger.Debugger; import net.momirealms.craftengine.core.plugin.logger.Debugger;
import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.Holder;
import net.momirealms.craftengine.core.sound.SoundData; import net.momirealms.craftengine.core.sound.SoundData;
import net.momirealms.craftengine.core.sound.SoundSet; import net.momirealms.craftengine.core.sound.SoundSet;
import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.world.chunk.PalettedContainer; import net.momirealms.craftengine.core.util.ObjectHolder;
import net.momirealms.craftengine.core.util.Tristate;
import net.momirealms.craftengine.core.util.VersionHelper;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
@@ -61,6 +66,8 @@ public final class BukkitBlockManager extends AbstractBlockManager {
private Set<Object> missingHitSounds = Set.of(); private Set<Object> missingHitSounds = Set.of();
private Set<Object> missingStepSounds = Set.of(); private Set<Object> missingStepSounds = Set.of();
private Set<Key> missingInteractSoundBlocks = Set.of(); private Set<Key> missingInteractSoundBlocks = Set.of();
// 缓存的VisualBlockStatePacket
private VisualBlockStatePacket cachedVisualBlockStatePacket;
public BukkitBlockManager(BukkitCraftEngine plugin) { public BukkitBlockManager(BukkitCraftEngine plugin) {
super(plugin, RegistryUtils.currentBlockRegistrySize(), Config.serverSideBlocks()); super(plugin, RegistryUtils.currentBlockRegistrySize(), Config.serverSideBlocks());
@@ -120,6 +127,11 @@ public final class BukkitBlockManager extends AbstractBlockManager {
public void delayedLoad() { public void delayedLoad() {
this.plugin.networkManager().registerBlockStatePacketListeners(this.blockStateMappings); // 重置方块映射表 this.plugin.networkManager().registerBlockStatePacketListeners(this.blockStateMappings); // 重置方块映射表
super.delayedLoad(); super.delayedLoad();
this.cachedVisualBlockStatePacket = VisualBlockStatePacket.create();
for (BukkitServerPlayer player : BukkitNetworkManager.instance().onlineUsers()) {
if (!player.clientModEnabled()) continue;
PayloadHelper.sendData(player, this.cachedVisualBlockStatePacket);
}
} }
@Override @Override
@@ -322,9 +334,6 @@ public final class BukkitBlockManager extends AbstractBlockManager {
// 注册服务端侧的真实方块 // 注册服务端侧的真实方块
private void registerServerSideCustomBlocks(int count) { private void registerServerSideCustomBlocks(int count) {
// 这个会影响全局调色盘 // 这个会影响全局调色盘
if (MiscUtils.ceilLog2(this.vanillaBlockStateCount + count) == MiscUtils.ceilLog2(this.vanillaBlockStateCount)) {
PalettedContainer.NEED_DOWNGRADE = false;
}
try { try {
unfreezeRegistry(); unfreezeRegistry();
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
@@ -359,6 +368,10 @@ public final class BukkitBlockManager extends AbstractBlockManager {
return this.cachedUpdateTagsPacket; return this.cachedUpdateTagsPacket;
} }
public VisualBlockStatePacket cachedVisualBlockStatePacket() {
return this.cachedVisualBlockStatePacket;
}
private void markVanillaNoteBlocks() { private void markVanillaNoteBlocks() {
try { try {
Object block = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, KeyUtils.toResourceLocation(BlockKeys.NOTE_BLOCK)); Object block = FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.BLOCK, KeyUtils.toResourceLocation(BlockKeys.NOTE_BLOCK));
@@ -418,8 +431,26 @@ public final class BukkitBlockManager extends AbstractBlockManager {
private void deceiveBukkitRegistry() { private void deceiveBukkitRegistry() {
try { try {
Map<Object, Material> magicMap = (Map<Object, Material>) CraftBukkitReflections.field$CraftMagicNumbers$BLOCK_MATERIAL.get(null); Map<Object, Material> magicMap = (Map<Object, Material>) CraftBukkitReflections.field$CraftMagicNumbers$BLOCK_MATERIAL.get(null);
for (DelegatingBlock customBlock : this.customBlocks) { Set<String> invalid = new HashSet<>();
magicMap.put(customBlock, Material.STONE); for (int i = 0; i < this.customBlocks.length; i++) {
DelegatingBlock customBlock = this.customBlocks[i];
String value = Config.deceiveBukkitMaterial(i).value();
Material material;
try {
material = Material.valueOf(value.toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException e) {
if (invalid.add(value)) {
this.plugin.logger().warn("Cannot load 'deceive-bukkit-material'. '" + value + "' is an invalid bukkit material", e);
}
material = Material.BRICKS;
}
if (!material.isBlock()) {
if (invalid.add(value)) {
this.plugin.logger().warn("Cannot load 'deceive-bukkit-material'. '" + value + "' is an invalid bukkit block material");
}
material = Material.BRICKS;
}
magicMap.put(customBlock, material);
} }
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
this.plugin.logger().warn("Failed to deceive bukkit magic blocks", e); this.plugin.logger().warn("Failed to deceive bukkit magic blocks", e);
@@ -510,7 +541,7 @@ public final class BukkitBlockManager extends AbstractBlockManager {
@Override @Override
protected CustomBlock createCustomBlock(@NotNull Holder.Reference<CustomBlock> holder, protected CustomBlock createCustomBlock(@NotNull Holder.Reference<CustomBlock> holder,
@NotNull BlockStateVariantProvider variantProvider, @NotNull BlockStateVariantProvider variantProvider,
@NotNull Map<EventTrigger, List<Function<PlayerOptionalContext>>> events, @NotNull Map<EventTrigger, List<Function<Context>>> events,
@Nullable LootTable<?> lootTable) { @Nullable LootTable<?> lootTable) {
return new BukkitCustomBlock(holder, variantProvider, events, lootTable); return new BukkitCustomBlock(holder, variantProvider, events, lootTable);
} }

View File

@@ -4,7 +4,7 @@ import net.momirealms.craftengine.core.block.AbstractCustomBlock;
import net.momirealms.craftengine.core.block.BlockStateVariantProvider; import net.momirealms.craftengine.core.block.BlockStateVariantProvider;
import net.momirealms.craftengine.core.block.CustomBlock; import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.loot.LootTable;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.Context;
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.function.Function; import net.momirealms.craftengine.core.plugin.context.function.Function;
import net.momirealms.craftengine.core.registry.Holder; import net.momirealms.craftengine.core.registry.Holder;
@@ -19,7 +19,7 @@ public final class BukkitCustomBlock extends AbstractCustomBlock {
public BukkitCustomBlock( public BukkitCustomBlock(
@NotNull Holder.Reference<CustomBlock> holder, @NotNull Holder.Reference<CustomBlock> holder,
@NotNull BlockStateVariantProvider variantProvider, @NotNull BlockStateVariantProvider variantProvider,
@NotNull Map<EventTrigger, List<Function<PlayerOptionalContext>>> events, @NotNull Map<EventTrigger, List<Function<Context>>> events,
@Nullable LootTable<?> lootTable @Nullable LootTable<?> lootTable
) { ) {
super(holder, variantProvider, events, lootTable); super(holder, variantProvider, events, lootTable);

View File

@@ -3,18 +3,31 @@ package net.momirealms.craftengine.bukkit.block;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.core.block.AbstractBlockStateWrapper; import net.momirealms.craftengine.core.block.AbstractBlockStateWrapper;
import net.momirealms.craftengine.core.block.BlockStateWrapper; import net.momirealms.craftengine.core.block.BlockStateWrapper;
import net.momirealms.craftengine.core.block.CustomBlockStateWrapper;
import net.momirealms.craftengine.core.block.ImmutableBlockState; import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.properties.Property; import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.Key;
import java.util.Collection;
import java.util.List;
import java.util.Optional; import java.util.Optional;
public class BukkitCustomBlockStateWrapper extends AbstractBlockStateWrapper { public class BukkitCustomBlockStateWrapper extends AbstractBlockStateWrapper implements CustomBlockStateWrapper {
public BukkitCustomBlockStateWrapper(Object blockState, int registryId) { public BukkitCustomBlockStateWrapper(Object blockState, int registryId) {
super(blockState, registryId); super(blockState, registryId);
} }
@Override
public BlockStateWrapper visualBlockState() {
return getImmutableBlockState().map(ImmutableBlockState::vanillaBlockState).orElse(null);
}
@Override
public boolean isCustom() {
return true;
}
@Override @Override
public Key ownerId() { public Key ownerId() {
return getImmutableBlockState().map(state -> state.owner().value().id()).orElseGet(() -> BlockStateUtils.getBlockOwnerIdFromState(super.blockState)); return getImmutableBlockState().map(state -> state.owner().value().id()).orElseGet(() -> BlockStateUtils.getBlockOwnerIdFromState(super.blockState));
@@ -51,6 +64,12 @@ public class BukkitCustomBlockStateWrapper extends AbstractBlockStateWrapper {
return getImmutableBlockState().map(state -> state.owner().value().getProperty(propertyName) != null).orElse(false); return getImmutableBlockState().map(state -> state.owner().value().getProperty(propertyName) != null).orElse(false);
} }
@Override
public Collection<String> getPropertyNames() {
Optional<ImmutableBlockState> immutableBlockState = getImmutableBlockState();
return immutableBlockState.<Collection<String>>map(state -> state.getProperties().stream().map(Property::name).toList()).orElseGet(List::of);
}
@Override @Override
public String getAsString() { public String getAsString() {
return getImmutableBlockState().map(ImmutableBlockState::toString).orElseGet(() -> BlockStateUtils.fromBlockData(super.blockState).getAsString()); return getImmutableBlockState().map(ImmutableBlockState::toString).orElseGet(() -> BlockStateUtils.fromBlockData(super.blockState).getAsString());

View File

@@ -8,6 +8,8 @@ import net.momirealms.craftengine.core.block.BlockStateWrapper;
import net.momirealms.craftengine.core.block.StatePropertyAccessor; import net.momirealms.craftengine.core.block.StatePropertyAccessor;
import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.Key;
import java.util.Collection;
public class BukkitVanillaBlockStateWrapper extends AbstractBlockStateWrapper { public class BukkitVanillaBlockStateWrapper extends AbstractBlockStateWrapper {
private final StatePropertyAccessor accessor; private final StatePropertyAccessor accessor;
@@ -16,6 +18,11 @@ public class BukkitVanillaBlockStateWrapper extends AbstractBlockStateWrapper {
this.accessor = FastNMS.INSTANCE.createStatePropertyAccessor(blockState); this.accessor = FastNMS.INSTANCE.createStatePropertyAccessor(blockState);
} }
@Override
public boolean isCustom() {
return false;
}
@Override @Override
public Key ownerId() { public Key ownerId() {
return BlockStateUtils.getBlockOwnerIdFromState(super.blockState); return BlockStateUtils.getBlockOwnerIdFromState(super.blockState);
@@ -31,6 +38,11 @@ public class BukkitVanillaBlockStateWrapper extends AbstractBlockStateWrapper {
return this.accessor.hasProperty(propertyName); return this.accessor.hasProperty(propertyName);
} }
@Override
public Collection<String> getPropertyNames() {
return this.accessor.getPropertyNames();
}
@Override @Override
public String getAsString() { public String getAsString() {
return BlockStateUtils.fromBlockData(super.blockState).getAsString(); return BlockStateUtils.fromBlockData(super.blockState).getAsString();

View File

@@ -43,6 +43,7 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
public static final Key ATTACHED_STEM_BLOCK = Key.from("craftengine:attached_stem_block"); public static final Key ATTACHED_STEM_BLOCK = Key.from("craftengine:attached_stem_block");
public static final Key CHIME_BLOCK = Key.from("craftengine:chime_block"); public static final Key CHIME_BLOCK = Key.from("craftengine:chime_block");
public static final Key BUDDING_BLOCK = Key.from("craftengine:budding_block"); public static final Key BUDDING_BLOCK = Key.from("craftengine:budding_block");
public static final Key SEAT_BLOCK = Key.from("craftengine:seat_block");
public static void init() { public static void init() {
register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE); register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE);
@@ -84,5 +85,6 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
register(ATTACHED_STEM_BLOCK, AttachedStemBlockBehavior.FACTORY); register(ATTACHED_STEM_BLOCK, AttachedStemBlockBehavior.FACTORY);
register(CHIME_BLOCK, ChimeBlockBehavior.FACTORY); register(CHIME_BLOCK, ChimeBlockBehavior.FACTORY);
register(BUDDING_BLOCK, BuddingBlockBehavior.FACTORY); register(BUDDING_BLOCK, BuddingBlockBehavior.FACTORY);
register(SEAT_BLOCK, SeatBlockBehavior.FACTORY);
} }
} }

View File

@@ -75,11 +75,11 @@ public class DirectionalAttachedBlockBehavior extends BukkitBlockBehavior {
if (behavior == null) return false; if (behavior == null) return false;
Direction direction; Direction direction;
if (isSixDirection) { if (isSixDirection) {
direction = ((Direction) state.get(behavior.facingProperty)).opposite(); direction = (Direction) state.get(behavior.facingProperty);
} else { } else {
direction = ((HorizontalDirection) state.get(behavior.facingProperty)).opposite().toDirection(); direction = ((HorizontalDirection) state.get(behavior.facingProperty)).toDirection();
} }
BlockPos blockPos = LocationUtils.fromBlockPos(args[2]).relative(direction); BlockPos blockPos = LocationUtils.fromBlockPos(args[2]).relative(direction.opposite());
Object nmsPos = LocationUtils.toBlockPos(blockPos); Object nmsPos = LocationUtils.toBlockPos(blockPos);
Object nmsState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(args[1], nmsPos); Object nmsState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(args[1], nmsPos);
return FastNMS.INSTANCE.method$BlockStateBase$isFaceSturdy(nmsState, args[1], nmsPos, DirectionUtils.toNMSDirection(direction), CoreReflections.instance$SupportType$FULL) return FastNMS.INSTANCE.method$BlockStateBase$isFaceSturdy(nmsState, args[1], nmsPos, DirectionUtils.toNMSDirection(direction), CoreReflections.instance$SupportType$FULL)

View File

@@ -124,7 +124,7 @@ public class FallingBlockBehavior extends BukkitBlockBehavior {
SoundData fallSound = null; SoundData fallSound = null;
SoundData destroySound = null; SoundData destroySound = null;
if (sounds != null) { if (sounds != null) {
fallSound = Optional.ofNullable(sounds.get("fall")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null); fallSound = Optional.ofNullable(sounds.get("land")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null);
destroySound = Optional.ofNullable(sounds.get("destroy")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null); destroySound = Optional.ofNullable(sounds.get("destroy")).map(obj -> SoundData.create(obj, SoundData.SoundValue.FIXED_1, SoundData.SoundValue.ranged(0.9f, 1f))).orElse(null);
} }
return new FallingBlockBehavior(block, hurtAmount, hurtMax, fallSound, destroySound); return new FallingBlockBehavior(block, hurtAmount, hurtMax, fallSound, destroySound);

View File

@@ -0,0 +1,80 @@
package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.block.entity.BukkitBlockEntityTypes;
import net.momirealms.craftengine.bukkit.block.entity.SeatBlockEntity;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
import net.momirealms.craftengine.core.block.BlockBehavior;
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.block.behavior.EntityBlockBehavior;
import net.momirealms.craftengine.core.block.entity.BlockEntity;
import net.momirealms.craftengine.core.block.entity.BlockEntityType;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.entity.player.InteractionResult;
import net.momirealms.craftengine.core.entity.seat.SeatConfig;
import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.util.HorizontalDirection;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.CEWorld;
import java.util.Map;
public class SeatBlockBehavior extends BukkitBlockBehavior implements EntityBlockBehavior {
public static final Factory FACTORY = new Factory();
private final Property<HorizontalDirection> directionProperty;
private final SeatConfig[] seats;
public SeatBlockBehavior(CustomBlock customBlock, Property<HorizontalDirection> directionProperty, SeatConfig[] seats) {
super(customBlock);
this.seats = seats;
this.directionProperty = directionProperty;
}
@Override
public BlockEntity createBlockEntity(BlockPos pos, ImmutableBlockState state) {
return new SeatBlockEntity(pos, state, this.seats);
}
@Override
public <T extends BlockEntity> BlockEntityType<T> blockEntityType(ImmutableBlockState state) {
return EntityBlockBehavior.blockEntityTypeHelper(BukkitBlockEntityTypes.SEAT);
}
public Property<HorizontalDirection> directionProperty() {
return this.directionProperty;
}
@Override
public InteractionResult useWithoutItem(UseOnContext context, ImmutableBlockState state) {
BukkitServerPlayer player = (BukkitServerPlayer) context.getPlayer();
if (player == null || player.isSecondaryUseActive()) {
return InteractionResult.PASS;
}
player.swingHand(context.getHand());
CEWorld world = context.getLevel().storageWorld();
BlockEntity blockEntity = world.getBlockEntityAtIfLoaded(context.getClickedPos());
if (!(blockEntity instanceof SeatBlockEntity seatBlockEntity)) {
return InteractionResult.PASS;
}
if (seatBlockEntity.spawnSeat(player)) {
return InteractionResult.SUCCESS_AND_CANCEL;
} else {
return InteractionResult.PASS;
}
}
public static class Factory implements BlockBehaviorFactory {
@SuppressWarnings("unchecked")
@Override
public BlockBehavior create(CustomBlock block, Map<String, Object> arguments) {
Property<HorizontalDirection> directionProperty = null;
Property<?> facing = block.getProperty("facing");
if (facing != null && facing.valueClass() == HorizontalDirection.class) {
directionProperty = (Property<HorizontalDirection>) facing;
}
return new SeatBlockBehavior(block, directionProperty, SeatConfig.fromObj(arguments.get("seats")));
}
}
}

View File

@@ -38,7 +38,7 @@ public class SimpleParticleBlockBehavior extends BukkitBlockBehavior implements
} }
@Override @Override
public <T extends BlockEntity> BlockEntityType<T> blockEntityType() { public <T extends BlockEntity> BlockEntityType<T> blockEntityType(ImmutableBlockState state) {
return EntityBlockBehavior.blockEntityTypeHelper(BukkitBlockEntityTypes.SIMPLE_PARTICLE); return EntityBlockBehavior.blockEntityTypeHelper(BukkitBlockEntityTypes.SIMPLE_PARTICLE);
} }

View File

@@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.block.entity.BukkitBlockEntityTypes; import net.momirealms.craftengine.bukkit.block.entity.BukkitBlockEntityTypes;
import net.momirealms.craftengine.bukkit.block.entity.SimpleStorageBlockEntity; import net.momirealms.craftengine.bukkit.block.entity.SimpleStorageBlockEntity;
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.gui.BukkitInventory; import net.momirealms.craftengine.bukkit.plugin.gui.BukkitInventory;
import net.momirealms.craftengine.bukkit.util.BlockStateUtils; import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils; import net.momirealms.craftengine.bukkit.util.LocationUtils;
@@ -24,6 +25,7 @@ import net.momirealms.craftengine.core.util.MiscUtils;
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.CEWorld; import net.momirealms.craftengine.core.world.CEWorld;
import org.bukkit.Location;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
@@ -71,15 +73,23 @@ public class SimpleStorageBlockBehavior extends BukkitBlockBehavior implements E
public InteractionResult useWithoutItem(UseOnContext context, ImmutableBlockState state) { public InteractionResult useWithoutItem(UseOnContext context, ImmutableBlockState state) {
CEWorld world = context.getLevel().storageWorld(); CEWorld world = context.getLevel().storageWorld();
net.momirealms.craftengine.core.entity.player.Player player = context.getPlayer(); net.momirealms.craftengine.core.entity.player.Player player = context.getPlayer();
BlockEntity blockEntity = world.getBlockEntityAtIfLoaded(context.getClickedPos()); if (player == null) {
if (player != null && blockEntity instanceof SimpleStorageBlockEntity entity) { return InteractionResult.SUCCESS_AND_CANCEL;
Player bukkitPlayer = (Player) player.platformPlayer();
Optional.ofNullable(entity.inventory()).ifPresent(inventory -> {
entity.onPlayerOpen(player);
bukkitPlayer.openInventory(inventory);
new BukkitInventory(inventory).open(player, AdventureHelper.miniMessage().deserialize(this.containerTitle, PlayerOptionalContext.of(player).tagResolvers()));
});
} }
BlockPos blockPos = context.getClickedPos();
World bukkitWorld = (World) context.getLevel().platformWorld();
Location location = new Location(bukkitWorld, blockPos.x(), blockPos.y(), blockPos.z());
Player bukkitPlayer = (Player) player.platformPlayer();
if (!BukkitCraftEngine.instance().antiGriefProvider().canOpenContainer(bukkitPlayer, location)) {
return InteractionResult.SUCCESS_AND_CANCEL;
}
BlockEntity blockEntity = world.getBlockEntityAtIfLoaded(blockPos);
if (!(blockEntity instanceof SimpleStorageBlockEntity entity) || entity.inventory() == null) {
return InteractionResult.SUCCESS_AND_CANCEL;
}
entity.onPlayerOpen(player);
bukkitPlayer.openInventory(entity.inventory());
new BukkitInventory(entity.inventory()).open(player, AdventureHelper.miniMessage().deserialize(this.containerTitle, PlayerOptionalContext.of(player).tagResolvers()));
return InteractionResult.SUCCESS_AND_CANCEL; return InteractionResult.SUCCESS_AND_CANCEL;
} }
@@ -106,7 +116,7 @@ public class SimpleStorageBlockBehavior extends BukkitBlockBehavior implements E
} }
@Override @Override
public <T extends BlockEntity> BlockEntityType<T> blockEntityType() { public <T extends BlockEntity> BlockEntityType<T> blockEntityType(ImmutableBlockState state) {
return EntityBlockBehavior.blockEntityTypeHelper(BukkitBlockEntityTypes.SIMPLE_STORAGE); return EntityBlockBehavior.blockEntityTypeHelper(BukkitBlockEntityTypes.SIMPLE_STORAGE);
} }

View File

@@ -1,6 +1,5 @@
package net.momirealms.craftengine.bukkit.block.behavior; package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.core.block.BlockBehavior;
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.behavior.AbstractBlockBehavior; import net.momirealms.craftengine.core.block.behavior.AbstractBlockBehavior;
@@ -47,7 +46,7 @@ public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public <T extends BlockBehavior> Optional<T> getAs(Class<T> tClass) { public <T> Optional<T> getAs(Class<T> tClass) {
for (AbstractBlockBehavior behavior : this.behaviors) { for (AbstractBlockBehavior behavior : this.behaviors) {
if (tClass.isInstance(behavior)) { if (tClass.isInstance(behavior)) {
return Optional.of((T) behavior); return Optional.of((T) behavior);
@@ -74,13 +73,18 @@ public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior
@Override @Override
public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) { public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) {
boolean hasPass = false;
for (AbstractBlockBehavior behavior : this.behaviors) { for (AbstractBlockBehavior behavior : this.behaviors) {
InteractionResult result = behavior.useOnBlock(context, state); InteractionResult result = behavior.useOnBlock(context, state);
if (result != InteractionResult.PASS && result != InteractionResult.TRY_EMPTY_HAND) { if (result == InteractionResult.PASS) {
hasPass = true;
continue;
}
if (result != InteractionResult.TRY_EMPTY_HAND) {
return result; return result;
} }
} }
return super.useOnBlock(context, state); return hasPass ? InteractionResult.PASS : super.useOnBlock(context, state);
} }
@Override @Override

View File

@@ -51,7 +51,7 @@ public class VerticalCropBlockBehavior extends BukkitBlockBehavior {
if (FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, (this.direction ? LocationUtils.above(blockPos) : LocationUtils.below(blockPos))) == MBlocks.AIR$defaultState) { if (FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, (this.direction ? LocationUtils.above(blockPos) : LocationUtils.below(blockPos))) == MBlocks.AIR$defaultState) {
int currentHeight = 1; int currentHeight = 1;
BlockPos currentPos = LocationUtils.fromBlockPos(blockPos); BlockPos currentPos = LocationUtils.fromBlockPos(blockPos);
while (true) { for (;;) {
Object nextPos = LocationUtils.toBlockPos(currentPos.x(), this.direction ? currentPos.y() - currentHeight : currentPos.y() + currentHeight, currentPos.z()); Object nextPos = LocationUtils.toBlockPos(currentPos.x(), this.direction ? currentPos.y() - currentHeight : currentPos.y() + currentHeight, currentPos.z());
Object nextState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, nextPos); Object nextState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, nextPos);
Optional<ImmutableBlockState> optionalBelowCustomState = BlockStateUtils.getOptionalCustomBlockState(nextState); Optional<ImmutableBlockState> optionalBelowCustomState = BlockStateUtils.getOptionalCustomBlockState(nextState);

View File

@@ -47,7 +47,7 @@ public class WallTorchParticleBlockBehavior extends BukkitBlockBehavior implemen
} }
@Override @Override
public <T extends BlockEntity> BlockEntityType<T> blockEntityType() { public <T extends BlockEntity> BlockEntityType<T> blockEntityType(ImmutableBlockState state) {
return EntityBlockBehavior.blockEntityTypeHelper(BukkitBlockEntityTypes.WALL_TORCH_PARTICLE); return EntityBlockBehavior.blockEntityTypeHelper(BukkitBlockEntityTypes.WALL_TORCH_PARTICLE);
} }

View File

@@ -5,7 +5,10 @@ import net.momirealms.craftengine.core.block.entity.BlockEntityTypeKeys;
import net.momirealms.craftengine.core.block.entity.BlockEntityTypes; import net.momirealms.craftengine.core.block.entity.BlockEntityTypes;
public class BukkitBlockEntityTypes extends BlockEntityTypes { public class BukkitBlockEntityTypes extends BlockEntityTypes {
public static final BlockEntityType<SimpleStorageBlockEntity> SIMPLE_STORAGE = register(BlockEntityTypeKeys.SIMPLE_STORAGE, SimpleStorageBlockEntity::new); public static final BlockEntityType<SimpleStorageBlockEntity> SIMPLE_STORAGE = register(BlockEntityTypeKeys.SIMPLE_STORAGE);
public static final BlockEntityType<SimpleParticleBlockEntity> SIMPLE_PARTICLE = register(BlockEntityTypeKeys.SIMPLE_PARTICLE, SimpleParticleBlockEntity::new); public static final BlockEntityType<SimpleParticleBlockEntity> SIMPLE_PARTICLE = register(BlockEntityTypeKeys.SIMPLE_PARTICLE);
public static final BlockEntityType<WallTorchParticleBlockEntity> WALL_TORCH_PARTICLE = register(BlockEntityTypeKeys.WALL_TORCH_PARTICLE, WallTorchParticleBlockEntity::new); public static final BlockEntityType<WallTorchParticleBlockEntity> WALL_TORCH_PARTICLE = register(BlockEntityTypeKeys.WALL_TORCH_PARTICLE);
public static final BlockEntityType<SeatBlockEntity> SEAT = register(BlockEntityTypeKeys.SEAT);
private BukkitBlockEntityTypes() {}
} }

View File

@@ -0,0 +1,69 @@
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.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.entity.BlockEntity;
import net.momirealms.craftengine.core.block.properties.Property;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.entity.seat.Seat;
import net.momirealms.craftengine.core.entity.seat.SeatConfig;
import net.momirealms.craftengine.core.entity.seat.SeatOwner;
import net.momirealms.craftengine.core.util.HorizontalDirection;
import net.momirealms.craftengine.core.world.BlockPos;
import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.sparrow.nbt.CompoundTag;
import java.util.Optional;
public class SeatBlockEntity extends BlockEntity implements SeatOwner {
private final Seat<SeatBlockEntity>[] seats;
@SuppressWarnings("unchecked")
public SeatBlockEntity(BlockPos pos, ImmutableBlockState blockState, SeatConfig[] seats) {
super(BukkitBlockEntityTypes.SEAT, pos, blockState);
this.seats = new Seat[seats.length];
for (int i = 0; i < seats.length; i++) {
this.seats[i] = new BukkitSeat<>(this, seats[i]);
}
}
@Override
public void saveCustomData(CompoundTag data) {
data.putString("type", "seat_block_entity");
}
@Override
public void preRemove() {
for (Seat<SeatBlockEntity> seat : this.seats) {
seat.destroy();
}
}
public boolean spawnSeat(Player player) {
Optional<SeatBlockBehavior> seatBehavior = super.blockState.behavior().getAs(SeatBlockBehavior.class);
if (seatBehavior.isEmpty()) {
return false;
}
float yRot = 0;
Property<HorizontalDirection> directionProperty = seatBehavior.get().directionProperty();
if (directionProperty != null) {
HorizontalDirection direction = super.blockState.get(directionProperty);
if (direction == HorizontalDirection.NORTH) {
yRot = 180;
} else if (direction == HorizontalDirection.EAST) {
yRot = -90;
} else if (direction == HorizontalDirection.WEST) {
yRot = 90;
}
}
for (Seat<SeatBlockEntity> seat : this.seats) {
if (!seat.isOccupied()) {
if (seat.spawnSeat(player, new WorldPosition(super.world.world(), super.pos.x() + 0.5, super.pos.y(), super.pos.z() + 0.5, 0, 180 - yRot))) {
return true;
}
}
}
return false;
}
}

View File

@@ -85,7 +85,7 @@ public class SimpleStorageBlockEntity extends BlockEntity {
} }
if (VersionHelper.isOrAbove1_20_5()) { if (VersionHelper.isOrAbove1_20_5()) {
CoreReflections.instance$ItemStack$CODEC.parse(MRegistryOps.SPARROW_NBT, itemTag) CoreReflections.instance$ItemStack$CODEC.parse(MRegistryOps.SPARROW_NBT, itemTag)
.resultOrPartial((s) -> CraftEngine.instance().logger().severe("Tried to load invalid item: '" + itemTag + "'. " + s)) .resultOrPartial((error) -> CraftEngine.instance().logger().severe("Tried to load invalid item: '" + itemTag + "'. " + error))
.ifPresent(nmsStack -> storageContents[slot] = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsStack)); .ifPresent(nmsStack -> storageContents[slot] = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(nmsStack));
} else { } else {
Object nmsTag = MRegistryOps.SPARROW_NBT.convertTo(MRegistryOps.NBT, itemTag); Object nmsTag = MRegistryOps.SPARROW_NBT.convertTo(MRegistryOps.NBT, itemTag);

View File

@@ -9,6 +9,8 @@ public class BukkitBlockEntityElementConfigs extends BlockEntityElementConfigs {
register(TEXT_DISPLAY, TextDisplayBlockEntityElementConfig.FACTORY); register(TEXT_DISPLAY, TextDisplayBlockEntityElementConfig.FACTORY);
} }
private BukkitBlockEntityElementConfigs() {}
public static void init() { public static void init() {
} }
} }

View File

@@ -34,6 +34,8 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo
private final Quaternionf rotation; private final Quaternionf rotation;
private final ItemDisplayContext displayContext; private final ItemDisplayContext displayContext;
private final Billboard billboard; private final Billboard billboard;
private final float shadowRadius;
private final float shadowStrength;
public ItemDisplayBlockEntityElementConfig(Function<Player, Item<?>> item, public ItemDisplayBlockEntityElementConfig(Function<Player, Item<?>> item,
Vector3f scale, Vector3f scale,
@@ -43,7 +45,9 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo
float yRot, float yRot,
Quaternionf rotation, Quaternionf rotation,
ItemDisplayContext displayContext, ItemDisplayContext displayContext,
Billboard billboard) { Billboard billboard,
float shadowRadius,
float shadowStrength) {
this.item = item; this.item = item;
this.scale = scale; this.scale = scale;
this.position = position; this.position = position;
@@ -53,6 +57,8 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo
this.rotation = rotation; this.rotation = rotation;
this.displayContext = displayContext; this.displayContext = displayContext;
this.billboard = billboard; this.billboard = billboard;
this.shadowRadius = shadowRadius;
this.shadowStrength = shadowStrength;
this.lazyMetadataPacket = player -> { this.lazyMetadataPacket = player -> {
List<Object> dataValues = new ArrayList<>(); List<Object> dataValues = new ArrayList<>();
ItemDisplayEntityData.DisplayedItem.addEntityDataIfNotDefaultValue(item.apply(player).getLiteralObject(), dataValues); ItemDisplayEntityData.DisplayedItem.addEntityDataIfNotDefaultValue(item.apply(player).getLiteralObject(), dataValues);
@@ -61,6 +67,8 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo
ItemDisplayEntityData.BillboardConstraints.addEntityDataIfNotDefaultValue(this.billboard.id(), dataValues); ItemDisplayEntityData.BillboardConstraints.addEntityDataIfNotDefaultValue(this.billboard.id(), dataValues);
ItemDisplayEntityData.Translation.addEntityDataIfNotDefaultValue(this.translation, dataValues); ItemDisplayEntityData.Translation.addEntityDataIfNotDefaultValue(this.translation, dataValues);
ItemDisplayEntityData.DisplayType.addEntityDataIfNotDefaultValue(this.displayContext.id(), dataValues); ItemDisplayEntityData.DisplayType.addEntityDataIfNotDefaultValue(this.displayContext.id(), dataValues);
ItemDisplayEntityData.ShadowRadius.addEntityDataIfNotDefaultValue(this.shadowRadius, dataValues);
ItemDisplayEntityData.ShadowStrength.addEntityDataIfNotDefaultValue(this.shadowStrength, dataValues);
return dataValues; return dataValues;
}; };
} }
@@ -106,6 +114,14 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo
return rotation; return rotation;
} }
public float shadowRadius() {
return shadowRadius;
}
public float shadowStrength() {
return shadowStrength;
}
public List<Object> metadataValues(Player player) { public List<Object> metadataValues(Player player) {
return this.lazyMetadataPacket.apply(player); return this.lazyMetadataPacket.apply(player);
} }
@@ -125,7 +141,9 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo
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)), ItemDisplayContext.valueOf(arguments.getOrDefault("display-context", "none").toString().toUpperCase(Locale.ROOT)),
Billboard.valueOf(arguments.getOrDefault("billboard", "fixed").toString().toUpperCase(Locale.ROOT)) Billboard.valueOf(arguments.getOrDefault("billboard", "fixed").toString().toUpperCase(Locale.ROOT)),
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-radius", 0f), "shadow-radius"),
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("shadow-strength", 1f), "shadow-strength")
); );
} }
} }

View File

@@ -13,7 +13,7 @@ import java.lang.ref.WeakReference;
import java.util.UUID; import java.util.UUID;
public class BukkitEntity extends AbstractEntity { public class BukkitEntity extends AbstractEntity {
private final WeakReference<org.bukkit.entity.Entity> entity; protected final WeakReference<org.bukkit.entity.Entity> entity;
public BukkitEntity(org.bukkit.entity.Entity entity) { public BukkitEntity(org.bukkit.entity.Entity entity) {
this.entity = new WeakReference<>(entity); this.entity = new WeakReference<>(entity);
@@ -103,4 +103,9 @@ public class BukkitEntity extends AbstractEntity {
public <T> void setEntityData(EntityData<T> data, T value, boolean force) { public <T> void setEntityData(EntityData<T> data, T value, boolean force) {
FastNMS.INSTANCE.method$SynchedEntityData$set(entityData(), data.entityDataAccessor(), value, force); FastNMS.INSTANCE.method$SynchedEntityData$set(entityData(), data.entityDataAccessor(), value, force);
} }
@Override
public void remove() {
this.platformEntity().remove();
}
} }

View File

@@ -0,0 +1,17 @@
package net.momirealms.craftengine.bukkit.entity;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.core.entity.ItemEntity;
import org.bukkit.entity.Item;
public class BukkitItemEntity extends BukkitEntity implements ItemEntity {
public BukkitItemEntity(Item entity) {
super(entity);
}
@Override
public net.momirealms.craftengine.core.item.Item<?> getItem() {
return BukkitItemManager.instance().wrap(((Item) platformEntity()).getItemStack());
}
}

View File

@@ -7,13 +7,13 @@ import net.momirealms.craftengine.core.util.VersionHelper;
import java.util.Optional; import java.util.Optional;
public class AbstractMinecartData<T> extends VehicleEntityData<T> { public class AbstractMinecartData<T> extends VehicleEntityData<T> {
// 1.20~1.21.2 // 1.20~1.21.4
public static final AbstractMinecartData<Integer> DisplayBlock = of(AbstractMinecartData.class, EntityDataValue.Serializers$INT, BlockStateUtils.blockStateToId(MBlocks.AIR$defaultState), !VersionHelper.isOrAbove1_21_3()); public static final AbstractMinecartData<Integer> DisplayBlock = of(AbstractMinecartData.class, EntityDataValue.Serializers$INT, BlockStateUtils.blockStateToId(MBlocks.AIR$defaultState), !VersionHelper.isOrAbove1_21_5());
// 1.21.3+ // 1.21.5+
public static final AbstractMinecartData<Optional<Object>> CustomDisplayBlock = of(AbstractMinecartData.class, EntityDataValue.Serializers$OPTIONAL_BLOCK_STATE, Optional.empty(), VersionHelper.isOrAbove1_21_3()); public static final AbstractMinecartData<Optional<Object>> CustomDisplayBlock = of(AbstractMinecartData.class, EntityDataValue.Serializers$OPTIONAL_BLOCK_STATE, Optional.empty(), VersionHelper.isOrAbove1_21_5());
public static final AbstractMinecartData<Integer> DisplayOffset = of(AbstractMinecartData.class, EntityDataValue.Serializers$INT, 6, true); public static final AbstractMinecartData<Integer> DisplayOffset = of(AbstractMinecartData.class, EntityDataValue.Serializers$INT, 6, true);
// 1.20~1.21.2 // 1.20~1.21.4
public static final AbstractMinecartData<Boolean> CustomDisplay = of(AbstractMinecartData.class, EntityDataValue.Serializers$BOOLEAN, false, !VersionHelper.isOrAbove1_21_3()); public static final AbstractMinecartData<Boolean> CustomDisplay = of(AbstractMinecartData.class, EntityDataValue.Serializers$BOOLEAN, false, !VersionHelper.isOrAbove1_21_5());
public static <T> AbstractMinecartData<T> of(final Class<?> clazz, final Object serializer, T defaultValue, boolean condition) { public static <T> AbstractMinecartData<T> of(final Class<?> clazz, final Object serializer, T defaultValue, boolean condition) {
if (!condition) return null; if (!condition) return null;

View File

@@ -5,7 +5,7 @@ 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.FurnitureSettings; import net.momirealms.craftengine.core.entity.furniture.FurnitureSettings;
import net.momirealms.craftengine.core.loot.LootTable; import net.momirealms.craftengine.core.loot.LootTable;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.Context;
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.function.Function; import net.momirealms.craftengine.core.plugin.context.function.Function;
import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.Key;
@@ -20,7 +20,7 @@ public class BukkitCustomFurniture extends AbstractCustomFurniture {
protected BukkitCustomFurniture(@NotNull Key id, protected BukkitCustomFurniture(@NotNull Key id,
@NotNull FurnitureSettings settings, @NotNull FurnitureSettings settings,
@NotNull Map<AnchorType, Placement> placements, @NotNull Map<AnchorType, Placement> placements,
@NotNull Map<EventTrigger, List<Function<PlayerOptionalContext>>> events, @NotNull Map<EventTrigger, List<Function<Context>>> events,
@Nullable LootTable<?> lootTable) { @Nullable LootTable<?> lootTable) {
super(id, settings, placements, events, lootTable); super(id, settings, placements, events, lootTable);
} }
@@ -33,7 +33,7 @@ public class BukkitCustomFurniture extends AbstractCustomFurniture {
private Key id; private Key id;
private Map<AnchorType, Placement> placements; private Map<AnchorType, Placement> placements;
private FurnitureSettings settings; private FurnitureSettings settings;
private Map<EventTrigger, List<Function<PlayerOptionalContext>>> events; private Map<EventTrigger, List<Function<Context>>> events;
private LootTable<?> lootTable; private LootTable<?> lootTable;
@Override @Override
@@ -66,7 +66,7 @@ public class BukkitCustomFurniture extends AbstractCustomFurniture {
} }
@Override @Override
public Builder events(Map<EventTrigger, List<Function<PlayerOptionalContext>>> events) { public Builder events(Map<EventTrigger, List<Function<Context>>> events) {
this.events = events; this.events = events;
return this; return this;
} }

View File

@@ -5,20 +5,16 @@ import it.unimi.dsi.fastutil.ints.IntArrayList;
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.util.EntityUtils;
import net.momirealms.craftengine.bukkit.util.LegacyAttributeUtils;
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.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.ArrayUtils;
import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.QuaternionUtils; import net.momirealms.craftengine.core.util.QuaternionUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
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.attribute.Attribute; import org.bukkit.entity.Entity;
import org.bukkit.entity.*; import org.bukkit.entity.Player;
import org.bukkit.persistence.PersistentDataType; import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -30,7 +26,6 @@ import java.lang.ref.WeakReference;
import java.util.*; import java.util.*;
public class BukkitFurniture implements Furniture { public class BukkitFurniture implements Furniture {
private final Key id;
private final CustomFurniture furniture; private final CustomFurniture furniture;
private final CustomFurniture.Placement placement; private final CustomFurniture.Placement placement;
private FurnitureExtraData extraData; private FurnitureExtraData extraData;
@@ -44,13 +39,10 @@ public class BukkitFurniture implements Furniture {
// cache // cache
private final List<Integer> fakeEntityIds; private final List<Integer> fakeEntityIds;
private final List<Integer> entityIds; private final List<Integer> entityIds;
private final Map<Integer, HitBox> hitBoxes = new Int2ObjectArrayMap<>(); private final Map<Integer, BukkitHitBox> hitBoxes = new Int2ObjectArrayMap<>();
private final Map<Integer, AABB> aabb = new Int2ObjectArrayMap<>(); private final Map<Integer, HitBoxPart> hitBoxParts = new Int2ObjectArrayMap<>();
private final boolean minimized; private final boolean minimized;
private final boolean hasExternalModel; private final boolean hasExternalModel;
// seats
private final Set<Vector3f> occupiedSeats = Collections.synchronizedSet(new HashSet<>());
private final Vector<WeakReference<Entity>> seats = new Vector<>();
// cached spawn packet // cached spawn packet
private Object cachedSpawnPacket; private Object cachedSpawnPacket;
private Object cachedMinimizedSpawnPacket; private Object cachedMinimizedSpawnPacket;
@@ -58,37 +50,40 @@ public class BukkitFurniture implements Furniture {
public BukkitFurniture(Entity baseEntity, public BukkitFurniture(Entity baseEntity,
CustomFurniture furniture, CustomFurniture furniture,
FurnitureExtraData extraData) { FurnitureExtraData extraData) {
this.id = furniture.id();
this.extraData = extraData; this.extraData = extraData;
this.baseEntityId = baseEntity.getEntityId(); this.baseEntityId = baseEntity.getEntityId();
this.location = baseEntity.getLocation(); this.location = baseEntity.getLocation();
this.baseEntity = new WeakReference<>(baseEntity); this.baseEntity = new WeakReference<>(baseEntity);
this.furniture = furniture; this.furniture = furniture;
this.minimized = furniture.settings().minimized(); this.minimized = furniture.settings().minimized();
this.placement = furniture.getValidPlacement(extraData.anchorType().orElseGet(furniture::getAnyAnchorType));
List<Integer> fakeEntityIds = new IntArrayList(); List<Integer> fakeEntityIds = new IntArrayList();
List<Integer> mainEntityIds = new IntArrayList(); List<Integer> mainEntityIds = new IntArrayList();
mainEntityIds.add(this.baseEntityId); mainEntityIds.add(this.baseEntityId);
this.placement = furniture.getValidPlacement(extraData.anchorType().orElseGet(furniture::getAnyAnchorType)); // 绑定外部模型
// bind external furniture
Optional<ExternalModel> optionalExternal = placement.externalModel(); Optional<ExternalModel> optionalExternal = placement.externalModel();
if (optionalExternal.isPresent()) { if (optionalExternal.isPresent()) {
try { try {
optionalExternal.get().bindModel(new BukkitEntity(baseEntity)); optionalExternal.get().bindModel(new BukkitEntity(baseEntity));
} catch (Exception e) { } catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to load external model for furniture " + id, e); CraftEngine.instance().logger().warn("Failed to load external model for furniture " + id(), e);
} }
this.hasExternalModel = true; this.hasExternalModel = true;
} else { } else {
this.hasExternalModel = false; this.hasExternalModel = false;
} }
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();
List<Object> packets = new ArrayList<>(); List<Object> packets = new ArrayList<>();
List<Object> minimizedPackets = new ArrayList<>(); List<Object> minimizedPackets = new ArrayList<>();
List<Collider> colliders = new ArrayList<>(); List<Collider> colliders = new ArrayList<>(4);
WorldPosition position = position(); WorldPosition position = position();
// 初始化家具的元素
for (FurnitureElement element : placement.elements()) { for (FurnitureElement element : placement.elements()) {
int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet(); int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet();
fakeEntityIds.add(entityId); fakeEntityIds.add(entityId);
@@ -97,28 +92,41 @@ public class BukkitFurniture implements Furniture {
if (this.minimized) minimizedPackets.add(packet); if (this.minimized) minimizedPackets.add(packet);
}); });
} }
for (HitBox hitBox : placement.hitBoxes()) {
int[] ids = hitBox.acquireEntityIds(CoreReflections.instance$Entity$ENTITY_COUNTER::incrementAndGet); // 初始化碰撞箱
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) { for (int entityId : ids) {
fakeEntityIds.add(entityId); fakeEntityIds.add(entityId);
mainEntityIds.add(entityId); mainEntityIds.add(entityId);
this.hitBoxes.put(entityId, hitBox); this.hitBoxes.put(entityId, hitBox);
} }
hitBox.initPacketsAndColliders(ids, position, conjugated, (packet, canBeMinimized) -> {
packets.add(packet);
if (this.minimized && !canBeMinimized) {
minimizedPackets.add(packet);
}
}, colliders::add, this.aabb::put);
} }
// 初始化缓存的家具包
try { try {
this.cachedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets); this.cachedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets);
if (this.minimized) { if (this.minimized) {
this.cachedMinimizedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(minimizedPackets); this.cachedMinimizedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(minimizedPackets);
} }
} catch (Exception e) { } catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to init spawn packets for furniture " + id, e); CraftEngine.instance().logger().warn("Failed to init spawn packets for furniture " + id(), e);
} }
this.fakeEntityIds = fakeEntityIds; this.fakeEntityIds = fakeEntityIds;
this.entityIds = mainEntityIds; this.entityIds = mainEntityIds;
this.colliderEntities = colliders.toArray(new Collider[0]); this.colliderEntities = colliders.toArray(new Collider[0]);
@@ -186,57 +194,25 @@ public class BukkitFurniture implements Furniture {
return; return;
} }
this.baseEntity().remove(); this.baseEntity().remove();
this.destroyColliders();
this.destroySeats();
}
@Override
public void destroyColliders() {
for (Collider entity : this.colliderEntities) { for (Collider entity : this.colliderEntities) {
if (entity != null) if (entity != null)
entity.destroy(); entity.destroy();
} }
for (WeakReference<Entity> r : this.seats) {
Entity entity = r.get();
if (entity == null) continue;
for (Entity passenger : entity.getPassengers()) {
entity.removePassenger(passenger);
}
entity.remove();
}
this.seats.clear();
} }
@Override @Override
public void destroySeats() { public void destroySeats() {
for (WeakReference<Entity> entity : this.seats) { for (HitBox hitBox : this.hitBoxes.values()) {
Entity e = entity.get(); for (Seat<HitBox> seat : hitBox.seats()) {
if (e != null) { seat.destroy();
e.remove();
} }
} }
this.seats.clear();
}
@Override
public Optional<Seat> findFirstAvailableSeat(int targetEntityId) {
HitBox hitbox = hitBoxes.get(targetEntityId);
if (hitbox == null) return Optional.empty();
Seat[] seats = hitbox.seats();
if (ArrayUtils.isEmpty(seats)) return Optional.empty();
return Arrays.stream(seats)
.filter(s -> !occupiedSeats.contains(s.offset()))
.findFirst();
}
@Override
public boolean removeOccupiedSeat(Vector3f seat) {
return this.occupiedSeats.remove(seat);
}
@Override
public boolean tryOccupySeat(Seat seat) {
if (this.occupiedSeats.contains(seat.offset())) {
return false;
}
this.occupiedSeats.add(seat.offset());
return true;
} }
@Override @Override
@@ -263,14 +239,14 @@ public class BukkitFurniture implements Furniture {
return this.colliderEntities; return this.colliderEntities;
} }
@Nullable @Override
public HitBox hitBoxByEntityId(int id) { public @Nullable HitBox hitBoxByEntityId(int id) {
return this.hitBoxes.get(id); return this.hitBoxes.get(id);
} }
@Nullable @Override
public AABB aabbByEntityId(int id) { public @Nullable HitBoxPart hitBoxPartByEntityId(int id) {
return this.aabb.get(id); return this.hitBoxParts.get(id);
} }
@Override @Override
@@ -280,7 +256,7 @@ public class BukkitFurniture implements Furniture {
@Override @Override
public @NotNull Key id() { public @NotNull Key id() {
return this.id; return this.furniture.id();
} }
@Override @Override
@@ -293,11 +269,6 @@ public class BukkitFurniture implements Furniture {
return hasExternalModel; return hasExternalModel;
} }
@Override
public void spawnSeatEntityForPlayer(net.momirealms.craftengine.core.entity.player.Player player, Seat seat) {
spawnSeatEntityForPlayer((Player) player.platformPlayer(), seat);
}
@Override @Override
public FurnitureExtraData extraData() { public FurnitureExtraData extraData() {
return this.extraData; return this.extraData;
@@ -317,49 +288,4 @@ public class BukkitFurniture implements Furniture {
CraftEngine.instance().logger().warn("Failed to save furniture data.", e); CraftEngine.instance().logger().warn("Failed to save furniture data.", e);
} }
} }
private void spawnSeatEntityForPlayer(org.bukkit.entity.Player player, Seat seat) {
Location location = this.calculateSeatLocation(seat);
Entity seatEntity = seat.limitPlayerRotation() ?
EntityUtils.spawnEntity(player.getWorld(), VersionHelper.isOrAbove1_20_2() ? location.subtract(0,0.9875,0) : location.subtract(0,0.990625,0), EntityType.ARMOR_STAND, entity -> {
ArmorStand armorStand = (ArmorStand) entity;
if (VersionHelper.isOrAbove1_21_3()) {
Objects.requireNonNull(armorStand.getAttribute(Attribute.MAX_HEALTH)).setBaseValue(0.01);
} else {
LegacyAttributeUtils.setMaxHealth(armorStand);
}
armorStand.setSmall(true);
armorStand.setInvisible(true);
armorStand.setSilent(true);
armorStand.setInvulnerable(true);
armorStand.setArms(false);
armorStand.setCanTick(false);
armorStand.setAI(false);
armorStand.setGravity(false);
armorStand.setPersistent(false);
armorStand.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER, this.baseEntityId());
armorStand.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY, PersistentDataType.STRING, seat.offset().x + ", " + seat.offset().y + ", " + seat.offset().z);
}) :
EntityUtils.spawnEntity(player.getWorld(), VersionHelper.isOrAbove1_20_2() ? location : location.subtract(0,0.25,0), EntityType.ITEM_DISPLAY, entity -> {
ItemDisplay itemDisplay = (ItemDisplay) entity;
itemDisplay.setPersistent(false);
itemDisplay.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER, this.baseEntityId());
itemDisplay.getPersistentDataContainer().set(BukkitFurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY, PersistentDataType.STRING, seat.offset().x + ", " + seat.offset().y + ", " + seat.offset().z);
});
this.seats.add(new WeakReference<>(seatEntity));
if (!seatEntity.addPassenger(player)) {
seatEntity.remove();
this.removeOccupiedSeat(seat.offset());
}
}
private Location calculateSeatLocation(Seat seat) {
Vector3f offset = QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - this.location.getYaw()), 0).conjugate().transform(new Vector3f(seat.offset()));
double yaw = seat.yaw() + this.location.getYaw();
if (yaw < -180) yaw += 360;
Location newLocation = this.location.clone();
newLocation.setYaw((float) yaw);
newLocation.add(offset.x, offset.y + 0.6, -offset.z);
return newLocation;
}
} }

View File

@@ -38,14 +38,18 @@ public class BukkitFurnitureElement extends AbstractFurnitureElement {
Vector3f translation, Vector3f translation,
Vector3f position, Vector3f position,
Quaternionf rotation, Quaternionf rotation,
float shadowRadius,
float shadowStrength,
boolean applyDyedColor) { boolean applyDyedColor) {
super(item, billboard, transform, scale, translation, position, rotation, applyDyedColor); super(item, billboard, transform, scale, translation, position, rotation, shadowRadius, shadowStrength, applyDyedColor);
this.commonValues = new ArrayList<>(); this.commonValues = new ArrayList<>();
ItemDisplayEntityData.Scale.addEntityDataIfNotDefaultValue(scale(), this.commonValues); ItemDisplayEntityData.Scale.addEntityDataIfNotDefaultValue(scale(), this.commonValues);
ItemDisplayEntityData.RotationLeft.addEntityDataIfNotDefaultValue(rotation(), this.commonValues); ItemDisplayEntityData.RotationLeft.addEntityDataIfNotDefaultValue(rotation(), this.commonValues);
ItemDisplayEntityData.BillboardConstraints.addEntityDataIfNotDefaultValue(billboard().id(), this.commonValues); ItemDisplayEntityData.BillboardConstraints.addEntityDataIfNotDefaultValue(billboard().id(), this.commonValues);
ItemDisplayEntityData.Translation.addEntityDataIfNotDefaultValue(translation(), this.commonValues); ItemDisplayEntityData.Translation.addEntityDataIfNotDefaultValue(translation(), this.commonValues);
ItemDisplayEntityData.DisplayType.addEntityDataIfNotDefaultValue(transform().id(), this.commonValues); ItemDisplayEntityData.DisplayType.addEntityDataIfNotDefaultValue(transform().id(), this.commonValues);
ItemDisplayEntityData.ShadowRadius.addEntityDataIfNotDefaultValue(shadowRadius, this.commonValues);
ItemDisplayEntityData.ShadowStrength.addEntityDataIfNotDefaultValue(shadowStrength, this.commonValues);
} }
@Override @Override
@@ -53,7 +57,7 @@ public class BukkitFurnitureElement extends AbstractFurnitureElement {
WorldPosition position = furniture.position(); WorldPosition position = furniture.position();
Vector3f offset = conjugated.transform(new Vector3f(position())); Vector3f offset = conjugated.transform(new Vector3f(position()));
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityId, UUID.randomUUID(), position.x() + offset.x, position.y() + offset.y, position.z() - offset.z, 0, position.xRot(), 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 MEntityTypes.ITEM_DISPLAY, 0, CoreReflections.instance$Vec3$Zero, 0
)); ));
if (applyDyedColor()) { if (applyDyedColor()) {
@@ -102,6 +106,8 @@ public class BukkitFurnitureElement extends AbstractFurnitureElement {
private Vector3f translation; private Vector3f translation;
private Vector3f position; private Vector3f position;
private Quaternionf rotation; private Quaternionf rotation;
private float shadowRadius;
private float shadowStrength;
@Override @Override
public Builder applyDyedColor(boolean applyDyedColor) { public Builder applyDyedColor(boolean applyDyedColor) {
@@ -151,9 +157,21 @@ public class BukkitFurnitureElement extends AbstractFurnitureElement {
return this; 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 @Override
public FurnitureElement build() { public FurnitureElement build() {
return new BukkitFurnitureElement(item, billboard, transform, scale, translation, position, rotation, applyDyedColor); return new BukkitFurnitureElement(item, billboard, transform, scale, translation, position, rotation, shadowRadius, shadowStrength, applyDyedColor);
} }
} }
} }

View File

@@ -1,7 +1,7 @@
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.InteractionHitBox; import net.momirealms.craftengine.bukkit.entity.furniture.hitbox.InteractionHitBoxConfig;
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;
@@ -15,17 +15,13 @@ import net.momirealms.craftengine.core.entity.furniture.*;
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.ResourceConfigUtils;
import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.WorldPosition;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.entity.*; import org.bukkit.entity.*;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.persistence.PersistentDataType; import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.joml.Vector3f;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
@@ -37,8 +33,6 @@ import java.util.function.BiConsumer;
public class BukkitFurnitureManager extends AbstractFurnitureManager { 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_SEAT_BASE_ENTITY_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY);
public static final NamespacedKey FURNITURE_SEAT_VECTOR_3F_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY);
public static final NamespacedKey FURNITURE_COLLISION = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_COLLISION); public static final NamespacedKey FURNITURE_COLLISION = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_COLLISION);
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;
@@ -48,7 +42,6 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
private final Map<Integer, BukkitFurniture> furnitureByRealEntityId = new ConcurrentHashMap<>(256, 0.5f); 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> furnitureByEntityId = new ConcurrentHashMap<>(512, 0.5f);
// Event listeners // Event listeners
private final Listener dismountListener;
private final FurnitureEventListener furnitureEventListener; private final FurnitureEventListener furnitureEventListener;
public static BukkitFurnitureManager instance() { public static BukkitFurnitureManager instance() {
@@ -60,7 +53,6 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
instance = this; instance = this;
this.plugin = plugin; this.plugin = plugin;
this.furnitureEventListener = new FurnitureEventListener(this); this.furnitureEventListener = new FurnitureEventListener(this);
this.dismountListener = VersionHelper.isOrAbove1_20_3() ? new DismountListener1_20_3(this) : new DismountListener1_20(this::handleDismount);
} }
@Override @Override
@@ -95,7 +87,6 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
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(); COLLISION_ENTITY_TYPE = Config.colliderType();
Bukkit.getPluginManager().registerEvents(this.dismountListener, this.plugin.javaPlugin());
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(), () -> {});
@@ -129,15 +120,8 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
@Override @Override
public void disable() { public void disable() {
HandlerList.unregisterAll(this.dismountListener);
HandlerList.unregisterAll(this.furnitureEventListener); HandlerList.unregisterAll(this.furnitureEventListener);
unload(); unload();
for (Player player : Bukkit.getOnlinePlayers()) {
Entity vehicle = player.getVehicle();
if (vehicle != null) {
tryLeavingSeat(player, vehicle);
}
}
} }
@Override @Override
@@ -327,84 +311,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
} }
@Override @Override
protected HitBox defaultHitBox() { protected HitBoxConfig defaultHitBox() {
return InteractionHitBox.DEFAULT; return InteractionHitBoxConfig.DEFAULT;
}
protected void handleDismount(Player player, Entity entity) {
if (!isSeatCarrierType(entity)) return;
Location location = entity.getLocation();
plugin.scheduler().sync().runDelayed(() -> tryLeavingSeat(player, entity), player.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4);
}
@SuppressWarnings("DuplicatedCode")
protected void tryLeavingSeat(@NotNull Player player, @NotNull Entity vehicle) {
Integer baseFurniture = vehicle.getPersistentDataContainer().get(FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER);
if (baseFurniture == null) return;
vehicle.remove();
BukkitFurniture furniture = loadedFurnitureByRealEntityId(baseFurniture);
if (furniture == null) {
return;
}
String vector3f = vehicle.getPersistentDataContainer().get(BukkitFurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY, PersistentDataType.STRING);
if (vector3f == null) {
plugin.logger().warn("Failed to get vector3f for player " + player.getName() + "'s seat");
return;
}
Vector3f seatPos = ResourceConfigUtils.getAsVector3f(vector3f, "seat");
furniture.removeOccupiedSeat(seatPos);
if (player.getVehicle() != null) return;
Location vehicleLocation = vehicle.getLocation();
Location originalLocation = vehicleLocation.clone();
originalLocation.setY(furniture.location().getY());
Location targetLocation = originalLocation.clone().add(vehicleLocation.getDirection().multiply(1.1));
if (!isSafeLocation(targetLocation)) {
targetLocation = findSafeLocationNearby(originalLocation);
if (targetLocation == null) return;
}
targetLocation.setYaw(player.getLocation().getYaw());
targetLocation.setPitch(player.getLocation().getPitch());
if (VersionHelper.isFolia()) {
player.teleportAsync(targetLocation);
} else {
player.teleport(targetLocation);
}
}
protected boolean isSeatCarrierType(Entity entity) {
return (entity instanceof ArmorStand || entity instanceof ItemDisplay);
}
@SuppressWarnings("DuplicatedCode")
private boolean isSafeLocation(Location location) {
World world = location.getWorld();
if (world == null) return false;
int x = location.getBlockX();
int y = location.getBlockY();
int z = location.getBlockZ();
if (!world.getBlockAt(x, y - 1, z).getType().isSolid()) return false;
if (!world.getBlockAt(x, y, z).isPassable()) return false;
return world.getBlockAt(x, y + 1, z).isPassable();
}
@SuppressWarnings("DuplicatedCode")
@Nullable
private Location findSafeLocationNearby(Location center) {
World world = center.getWorld();
if (world == null) return null;
int centerX = center.getBlockX();
int centerY = center.getBlockY();
int centerZ = center.getBlockZ();
for (int dx = -1; dx <= 1; dx++) {
for (int dz = -1; dz <= 1; dz++) {
if (dx == 0 && dz == 0) continue;
int x = centerX + dx;
int z = centerZ + dz;
Location nearbyLocation = new Location(world, x + 0.5, centerY, z + 0.5);
if (isSafeLocation(nearbyLocation)) return nearbyLocation;
}
}
return null;
} }
} }

View File

@@ -0,0 +1,70 @@
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());
}
}

View File

@@ -1,21 +1,24 @@
package net.momirealms.craftengine.bukkit.entity.furniture; package net.momirealms.craftengine.bukkit.entity.furniture;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDismountEvent; import org.bukkit.event.entity.EntityDismountEvent;
public class DismountListener1_20_3 implements Listener { import java.util.function.BiConsumer;
private final BukkitFurnitureManager manager;
public DismountListener1_20_3(final BukkitFurnitureManager manager) { public class DismountListener1_20_3 implements Listener {
this.manager = manager; private final BiConsumer<Player, Entity> consumer;
public DismountListener1_20_3(final BiConsumer<Player, Entity> consumer) {
this.consumer = consumer;
} }
@EventHandler(ignoreCancelled = true) @EventHandler(ignoreCancelled = true)
public void onDismount(EntityDismountEvent event) { public void onDismount(EntityDismountEvent event) {
if (event.getEntity() instanceof Player player) { if (event.getEntity() instanceof Player player) {
this.manager.handleDismount(player, event.getDismounted()); this.consumer.accept(player, event.getDismounted());
} }
} }
} }

View File

@@ -2,21 +2,15 @@ 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 org.bukkit.entity.ArmorStand;
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;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerInteractAtEntityEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.world.ChunkUnloadEvent; 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.persistence.PersistentDataType;
import java.util.List; import java.util.List;
@@ -100,35 +94,4 @@ public class FurnitureEventListener implements Listener {
this.manager.handleCollisionEntityUnload(entity); this.manager.handleCollisionEntityUnload(entity);
} }
} }
@EventHandler(ignoreCancelled = true)
public void onPlayerQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
Entity entity = player.getVehicle();
if (entity == null) return;
if (this.manager.isSeatCarrierType(entity)) {
this.manager.tryLeavingSeat(player, entity);
}
}
@EventHandler(ignoreCancelled = true)
public void onPlayerDeath(PlayerDeathEvent event) {
Player player = event.getPlayer();
Entity entity = player.getVehicle();
if (entity == null) return;
if (this.manager.isSeatCarrierType(entity)) {
this.manager.tryLeavingSeat(player, entity);
}
}
// do not allow players to put item on seats
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onInteractArmorStand(PlayerInteractAtEntityEvent event) {
Entity clicked = event.getRightClicked();
if (clicked instanceof ArmorStand armorStand) {
Integer baseFurniture = armorStand.getPersistentDataContainer().get(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER);
if (baseFurniture == null) return;
event.setCancelled(true);
}
}
} }

View File

@@ -7,9 +7,9 @@ public class BukkitHitBoxTypes extends HitBoxTypes {
public static void init() {} public static void init() {}
static { static {
register(INTERACTION, InteractionHitBox.FACTORY); register(INTERACTION, InteractionHitBoxConfig.FACTORY);
register(SHULKER, ShulkerHitBox.FACTORY); register(SHULKER, ShulkerHitBoxConfig.FACTORY);
register(HAPPY_GHAST, HappyGhastHitBox.FACTORY); register(HAPPY_GHAST, HappyGhastHitBoxConfig.FACTORY);
register(CUSTOM, CustomHitBox.FACTORY); register(CUSTOM, CustomHitBoxConfig.FACTORY);
} }
} }

View File

@@ -6,6 +6,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflect
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeHolders; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeHolders;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections;
import net.momirealms.craftengine.core.entity.furniture.*; 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.plugin.locale.LocalizedResourceConfigException;
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;
@@ -23,13 +24,13 @@ import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
public class CustomHitBox extends AbstractHitBox { public class CustomHitBoxConfig extends AbstractHitBoxConfig {
public static final Factory FACTORY = new Factory(); public static final Factory FACTORY = new Factory();
private final float scale; private final float scale;
private final EntityType entityType; private final EntityType entityType;
private final List<Object> cachedValues = new ArrayList<>(); private final List<Object> cachedValues = new ArrayList<>();
public CustomHitBox(Seat[] seats, Vector3f position, EntityType type, float scale, boolean blocksBuilding, boolean canBeHitByProjectile) { public CustomHitBoxConfig(SeatConfig[] seats, Vector3f position, EntityType type, float scale, boolean blocksBuilding, boolean canBeHitByProjectile) {
super(seats, position, false, blocksBuilding, canBeHitByProjectile); super(seats, position, false, blocksBuilding, canBeHitByProjectile);
this.scale = scale; this.scale = scale;
this.entityType = type; this.entityType = type;
@@ -39,11 +40,11 @@ public class CustomHitBox extends AbstractHitBox {
} }
public EntityType entityType() { public EntityType entityType() {
return entityType; return this.entityType;
} }
public float scale() { public float scale() {
return scale; return this.scale;
} }
@Override @Override
@@ -52,11 +53,11 @@ public class CustomHitBox extends AbstractHitBox {
} }
@Override @Override
public void initPacketsAndColliders(int[] entityId, WorldPosition position, Quaternionf conjugated, BiConsumer<Object, Boolean> packets, Consumer<Collider> collider, BiConsumer<Integer, AABB> aabb) { 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())); Vector3f offset = conjugated.transform(new Vector3f(position()));
try { try {
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityId[0], UUID.randomUUID(), position.x() + offset.x, position.y() + offset.y, position.z() - offset.z, 0, position.xRot(), 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 FastNMS.INSTANCE.method$CraftEntityType$toNMSEntityType(this.entityType), 0, CoreReflections.instance$Vec3$Zero, 0
), true); ), true);
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId[0], List.copyOf(this.cachedValues)), true); packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId[0], List.copyOf(this.cachedValues)), true);
@@ -79,10 +80,10 @@ public class CustomHitBox extends AbstractHitBox {
return new int[] {entityIdSupplier.get()}; return new int[] {entityIdSupplier.get()};
} }
public static class Factory implements HitBoxFactory { public static class Factory implements HitBoxConfigFactory {
@Override @Override
public HitBox create(Map<String, Object> arguments) { public HitBoxConfig create(Map<String, Object> arguments) {
Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"); Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position");
float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", 1), "scale"); float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", 1), "scale");
String type = (String) arguments.getOrDefault("entity-type", "slime"); String type = (String) arguments.getOrDefault("entity-type", "slime");
@@ -92,7 +93,7 @@ public class CustomHitBox extends AbstractHitBox {
} }
boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", false), "can-be-hit-by-projectile"); 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 blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building");
return new CustomHitBox(HitBoxFactory.getSeats(arguments), position, entityType, scale, blocksBuilding, canBeHitByProjectile); return new CustomHitBoxConfig(SeatConfig.fromObj(arguments.get("seats")), position, entityType, scale, blocksBuilding, canBeHitByProjectile);
} }
} }
} }

View File

@@ -8,9 +8,11 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeH
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.plugin.reflection.minecraft.NetworkReflections;
import net.momirealms.craftengine.core.entity.furniture.*; 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.Key;
import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.util.VersionHelper; 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.World;
import net.momirealms.craftengine.core.world.WorldPosition; import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.craftengine.core.world.collision.AABB; import net.momirealms.craftengine.core.world.collision.AABB;
@@ -22,13 +24,13 @@ import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
public class HappyGhastHitBox extends AbstractHitBox { public class HappyGhastHitBoxConfig extends AbstractHitBoxConfig {
public static final Factory FACTORY = new Factory(); public static final Factory FACTORY = new Factory();
private final double scale; private final double scale;
private final boolean hardCollision; private final boolean hardCollision;
private final List<Object> cachedValues = new ArrayList<>(); private final List<Object> cachedValues = new ArrayList<>();
public HappyGhastHitBox(Seat[] seats, Vector3f position, double scale, boolean canUseOn, boolean blocksBuilding, boolean canBeHitByProjectile, boolean hardCollision) { public HappyGhastHitBoxConfig(SeatConfig[] seats, Vector3f position, double scale, boolean canUseOn, boolean blocksBuilding, boolean canBeHitByProjectile, boolean hardCollision) {
super(seats, position, canUseOn, blocksBuilding, canBeHitByProjectile); super(seats, position, canUseOn, blocksBuilding, canBeHitByProjectile);
this.scale = scale; this.scale = scale;
this.hardCollision = hardCollision; this.hardCollision = hardCollision;
@@ -43,21 +45,26 @@ public class HappyGhastHitBox extends AbstractHitBox {
} }
public double scale() { public double scale() {
return scale; return this.scale;
} }
public boolean hardCollision() { public boolean hardCollision() {
return hardCollision; return this.hardCollision;
} }
@Override @Override
public void initPacketsAndColliders(int[] entityIds, WorldPosition position, Quaternionf conjugated, BiConsumer<Object, Boolean> packets, Consumer<Collider> collider, BiConsumer<Integer, AABB> aabb) { 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())); Vector3f offset = conjugated.transform(new Vector3f(position()));
try { try {
double x = position.x(); double x = position.x();
double y = position.y(); double y = position.y();
double z = position.z(); double z = position.z();
float yaw = position.xRot(); float yaw = position.yRot();
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityIds[0], UUID.randomUUID(), x + offset.x, y + offset.y, z - offset.z, 0, yaw, entityIds[0], UUID.randomUUID(), x + offset.x, y + offset.y, z - offset.z, 0, yaw,
MEntityTypes.HAPPY_GHAST, 0, CoreReflections.instance$Vec3$Zero, 0 MEntityTypes.HAPPY_GHAST, 0, CoreReflections.instance$Vec3$Zero, 0
@@ -76,11 +83,11 @@ public class HappyGhastHitBox extends AbstractHitBox {
} }
} }
public Collider createCollider(World world, Vector3f offset, double x, double y, double z, int entityId, BiConsumer<Integer, AABB> aabb) { 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); AABB ceAABB = createAABB(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(entityId, ceAABB); aabb.accept(new HitBoxPart(entityId, ceAABB, new Vec3d(x, y, z)));
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());
} }
@@ -109,10 +116,10 @@ public class HappyGhastHitBox extends AbstractHitBox {
return new int[] {entityIdSupplier.get()}; return new int[] {entityIdSupplier.get()};
} }
public static class Factory implements HitBoxFactory { public static class Factory implements HitBoxConfigFactory {
@Override @Override
public HitBox create(Map<String, Object> arguments) { public HitBoxConfig create(Map<String, Object> arguments) {
if (!VersionHelper.isOrAbove1_21_6()) { if (!VersionHelper.isOrAbove1_21_6()) {
throw new UnsupportedOperationException("HappyGhastHitBox is only supported on 1.21.6+"); throw new UnsupportedOperationException("HappyGhastHitBox is only supported on 1.21.6+");
} }
@@ -121,8 +128,8 @@ public class HappyGhastHitBox extends AbstractHitBox {
boolean canUseOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", true), "can-use-item-on"); 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 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"); boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building");
return new HappyGhastHitBox( return new HappyGhastHitBoxConfig(
HitBoxFactory.getSeats(arguments), SeatConfig.fromObj(arguments.get("seats")),
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"), ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"),
scale, canUseOn, blocksBuilding, canBeHitByProjectile, hardCollision scale, canUseOn, blocksBuilding, canBeHitByProjectile, hardCollision
); );

View File

@@ -6,6 +6,7 @@ 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.plugin.reflection.minecraft.MEntityTypes;
import net.momirealms.craftengine.core.entity.furniture.*; 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.Key;
import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.Vec3d; import net.momirealms.craftengine.core.world.Vec3d;
@@ -22,15 +23,15 @@ import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
public class InteractionHitBox extends AbstractHitBox { public class InteractionHitBoxConfig extends AbstractHitBoxConfig {
public static final Factory FACTORY = new Factory(); public static final Factory FACTORY = new Factory();
public static final InteractionHitBox DEFAULT = new InteractionHitBox(new Seat[0], new Vector3f(), new Vector3f(1,1,1), true, false, false, false); 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 Vector3f size;
private final boolean responsive; private final boolean responsive;
private final List<Object> cachedValues = new ArrayList<>(); private final List<Object> cachedValues = new ArrayList<>();
public InteractionHitBox(Seat[] seats, Vector3f position, Vector3f size, boolean responsive, boolean canUseOn, boolean blocksBuilding, boolean canBeHitByProjectile) { public InteractionHitBoxConfig(SeatConfig[] seats, Vector3f position, Vector3f size, boolean responsive, boolean canUseOn, boolean blocksBuilding, boolean canBeHitByProjectile) {
super(seats, position, canUseOn, blocksBuilding, canBeHitByProjectile); super(seats, position, canUseOn, blocksBuilding, canBeHitByProjectile);
this.size = size; this.size = size;
this.responsive = responsive; this.responsive = responsive;
@@ -40,11 +41,11 @@ public class InteractionHitBox extends AbstractHitBox {
} }
public boolean responsive() { public boolean responsive() {
return responsive; return this.responsive;
} }
public Vector3f size() { public Vector3f size() {
return size; return this.size;
} }
@Override @Override
@@ -53,22 +54,26 @@ public class InteractionHitBox extends AbstractHitBox {
} }
@Override @Override
public void initPacketsAndColliders(int[] entityId, WorldPosition position, Quaternionf conjugated, BiConsumer<Object, Boolean> packets, Consumer<Collider> collider, BiConsumer<Integer, AABB> aabb) { 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())); Vector3f offset = conjugated.transform(new Vector3f(position()));
double x = position.x(); double x = position.x();
double y = position.y(); double y = position.y();
double z = position.z(); double z = position.z();
float yaw = position.xRot(); float yaw = position.yRot();
Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y, z - offset.z);
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket( packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
entityId[0], UUID.randomUUID(), x + offset.x, y + offset.y, z - offset.z, 0, yaw, entityId[0], UUID.randomUUID(), vec3d.x, vec3d.y, vec3d.z, 0, yaw,
MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0 MEntityTypes.INTERACTION, 0, CoreReflections.instance$Vec3$Zero, 0
), true); ), true);
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId[0], List.copyOf(this.cachedValues)), true); packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId[0], List.copyOf(this.cachedValues)), true);
if (canUseItemOn()) { aabb.accept(new HitBoxPart(entityId[0], AABB.fromInteraction(vec3d, this.size.x, this.size.y), vec3d));
aabb.accept(entityId[0], AABB.fromInteraction(new Vec3d(x + offset.x, y + offset.y, z - offset.z), this.size.x, this.size.y));
}
if (blocksBuilding() || this.canBeHitByProjectile()) { if (blocksBuilding() || this.canBeHitByProjectile()) {
AABB ceAABB = AABB.fromInteraction(new Vec3d(x + offset.x, y + offset.y, z - offset.z), this.size.x, this.size.y); 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); 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())); collider.accept(new BukkitCollider(position.world().serverWorld(), nmsAABB, x, y, z, this.canBeHitByProjectile(), false, this.blocksBuilding()));
} }
@@ -88,10 +93,10 @@ public class InteractionHitBox extends AbstractHitBox {
return new int[] {entityIdSupplier.get()}; return new int[] {entityIdSupplier.get()};
} }
public static class Factory implements HitBoxFactory { public static class Factory implements HitBoxConfigFactory {
@Override @Override
public HitBox create(Map<String, Object> arguments) { public HitBoxConfig create(Map<String, Object> arguments) {
Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"); Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position");
float width; float width;
float height; float height;
@@ -100,15 +105,15 @@ public class InteractionHitBox extends AbstractHitBox {
width = Float.parseFloat(split[0]); width = Float.parseFloat(split[0]);
height = Float.parseFloat(split[1]); height = Float.parseFloat(split[1]);
} else { } else {
width = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("width", "1"), "width"); width = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("width", 1), "width");
height = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("height", "1"), "height"); height = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("height", 1), "height");
} }
boolean canUseOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", false), "can-use-item-on"); 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 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 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 blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building");
return new InteractionHitBox( return new InteractionHitBoxConfig(
HitBoxFactory.getSeats(arguments), SeatConfig.fromObj(arguments.get("seats")),
position, position,
new Vector3f(width, height, width), new Vector3f(width, height, width),
interactive, canUseOn, blocksBuilding, canBeHitByProjectile interactive, canUseOn, blocksBuilding, canBeHitByProjectile

View File

@@ -10,6 +10,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityType
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections; 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.*;
import net.momirealms.craftengine.core.entity.seat.SeatConfig;
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.World; import net.momirealms.craftengine.core.world.World;
@@ -23,7 +24,7 @@ import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
public class ShulkerHitBox extends AbstractHitBox { public class ShulkerHitBoxConfig extends AbstractHitBoxConfig {
public static final Factory FACTORY = new Factory(); public static final Factory FACTORY = new Factory();
// 1.20.6+ // 1.20.6+
private final float scale; private final float scale;
@@ -35,7 +36,7 @@ public class ShulkerHitBox extends AbstractHitBox {
private final DirectionalShulkerSpawner spawner; private final DirectionalShulkerSpawner spawner;
private final AABBCreator aabbCreator; private final AABBCreator aabbCreator;
public ShulkerHitBox(Seat[] seats, Vector3f position, Direction direction, float scale, byte peek, boolean interactionEntity, boolean interactive, boolean canUseOn, boolean blocksBuilding, boolean canBeHitByProjectile) { public ShulkerHitBoxConfig(SeatConfig[] seats, Vector3f position, Direction direction, float scale, byte peek, boolean interactionEntity, boolean interactive, boolean canUseOn, boolean blocksBuilding, boolean canBeHitByProjectile) {
super(seats, position, canUseOn, blocksBuilding, canBeHitByProjectile); super(seats, position, canUseOn, blocksBuilding, canBeHitByProjectile);
this.direction = direction; this.direction = direction;
this.scale = scale; this.scale = scale;
@@ -65,7 +66,8 @@ public class ShulkerHitBox extends AbstractHitBox {
), true); ), true);
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true); packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true);
if (canUseOn) { if (canUseOn) {
aabb.accept(entityIds[2], AABB.fromInteraction(new Vec3d(x + offset.x, y + offset.y, z - offset.z), scale, shulkerHeight)); Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y, z - offset.z);
aabb.accept(new HitBoxPart(entityIds[2], AABB.fromInteraction(vec3d, scale, shulkerHeight), vec3d));
} }
} }
}; };
@@ -84,7 +86,8 @@ public class ShulkerHitBox extends AbstractHitBox {
), true); ), true);
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true); packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true);
if (canUseOn) { if (canUseOn) {
aabb.accept(entityIds[2], AABB.fromInteraction(new Vec3d(x + offset.x, y + offset.y - shulkerHeight + scale, z - offset.z), scale, shulkerHeight)); Vec3d vec3d = new Vec3d(x + offset.x, y + offset.y - shulkerHeight + scale, z - offset.z);
aabb.accept(new HitBoxPart(entityIds[2], AABB.fromInteraction(vec3d, scale, shulkerHeight), vec3d));
} }
} }
}; };
@@ -113,8 +116,10 @@ public class ShulkerHitBox extends AbstractHitBox {
), true); ), true);
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[3], List.copyOf(cachedInteractionValues)), true); packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[3], List.copyOf(cachedInteractionValues)), true);
if (canUseOn) { if (canUseOn) {
aabb.accept(entityIds[2], AABB.fromInteraction(new Vec3d(x + offset.x, y + offset.y, z - offset.z), scale, scale)); Vec3d vec3d1 = new Vec3d(x + offset.x, y + offset.y, z - offset.z);
aabb.accept(entityIds[3], AABB.fromInteraction(new Vec3d(x + offset.x + shulkerDirection.stepX() * distance, y + offset.y, z - offset.z + shulkerDirection.stepZ() * distance), scale, scale)); Vec3d vec3d2 = new Vec3d(x + offset.x + shulkerDirection.stepX() * distance, y + offset.y, z - offset.z + shulkerDirection.stepZ() * distance);
aabb.accept(new HitBoxPart(entityIds[2], AABB.fromInteraction(vec3d1, scale, scale), vec3d1));
aabb.accept(new HitBoxPart(entityIds[3], AABB.fromInteraction(vec3d2, scale, scale), vec3d2));
} }
} }
}; };
@@ -126,11 +131,11 @@ public class ShulkerHitBox extends AbstractHitBox {
} }
} }
public Collider createCollider(Direction direction, World world, Vector3f offset, double x, double y, double z, int entityId, BiConsumer<Integer, AABB> aabb) { public Collider createCollider(Direction direction, World world, Vector3f offset, double x, double y, double z, int entityId, Consumer<HitBoxPart> 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(entityId, ceAABB); aabb.accept(new HitBoxPart(entityId, ceAABB, new Vec3d(x, y, z)));
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());
} }
@@ -200,13 +205,18 @@ public class ShulkerHitBox extends AbstractHitBox {
} }
@Override @Override
public void initPacketsAndColliders(int[] entityIds, WorldPosition position, Quaternionf conjugated, BiConsumer<Object, Boolean> packets, Consumer<Collider> collider, BiConsumer<Integer, AABB> aabb) { 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())); Vector3f offset = conjugated.transform(new Vector3f(position()));
try { try {
double x = position.x(); double x = position.x();
double y = position.y(); double y = position.y();
double z = position.z(); double z = position.z();
float yaw = position.xRot(); float yaw = position.yRot();
double originalY = y + offset.y; double originalY = y + offset.y;
double integerPart = Math.floor(originalY); double integerPart = Math.floor(originalY);
double fractionalPart = originalY - integerPart; double fractionalPart = originalY - integerPart;
@@ -251,7 +261,16 @@ public class ShulkerHitBox extends AbstractHitBox {
@FunctionalInterface @FunctionalInterface
interface DirectionalShulkerSpawner { 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, BiConsumer<Integer, AABB> aabb); 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 @FunctionalInterface
@@ -276,10 +295,10 @@ public class ShulkerHitBox extends AbstractHitBox {
} }
} }
public static class Factory implements HitBoxFactory { public static class Factory implements HitBoxConfigFactory {
@Override @Override
public HitBox create(Map<String, Object> arguments) { public HitBoxConfig create(Map<String, Object> arguments) {
Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"); Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position");
float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", "1"), "scale"); float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", "1"), "scale");
byte peek = (byte) ResourceConfigUtils.getAsInt(arguments.getOrDefault("peek", 0), "peek"); byte peek = (byte) ResourceConfigUtils.getAsInt(arguments.getOrDefault("peek", 0), "peek");
@@ -289,8 +308,8 @@ public class ShulkerHitBox extends AbstractHitBox {
boolean canUseItemOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", true), "can-use-item-on"); 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 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"); boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building");
return new ShulkerHitBox( return new ShulkerHitBoxConfig(
HitBoxFactory.getSeats(arguments), SeatConfig.fromObj(arguments.get("seats")),
position, directionEnum, position, directionEnum,
scale, peek, interactionEntity, interactive, canUseItemOn, blocksBuilding, canBeHitByProjectile scale, peek, interactionEntity, interactive, canUseItemOn, blocksBuilding, canBeHitByProjectile
); );

View File

@@ -0,0 +1,146 @@
package net.momirealms.craftengine.bukkit.entity.seat;
import net.momirealms.craftengine.bukkit.util.EntityUtils;
import net.momirealms.craftengine.bukkit.util.LegacyAttributeUtils;
import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.core.entity.seat.Seat;
import net.momirealms.craftengine.core.entity.seat.SeatConfig;
import net.momirealms.craftengine.core.entity.seat.SeatOwner;
import net.momirealms.craftengine.core.util.QuaternionUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.craftengine.core.world.WorldPosition;
import net.momirealms.sparrow.nbt.CompoundTag;
import net.momirealms.sparrow.nbt.NBT;
import org.bukkit.Location;
import org.bukkit.attribute.Attribute;
import org.bukkit.entity.*;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector3f;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.Objects;
public class BukkitSeat<O extends SeatOwner> implements Seat<O> {
private final O owner;
private final SeatConfig seatConfig;
private WeakReference<Entity> entity;
public BukkitSeat(O owner, SeatConfig config) {
this.owner = owner;
this.seatConfig = config;
}
@Override
public O owner() {
return this.owner;
}
@Override
public SeatConfig config() {
return this.seatConfig;
}
@Nullable
public Entity getSeatEntity() {
return this.entity == null ? null : this.entity.get();
}
@Override
public boolean isOccupied() {
Entity seatEntity = getSeatEntity();
return seatEntity != null && seatEntity.isValid() && !seatEntity.getPassengers().isEmpty();
}
@Override
public void destroy() {
if (this.entity != null) {
Entity entity = this.entity.get();
if (entity != null) {
if (entity.isValid()) {
entity.remove();
}
this.entity = null;
}
}
}
private float yRot() {
return this.seatConfig.yRot();
}
private Vector3f position() {
return this.seatConfig.position();
}
private boolean limitPlayerRotation() {
return this.seatConfig.limitPlayerRotation();
}
private Location calculateSeatLocation(Location sourceLocation) {
Vector3f offset = QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - sourceLocation.getYaw()), 0).conjugate().transform(new Vector3f(this.position()));
double yaw = this.yRot() + sourceLocation.getYaw();
if (yaw < -180) yaw += 360;
Location newLocation = sourceLocation.clone();
newLocation.setYaw((float) yaw);
newLocation.add(offset.x, offset.y + 0.6, -offset.z);
return newLocation;
}
@Override
public boolean spawnSeat(net.momirealms.craftengine.core.entity.player.Player player, WorldPosition source) {
return spawnSeatEntityForPlayer((Player) player.platformPlayer(), LocationUtils.toLocation(source));
}
private boolean spawnSeatEntityForPlayer(Player player, Location sourceLocation) {
// 移除就有的座椅
this.destroy();
// 计算座椅的位置
Location location = this.calculateSeatLocation(sourceLocation);
CompoundTag extraData = new CompoundTag();
this.owner.saveCustomData(extraData);
byte[] data;
try {
data = NBT.toBytes(extraData);
} catch (IOException e) {
return false;
}
// 生成座椅实体
Entity seatEntity = this.limitPlayerRotation() ?
EntityUtils.spawnEntity(player.getWorld(), VersionHelper.isOrAbove1_20_2() ? location.subtract(0,0.9875,0) : location.subtract(0,0.990625,0), EntityType.ARMOR_STAND, entity -> {
ArmorStand armorStand = (ArmorStand) entity;
if (VersionHelper.isOrAbove1_21_3()) {
Objects.requireNonNull(armorStand.getAttribute(Attribute.MAX_HEALTH)).setBaseValue(0.01);
} else {
LegacyAttributeUtils.setMaxHealth(armorStand);
}
armorStand.setSmall(true);
armorStand.setInvisible(true);
armorStand.setSilent(true);
armorStand.setInvulnerable(true);
armorStand.setArms(false);
armorStand.setCanTick(false);
armorStand.setAI(false);
armorStand.setGravity(false);
armorStand.setPersistent(false);
armorStand.getPersistentDataContainer().set(BukkitSeatManager.SEAT_KEY, PersistentDataType.BOOLEAN, true);
armorStand.getPersistentDataContainer().set(BukkitSeatManager.SEAT_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY, data);
}) :
EntityUtils.spawnEntity(player.getWorld(), VersionHelper.isOrAbove1_20_2() ? location : location.subtract(0,0.25,0), EntityType.ITEM_DISPLAY, entity -> {
ItemDisplay itemDisplay = (ItemDisplay) entity;
itemDisplay.setPersistent(false);
itemDisplay.getPersistentDataContainer().set(BukkitSeatManager.SEAT_KEY, PersistentDataType.BOOLEAN, true);
itemDisplay.getPersistentDataContainer().set(BukkitSeatManager.SEAT_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY, data);
});
if (!seatEntity.addPassenger(player)) {
seatEntity.remove();
return false;
} else {
this.entity = new WeakReference<>(seatEntity);
return true;
}
}
}

View File

@@ -0,0 +1,127 @@
package net.momirealms.craftengine.bukkit.entity.seat;
import net.momirealms.craftengine.bukkit.entity.furniture.DismountListener1_20;
import net.momirealms.craftengine.bukkit.entity.furniture.DismountListener1_20_3;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.util.EntityUtils;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.core.entity.seat.SeatManager;
import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.sparrow.nbt.CompoundTag;
import net.momirealms.sparrow.nbt.NBT;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.entity.ItemDisplay;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerInteractAtEntityEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
public class BukkitSeatManager implements SeatManager {
private static BukkitSeatManager instance;
public static final NamespacedKey SEAT_KEY = KeyUtils.toNamespacedKey(SeatManager.SEAT_KEY);
public static final NamespacedKey SEAT_EXTRA_DATA_KEY = KeyUtils.toNamespacedKey(SeatManager.SEAT_EXTRA_DATA_KEY);
private final BukkitCraftEngine plugin;
private final Listener dismountListener;
public BukkitSeatManager(BukkitCraftEngine plugin) {
this.plugin = plugin;
this.dismountListener = VersionHelper.isOrAbove1_20_3() ? new DismountListener1_20_3(this::handleDismount) : new DismountListener1_20(this::handleDismount);
instance = this;
}
public CompoundTag getSeatExtraData(Entity entity) {
if (!isSeatEntityType(entity)) {
throw new IllegalArgumentException("Entity is not a seat");
}
byte[] bytes = entity.getPersistentDataContainer().get(SEAT_EXTRA_DATA_KEY, PersistentDataType.BYTE_ARRAY);
try {
return NBT.fromBytes(bytes);
} catch (IOException e) {
throw new RuntimeException("Failed to read extra data from seat", e);
}
}
private void handleDismount(Player player, @NotNull Entity dismounted) {
if (!isSeatEntityType(dismounted)) return;
tryLeavingSeat(player, dismounted);
}
@Override
public void delayedInit() {
Bukkit.getPluginManager().registerEvents(this.dismountListener, this.plugin.javaPlugin());
}
@Override
public void disable() {
HandlerList.unregisterAll(this.dismountListener);
for (Player player : Bukkit.getOnlinePlayers()) {
Entity vehicle = player.getVehicle();
if (vehicle != null) {
tryLeavingSeat(player, vehicle);
}
}
}
@EventHandler(ignoreCancelled = true)
public void onPlayerQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
Entity entity = player.getVehicle();
if (entity == null) return;
if (this.isSeatEntityType(entity)) {
this.tryLeavingSeat(player, entity);
}
}
@EventHandler(ignoreCancelled = true)
public void onPlayerDeath(PlayerDeathEvent event) {
Player player = event.getPlayer();
Entity entity = player.getVehicle();
if (entity == null) return;
if (this.isSeatEntityType(entity)) {
this.tryLeavingSeat(player, entity);
}
}
// do not allow players to put item on seats
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
public void onInteractArmorStand(PlayerInteractAtEntityEvent event) {
Entity clicked = event.getRightClicked();
if (clicked instanceof ArmorStand armorStand) {
if (!armorStand.getPersistentDataContainer().has(SEAT_KEY)) return;
event.setCancelled(true);
}
}
protected boolean isSeatEntityType(Entity entity) {
return (entity instanceof ArmorStand || entity instanceof ItemDisplay);
}
protected void tryLeavingSeat(@NotNull Player player, @NotNull Entity seat) {
boolean isSeat = seat.getPersistentDataContainer().has(SEAT_KEY);
if (!isSeat) return;
Location location = seat.getLocation();
if (seat instanceof ArmorStand) {
location.add(0, 0.9875,0);
} else {
location.add(0,0.25,0);
}
seat.remove();
EntityUtils.safeDismount(player, location);
}
public static BukkitSeatManager instance() {
return instance;
}
}

View File

@@ -6,7 +6,7 @@ import net.momirealms.craftengine.core.item.*;
import net.momirealms.craftengine.core.item.behavior.ItemBehavior; import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; import net.momirealms.craftengine.core.item.modifier.ItemDataModifier;
import net.momirealms.craftengine.core.item.updater.ItemUpdateConfig; import net.momirealms.craftengine.core.item.updater.ItemUpdateConfig;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.Context;
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.function.Function; import net.momirealms.craftengine.core.plugin.context.function.Function;
import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.Key;
@@ -26,7 +26,7 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
List<ItemBehavior> behaviors, List<ItemBehavior> behaviors,
List<ItemDataModifier<ItemStack>> modifiers, List<ItemDataModifier<ItemStack>> clientBoundModifiers, List<ItemDataModifier<ItemStack>> modifiers, List<ItemDataModifier<ItemStack>> clientBoundModifiers,
ItemSettings settings, ItemSettings settings,
Map<EventTrigger, List<Function<PlayerOptionalContext>>> events, Map<EventTrigger, List<Function<Context>>> events,
ItemUpdateConfig updater) { ItemUpdateConfig updater) {
super(isVanillaItem, id, materialKey, clientBoundMaterialKey, behaviors, modifiers, clientBoundModifiers, settings, events, updater); super(isVanillaItem, id, materialKey, clientBoundMaterialKey, behaviors, modifiers, clientBoundModifiers, settings, events, updater);
this.item = item; this.item = item;
@@ -54,11 +54,15 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
} }
public Object clientItem() { public Object clientItem() {
return clientItem; return this.clientItem;
} }
public Object item() { public Object item() {
return item; return this.item;
}
public boolean hasClientboundMaterial() {
return this.clientItem != this.item;
} }
public static Builder<ItemStack> builder(Object item, Object clientBoundItem) { public static Builder<ItemStack> builder(Object item, Object clientBoundItem) {
@@ -72,7 +76,7 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
private final Object item; private final Object item;
private Key clientBoundItemKey; private Key clientBoundItemKey;
private final Object clientBoundItem; private final Object clientBoundItem;
private final Map<EventTrigger, List<Function<PlayerOptionalContext>>> events = new EnumMap<>(EventTrigger.class); private final Map<EventTrigger, List<Function<Context>>> events = new EnumMap<>(EventTrigger.class);
private final List<ItemBehavior> behaviors = new ArrayList<>(4); private final List<ItemBehavior> behaviors = new ArrayList<>(4);
private final List<ItemDataModifier<ItemStack>> modifiers = new ArrayList<>(4); private final List<ItemDataModifier<ItemStack>> modifiers = new ArrayList<>(4);
private final List<ItemDataModifier<ItemStack>> clientBoundModifiers = new ArrayList<>(4); private final List<ItemDataModifier<ItemStack>> clientBoundModifiers = new ArrayList<>(4);
@@ -151,7 +155,7 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
} }
@Override @Override
public Builder<ItemStack> events(Map<EventTrigger, List<Function<PlayerOptionalContext>>> events) { public Builder<ItemStack> events(Map<EventTrigger, List<Function<Context>>> events) {
this.events.putAll(events); this.events.putAll(events);
return this; return this;
} }

View File

@@ -22,8 +22,10 @@ import net.momirealms.craftengine.core.item.recipe.UniqueIdItem;
import net.momirealms.craftengine.core.pack.AbstractPackManager; import net.momirealms.craftengine.core.pack.AbstractPackManager;
import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.Config;
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.GsonHelper;
import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.UniqueKey;
import net.momirealms.craftengine.core.util.VersionHelper;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
@@ -103,17 +105,6 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
Bukkit.getPluginManager().registerEvents(this.armorEventListener, this.plugin.javaPlugin()); Bukkit.getPluginManager().registerEvents(this.armorEventListener, this.plugin.javaPlugin());
} }
@Override
public Item<ItemStack> decode(FriendlyByteBuf byteBuf) {
Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(byteBuf);
return this.wrap(FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf));
}
@Override
public void encode(FriendlyByteBuf byteBuf, Item<ItemStack> item) {
FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(FastNMS.INSTANCE.constructor$FriendlyByteBuf(byteBuf), item.getItem());
}
@Override @Override
public NetworkItemHandler<ItemStack> networkItemHandler() { public NetworkItemHandler<ItemStack> networkItemHandler() {
return this.networkItemHandler; return this.networkItemHandler;
@@ -124,37 +115,25 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
} }
@Override @Override
public Item<ItemStack> s2c(Item<ItemStack> item, Player player) { public Optional<Item<ItemStack>> s2c(Item<ItemStack> item, Player player) {
if (item.isEmpty()) return item; if (item.isEmpty()) return Optional.empty();
return this.networkItemHandler.s2c(item, player).orElse(item); return this.networkItemHandler.s2c(item, player);
} }
@Override @Override
public Item<ItemStack> c2s(Item<ItemStack> item) { public Optional<Item<ItemStack>> c2s(Item<ItemStack> item) {
if (item.isEmpty()) return item; if (item.isEmpty()) return Optional.empty();
return this.networkItemHandler.c2s(item).orElse(item); return this.networkItemHandler.c2s(item);
} }
public Optional<ItemStack> s2c(ItemStack itemStack, Player player) { public Optional<ItemStack> s2c(ItemStack item, Player player) {
try { if (item.isEmpty()) return Optional.empty();
Item<ItemStack> wrapped = wrap(itemStack); return this.networkItemHandler.s2c(wrap(item), player).map(Item::getItem);
if (wrapped.isEmpty()) return Optional.empty();
return this.networkItemHandler.s2c(wrapped, player).map(Item::getItem);
} catch (Throwable e) {
Debugger.ITEM.warn(() -> "Failed to handle s2c items.", e);
return Optional.empty();
}
} }
public Optional<ItemStack> c2s(ItemStack itemStack) { public Optional<ItemStack> c2s(ItemStack item) {
try { if (item.isEmpty()) return Optional.empty();
Item<ItemStack> wrapped = wrap(itemStack); return this.networkItemHandler.c2s(wrap(item)).map(Item::getItem);
if (wrapped.isEmpty()) return Optional.empty();
return this.networkItemHandler.c2s(wrapped).map(Item::getItem);
} catch (Throwable e) {
Debugger.COMMON.warn(() -> "Failed to handle c2s items.", e);
return Optional.empty();
}
} }
@Override @Override
@@ -169,7 +148,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
jsonObject.addProperty("count", result.count()); jsonObject.addProperty("count", result.count());
jsonObject.add("components", result.components()); jsonObject.add("components", result.components());
Object nmsStack = CoreReflections.instance$ItemStack$CODEC.parse(MRegistryOps.JSON, jsonObject) Object nmsStack = CoreReflections.instance$ItemStack$CODEC.parse(MRegistryOps.JSON, jsonObject)
.resultOrPartial((itemId) -> plugin.logger().severe("Tried to load invalid item: '" + itemId + "'")).orElse(null); .resultOrPartial((error) -> plugin.logger().severe("Tried to load invalid item: '" + error + "'")).orElse(null);
if (nmsStack == null) { if (nmsStack == null) {
return this.emptyItem; return this.emptyItem;
} }
@@ -429,7 +408,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
Object armorTrim = FastNMS.INSTANCE.constructor$ArmorTrim(optionalMaterial.get(), optionalPattern.get()); Object armorTrim = FastNMS.INSTANCE.constructor$ArmorTrim(optionalMaterial.get(), optionalPattern.get());
Object previousTrim; Object previousTrim;
if (VersionHelper.isOrAbove1_20_5()) { if (VersionHelper.isOrAbove1_20_5()) {
previousTrim = base.getExactComponent(ComponentKeys.TRIM); previousTrim = base.getExactComponent(DataComponentKeys.TRIM);
} else { } else {
try { try {
previousTrim = VersionHelper.isOrAbove1_20_2() ? previousTrim = VersionHelper.isOrAbove1_20_2() ?
@@ -445,7 +424,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
} }
Item<ItemStack> newItem = base.copyWithCount(1); Item<ItemStack> newItem = base.copyWithCount(1);
if (VersionHelper.isOrAbove1_20_5()) { if (VersionHelper.isOrAbove1_20_5()) {
newItem.setExactComponent(ComponentKeys.TRIM, armorTrim); newItem.setExactComponent(DataComponentKeys.TRIM, armorTrim);
} else { } else {
try { try {
CoreReflections.method$ArmorTrim$setTrim.invoke(null, FastNMS.INSTANCE.registryAccess(), newItem.getLiteralObject(), armorTrim); CoreReflections.method$ArmorTrim$setTrim.invoke(null, FastNMS.INSTANCE.registryAccess(), newItem.getLiteralObject(), armorTrim);

View File

@@ -8,13 +8,19 @@ 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.MBuiltInRegistries; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistryOps; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistryOps;
import net.momirealms.craftengine.bukkit.util.EquipmentSlotUtils;
import net.momirealms.craftengine.bukkit.util.ItemStackUtils; import net.momirealms.craftengine.bukkit.util.ItemStackUtils;
import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.core.entity.EquipmentSlot;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.ItemWrapper; import net.momirealms.craftengine.core.item.ItemWrapper;
import net.momirealms.craftengine.core.util.Key; import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.RandomUtils;
import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.util.VersionHelper;
import net.momirealms.sparrow.nbt.Tag; import net.momirealms.sparrow.nbt.Tag;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
@@ -23,6 +29,11 @@ public class ComponentItemWrapper implements ItemWrapper<ItemStack> {
private final ItemStack item; private final ItemStack item;
private final Object handle; private final Object handle;
public ComponentItemWrapper(final Object handle) {
this.handle = handle;
this.item = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(handle);
}
public ComponentItemWrapper(final ItemStack item) { public ComponentItemWrapper(final ItemStack item) {
this.item = ItemStackUtils.ensureCraftItemStack(item); this.item = ItemStackUtils.ensureCraftItemStack(item);
this.handle = FastNMS.INSTANCE.field$CraftItemStack$handle(this.item); this.handle = FastNMS.INSTANCE.field$CraftItemStack$handle(this.item);
@@ -131,7 +142,7 @@ public class ComponentItemWrapper implements ItemWrapper<ItemStack> {
} }
public void setNBTComponent(Object type, Object value) { public void setNBTComponent(Object type, Object value) {
setComponentInternal(type, MRegistryOps.NBT, value); setComponentInternal(type, MRegistryOps.NBT, value);
} }
public void setSparrowNBTComponent(Object type, Tag value) { public void setSparrowNBTComponent(Object type, Tag value) {
@@ -196,4 +207,39 @@ public class ComponentItemWrapper implements ItemWrapper<ItemStack> {
public void shrink(int amount) { public void shrink(int amount) {
count(count() - amount); count(count() - amount);
} }
@Override
public void hurtAndBreak(int amount, @Nullable Player player, @Nullable EquipmentSlot slot) {
if (player == null) {
if (this.hurt(amount)) {
this.shrink(1);
this.setJavaComponent(DataComponentTypes.DAMAGE, 0);
}
return;
}
FastNMS.INSTANCE.method$ItemStack$hurtAndBreak(
this.handle,
amount,
player.serverPlayer(),
slot != null ? EquipmentSlotUtils.toNMSEquipmentSlot(slot) : null
);
}
private boolean hurt(int amount) {
if (!this.hasComponent(DataComponentTypes.MAX_DAMAGE) || this.hasComponent(DataComponentTypes.UNBREAKABLE) || !this.hasComponent(DataComponentTypes.DAMAGE)) return false;
if (amount > 0) {
int level = this.item.getEnchantmentLevel(Enchantment.UNBREAKING);
int ignoredDamage = 0;
for (int i = 0; level > 0 && i < amount; ++i) {
if (RandomUtils.generateRandomInt(0, level + 1) > 0) ++ignoredDamage;
}
amount -= ignoredDamage;
if (amount <= 0) return false;
}
Optional<Integer> optionalDamage = this.getJavaComponent(DataComponentTypes.DAMAGE);
int damage = optionalDamage.orElse(0) + amount;
this.setJavaComponent(DataComponentTypes.DAMAGE, damage);
Optional<Integer> optionalMaxDamage = this.getJavaComponent(DataComponentTypes.MAX_DAMAGE);
return damage >= optionalMaxDamage.orElseGet(() -> (int) this.item.getType().getMaxDurability());
}
} }

View File

@@ -1,40 +0,0 @@
package net.momirealms.craftengine.bukkit.item;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.core.item.ComponentKeys;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.VersionHelper;
public class ComponentTypes {
public static final Object CUSTOM_MODEL_DATA = getComponentType(ComponentKeys.CUSTOM_MODEL_DATA);
public static final Object CUSTOM_NAME = getComponentType(ComponentKeys.CUSTOM_NAME);
public static final Object ITEM_NAME = getComponentType(ComponentKeys.ITEM_NAME);
public static final Object LORE = getComponentType(ComponentKeys.LORE);
public static final Object DAMAGE = getComponentType(ComponentKeys.DAMAGE);
public static final Object MAX_DAMAGE = getComponentType(ComponentKeys.MAX_DAMAGE);
public static final Object ENCHANTMENT_GLINT_OVERRIDE = getComponentType(ComponentKeys.ENCHANTMENT_GLINT_OVERRIDE);
public static final Object ENCHANTMENTS = getComponentType(ComponentKeys.ENCHANTMENTS);
public static final Object STORED_ENCHANTMENTS = getComponentType(ComponentKeys.STORED_ENCHANTMENTS);
public static final Object UNBREAKABLE = getComponentType(ComponentKeys.UNBREAKABLE);
public static final Object MAX_STACK_SIZE = getComponentType(ComponentKeys.MAX_STACK_SIZE);
public static final Object EQUIPPABLE = getComponentType(ComponentKeys.EQUIPPABLE);
public static final Object ITEM_MODEL = getComponentType(ComponentKeys.ITEM_MODEL);
public static final Object TOOLTIP_STYLE = getComponentType(ComponentKeys.TOOLTIP_STYLE);
public static final Object JUKEBOX_PLAYABLE = getComponentType(ComponentKeys.JUKEBOX_PLAYABLE);
public static final Object TRIM = getComponentType(ComponentKeys.TRIM);
public static final Object REPAIR_COST = getComponentType(ComponentKeys.REPAIR_COST);
public static final Object CUSTOM_DATA = getComponentType(ComponentKeys.CUSTOM_DATA);
public static final Object PROFILE = getComponentType(ComponentKeys.PROFILE);
public static final Object DYED_COLOR = getComponentType(ComponentKeys.DYED_COLOR);
public static final Object DEATH_PROTECTION = getComponentType(ComponentKeys.DEATH_PROTECTION);
public static final Object FIREWORK_EXPLOSION = getComponentType(ComponentKeys.FIREWORK_EXPLOSION);
private ComponentTypes() {}
private static Object getComponentType(Key key) {
if (!VersionHelper.isOrAbove1_20_5()) return null;
return FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.DATA_COMPONENT_TYPE, KeyUtils.toResourceLocation(key));
}
}

View File

@@ -0,0 +1,16 @@
package net.momirealms.craftengine.bukkit.item;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.VersionHelper;
public final class DataComponentPredicateTypes {
private DataComponentPredicateTypes() {}
public static Object byId(Key key) {
if (!VersionHelper.isOrAbove1_21_5()) return null;
return FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.DATA_COMPONENT_PREDICATE_TYPE, KeyUtils.toResourceLocation(key));
}
}

View File

@@ -0,0 +1,43 @@
package net.momirealms.craftengine.bukkit.item;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries;
import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.core.item.DataComponentKeys;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.VersionHelper;
public final class DataComponentTypes {
public static final Object CUSTOM_MODEL_DATA = byId(DataComponentKeys.CUSTOM_MODEL_DATA);
public static final Object CUSTOM_NAME = byId(DataComponentKeys.CUSTOM_NAME);
public static final Object ITEM_NAME = byId(DataComponentKeys.ITEM_NAME);
public static final Object LORE = byId(DataComponentKeys.LORE);
public static final Object DAMAGE = byId(DataComponentKeys.DAMAGE);
public static final Object MAX_DAMAGE = byId(DataComponentKeys.MAX_DAMAGE);
public static final Object ENCHANTMENT_GLINT_OVERRIDE = byId(DataComponentKeys.ENCHANTMENT_GLINT_OVERRIDE);
public static final Object ENCHANTMENTS = byId(DataComponentKeys.ENCHANTMENTS);
public static final Object STORED_ENCHANTMENTS = byId(DataComponentKeys.STORED_ENCHANTMENTS);
public static final Object UNBREAKABLE = byId(DataComponentKeys.UNBREAKABLE);
public static final Object MAX_STACK_SIZE = byId(DataComponentKeys.MAX_STACK_SIZE);
public static final Object EQUIPPABLE = byId(DataComponentKeys.EQUIPPABLE);
public static final Object ITEM_MODEL = byId(DataComponentKeys.ITEM_MODEL);
public static final Object TOOLTIP_STYLE = byId(DataComponentKeys.TOOLTIP_STYLE);
public static final Object JUKEBOX_PLAYABLE = byId(DataComponentKeys.JUKEBOX_PLAYABLE);
public static final Object TRIM = byId(DataComponentKeys.TRIM);
public static final Object REPAIR_COST = byId(DataComponentKeys.REPAIR_COST);
public static final Object CUSTOM_DATA = byId(DataComponentKeys.CUSTOM_DATA);
public static final Object PROFILE = byId(DataComponentKeys.PROFILE);
public static final Object DYED_COLOR = byId(DataComponentKeys.DYED_COLOR);
public static final Object DEATH_PROTECTION = byId(DataComponentKeys.DEATH_PROTECTION);
public static final Object FIREWORK_EXPLOSION = byId(DataComponentKeys.FIREWORK_EXPLOSION);
public static final Object BUNDLE_CONTENTS = byId(DataComponentKeys.BUNDLE_CONTENTS);
public static final Object CONTAINER = byId(DataComponentKeys.CONTAINER);
public static final Object BLOCK_STATE = byId(DataComponentKeys.BLOCK_STATE);
private DataComponentTypes() {}
public static Object byId(Key key) {
if (!VersionHelper.isOrAbove1_20_5()) return null;
return FastNMS.INSTANCE.method$Registry$getValue(MBuiltInRegistries.DATA_COMPONENT_TYPE, KeyUtils.toResourceLocation(key));
}
}

View File

@@ -3,10 +3,16 @@ package net.momirealms.craftengine.bukkit.item;
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.MRegistryOps; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistryOps;
import net.momirealms.craftengine.bukkit.util.EquipmentSlotUtils;
import net.momirealms.craftengine.bukkit.util.ItemStackUtils; import net.momirealms.craftengine.bukkit.util.ItemStackUtils;
import net.momirealms.craftengine.core.entity.EquipmentSlot;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.item.ItemWrapper; import net.momirealms.craftengine.core.item.ItemWrapper;
import net.momirealms.craftengine.core.util.RandomUtils;
import net.momirealms.sparrow.nbt.Tag; import net.momirealms.sparrow.nbt.Tag;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
public class LegacyItemWrapper implements ItemWrapper<ItemStack> { public class LegacyItemWrapper implements ItemWrapper<ItemStack> {
private final Object nmsStack; private final Object nmsStack;
@@ -21,11 +27,12 @@ public class LegacyItemWrapper implements ItemWrapper<ItemStack> {
Object finalNMSTag; Object finalNMSTag;
if (value instanceof Tag tag) { if (value instanceof Tag tag) {
finalNMSTag = MRegistryOps.SPARROW_NBT.convertTo(MRegistryOps.NBT, tag); finalNMSTag = MRegistryOps.SPARROW_NBT.convertTo(MRegistryOps.NBT, tag);
} else if (CoreReflections.clazz$Tag.isInstance(value)) {
finalNMSTag = value;
} else { } else {
finalNMSTag = MRegistryOps.JAVA.convertTo(MRegistryOps.NBT, value); finalNMSTag = MRegistryOps.JAVA.convertTo(MRegistryOps.NBT, value);
} }
Object currentTag = FastNMS.INSTANCE.field$ItemStack$getOrCreateTag(this.nmsStack);
if (path == null || path.length == 0) { if (path == null || path.length == 0) {
if (CoreReflections.clazz$CompoundTag.isInstance(finalNMSTag)) { if (CoreReflections.clazz$CompoundTag.isInstance(finalNMSTag)) {
FastNMS.INSTANCE.method$ItemStack$setTag(this.nmsStack, finalNMSTag); FastNMS.INSTANCE.method$ItemStack$setTag(this.nmsStack, finalNMSTag);
@@ -34,6 +41,8 @@ public class LegacyItemWrapper implements ItemWrapper<ItemStack> {
return false; return false;
} }
Object currentTag = FastNMS.INSTANCE.field$ItemStack$getOrCreateTag(this.nmsStack);
for (int i = 0; i < path.length - 1; i++) { for (int i = 0; i < path.length - 1; i++) {
Object pathSegment = path[i]; Object pathSegment = path[i];
if (pathSegment == null) return false; if (pathSegment == null) return false;
@@ -147,4 +156,38 @@ public class LegacyItemWrapper implements ItemWrapper<ItemStack> {
public void shrink(int amount) { public void shrink(int amount) {
this.count(count() - amount); this.count(count() - amount);
} }
@Override
public void hurtAndBreak(int amount, @Nullable Player player, @Nullable EquipmentSlot slot) {
if (player == null) {
if (this.hurt(amount)) {
this.shrink(1);
this.setTag(0, "Damage");
}
return;
}
FastNMS.INSTANCE.method$ItemStack$hurtAndBreak(
this.nmsStack,
amount,
player.serverPlayer(),
slot != null ? EquipmentSlotUtils.toNMSEquipmentSlot(slot) : null
);
}
private boolean hurt(int amount) {
if (ItemStackUtils.isEmpty(itemStack) || itemStack.getType().getMaxDurability() <= 0 || !hasTag("Unbreakable") || (boolean) getJavaTag("Unbreakable")) return false;
if (amount > 0) {
int level = this.itemStack.getEnchantmentLevel(Enchantment.UNBREAKING);
int ignoredDamage = 0;
for (int i = 0; level > 0 && i < amount; ++i) {
if (RandomUtils.generateRandomInt(0, level + 1) > 0) ++ignoredDamage;
}
amount -= ignoredDamage;
if (amount <= 0) return false;
}
int damage = this.hasTag("Damage") ? this.getJavaTag("Damage") : 0;
damage += amount;
this.setTag(damage, "Damage");
return damage >= this.itemStack.getType().getMaxDurability();
}
} }

View File

@@ -4,7 +4,7 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.entity.player.Player;
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.NetworkItemBuildContext;
import net.momirealms.craftengine.core.item.NetworkItemHandler; import net.momirealms.craftengine.core.item.NetworkItemHandler;
import net.momirealms.craftengine.core.item.modifier.ArgumentsModifier; import net.momirealms.craftengine.core.item.modifier.ArgumentsModifier;
import net.momirealms.craftengine.core.item.modifier.ItemDataModifier; import net.momirealms.craftengine.core.item.modifier.ItemDataModifier;
@@ -16,6 +16,7 @@ import net.momirealms.craftengine.core.plugin.context.ContextKey;
import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext; import net.momirealms.craftengine.core.plugin.context.NetworkTextReplaceContext;
import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider; import net.momirealms.craftengine.core.plugin.text.component.ComponentProvider;
import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.AdventureHelper;
import net.momirealms.craftengine.core.util.Pair;
import net.momirealms.sparrow.nbt.CompoundTag; import net.momirealms.sparrow.nbt.CompoundTag;
import net.momirealms.sparrow.nbt.ListTag; import net.momirealms.sparrow.nbt.ListTag;
import net.momirealms.sparrow.nbt.StringTag; import net.momirealms.sparrow.nbt.StringTag;
@@ -33,84 +34,214 @@ public final class LegacyNetworkItemHandler implements NetworkItemHandler<ItemSt
@Override @Override
public Optional<Item<ItemStack>> c2s(Item<ItemStack> wrapped) { public Optional<Item<ItemStack>> c2s(Item<ItemStack> wrapped) {
boolean forceReturn = false;
// 处理收纳袋
Object bundleContents = wrapped.getExactTag("Items");
if (bundleContents != null) {
List<Object> newItems = new ArrayList<>();
boolean changed = false;
for (Object tag : (Iterable<?>) bundleContents) {
Object previousItem = FastNMS.INSTANCE.method$ItemStack$of(tag);
Optional<ItemStack> itemStack = BukkitItemManager.instance().c2s(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(previousItem));
if (itemStack.isPresent()) {
newItems.add(FastNMS.INSTANCE.field$CraftItemStack$handle(itemStack.get()));
changed = true;
} else {
newItems.add(previousItem);
}
}
if (changed) {
Object listTag = FastNMS.INSTANCE.constructor$ListTag();
for (Object newItem : newItems) {
FastNMS.INSTANCE.method$ListTag$add(listTag, 0, FastNMS.INSTANCE.method$itemStack$save(newItem, FastNMS.INSTANCE.constructor$CompoundTag()));
}
wrapped.setTag(listTag, "Items");
forceReturn = true;
}
}
// 处理container
Object containerContents = wrapped.getExactTag("BlockEntityTag");
if (containerContents != null) {
Object itemTags = FastNMS.INSTANCE.method$CompoundTag$get(containerContents, "Items");
if (itemTags != null) {
boolean changed = false;
List<Pair<Byte, Object>> newItems = new ArrayList<>();
for (Object tag : (Iterable<?>) itemTags) {
Object previousItem = FastNMS.INSTANCE.method$ItemStack$of(tag);
Optional<ItemStack> itemStack = BukkitItemManager.instance().c2s(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(previousItem));
byte slot = FastNMS.INSTANCE.method$ByteTag$value(FastNMS.INSTANCE.method$CompoundTag$get(tag, "Slot"));
if (itemStack.isPresent()) {
newItems.add(Pair.of(slot, FastNMS.INSTANCE.field$CraftItemStack$handle(itemStack.get())));
changed = true;
} else {
newItems.add(Pair.of(slot, previousItem));
}
}
if (changed) {
Object listTag = FastNMS.INSTANCE.constructor$ListTag();
for (Pair<Byte, Object> newItem : newItems) {
Object newTag = FastNMS.INSTANCE.method$itemStack$save(newItem.right(), FastNMS.INSTANCE.constructor$CompoundTag());
Object slotTag = FastNMS.INSTANCE.constructor$ByteTag(newItem.left());
FastNMS.INSTANCE.method$CompoundTag$put(newTag, "Slot", slotTag);
FastNMS.INSTANCE.method$ListTag$add(listTag, 0, newTag);
}
wrapped.setTag(listTag, "BlockEntityTag", "Items");
forceReturn = true;
}
}
}
Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem(); Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
boolean hasDifferentMaterial = false;
if (optionalCustomItem.isPresent()) { if (optionalCustomItem.isPresent()) {
BukkitCustomItem customItem = (BukkitCustomItem) optionalCustomItem.get(); BukkitCustomItem customItem = (BukkitCustomItem) optionalCustomItem.get();
if (customItem.item() != FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject())) { if (customItem.item() != FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject())) {
wrapped = wrapped.unsafeTransmuteCopy(customItem.item(), wrapped.count()); wrapped = wrapped.unsafeTransmuteCopy(customItem.item(), wrapped.count());
hasDifferentMaterial = true; forceReturn = true;
}
}
if (!wrapped.hasTag(NETWORK_ITEM_TAG)) {
if (hasDifferentMaterial) {
return Optional.of(wrapped);
} }
} }
CompoundTag networkData = (CompoundTag) wrapped.getTag(NETWORK_ITEM_TAG); CompoundTag networkData = (CompoundTag) wrapped.getTag(NETWORK_ITEM_TAG);
if (networkData == null) return Optional.empty(); if (networkData != null) {
wrapped.removeTag(NETWORK_ITEM_TAG); forceReturn = true;
for (Map.Entry<String, Tag> entry : networkData.entrySet()) { // 移除tag
if (entry.getValue() instanceof CompoundTag tag) { wrapped.removeTag(NETWORK_ITEM_TAG);
NetworkItemHandler.apply(entry.getKey(), tag, wrapped); // 恢复物品
for (Map.Entry<String, Tag> entry : networkData.entrySet()) {
if (entry.getValue() instanceof CompoundTag tag) {
NetworkItemHandler.apply(entry.getKey(), tag, wrapped);
}
} }
} }
return Optional.of(wrapped);
return forceReturn ? Optional.of(wrapped) : Optional.empty();
} }
@Override @Override
public Optional<Item<ItemStack>> s2c(Item<ItemStack> wrapped, Player player) { public Optional<Item<ItemStack>> s2c(Item<ItemStack> wrapped, Player player) {
Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem(); boolean forceReturn = false;
if (optionalCustomItem.isEmpty()) {
if (!Config.interceptItem()) return Optional.empty(); // 处理收纳袋
return new OtherItem(wrapped, false).process(NetworkTextReplaceContext.of(player)); Object bundleContents = wrapped.getExactTag("Items");
} else { if (bundleContents != null) {
BukkitCustomItem customItem = (BukkitCustomItem) optionalCustomItem.get(); List<Object> newItems = new ArrayList<>();
Object serverItem = FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject()); boolean changed = false;
boolean hasDifferentMaterial = serverItem == customItem.item() && serverItem != customItem.clientItem(); for (Object tag : (Iterable<?>) bundleContents) {
if (hasDifferentMaterial) { Object previousItem = FastNMS.INSTANCE.method$ItemStack$of(tag);
wrapped = wrapped.unsafeTransmuteCopy(customItem.clientItem(), wrapped.count()); Optional<ItemStack> itemStack = BukkitItemManager.instance().s2c(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(previousItem), player);
} if (itemStack.isPresent()) {
if (!customItem.hasClientBoundDataModifier()) { newItems.add(FastNMS.INSTANCE.field$CraftItemStack$handle(itemStack.get()));
if (!Config.interceptItem() && !hasDifferentMaterial) return Optional.empty(); changed = true;
return new OtherItem(wrapped, hasDifferentMaterial).process(NetworkTextReplaceContext.of(player));
} else {
CompoundTag tag = new CompoundTag();
Tag argumentTag = wrapped.getTag(ArgumentsModifier.ARGUMENTS_TAG);
ItemBuildContext context;
if (argumentTag instanceof CompoundTag arguments) {
ContextHolder.Builder builder = ContextHolder.builder();
for (Map.Entry<String, Tag> entry : arguments.entrySet()) {
builder.withParameter(ContextKey.direct(entry.getKey()), entry.getValue().getAsString());
}
context = ItemBuildContext.of(player, builder);
} else { } else {
context = ItemBuildContext.of(player); newItems.add(previousItem);
} }
for (ItemDataModifier<ItemStack> modifier : customItem.clientBoundDataModifiers()) { }
modifier.prepareNetworkItem(wrapped, context, tag); if (changed) {
Object listTag = FastNMS.INSTANCE.constructor$ListTag();
for (Object newItem : newItems) {
FastNMS.INSTANCE.method$ListTag$add(listTag, 0, FastNMS.INSTANCE.method$itemStack$save(newItem, FastNMS.INSTANCE.constructor$CompoundTag()));
} }
for (ItemDataModifier<ItemStack> modifier : customItem.clientBoundDataModifiers()) { wrapped.setTag(listTag, "Items");
modifier.apply(wrapped, context); forceReturn = true;
}
if (Config.interceptItem()) {
if (!tag.containsKey("display.Name")) {
processCustomName(wrapped, tag::put, context);
}
if (!tag.containsKey("display.Lore")) {
processLore(wrapped, tag::put, context);
}
}
if (tag.isEmpty()) {
if (hasDifferentMaterial) {
return Optional.of(wrapped);
}
return Optional.empty();
}
wrapped.setTag(tag, NETWORK_ITEM_TAG);
return Optional.of(wrapped);
} }
} }
// 处理container
Object containerContents = wrapped.getExactTag("BlockEntityTag");
if (containerContents != null) {
Object itemTags = FastNMS.INSTANCE.method$CompoundTag$get(containerContents, "Items");
if (itemTags != null) {
boolean changed = false;
List<Pair<Byte, Object>> newItems = new ArrayList<>();
for (Object tag : (Iterable<?>) itemTags) {
Object previousItem = FastNMS.INSTANCE.method$ItemStack$of(tag);
Optional<ItemStack> itemStack = BukkitItemManager.instance().s2c(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(previousItem), player);
byte slot = FastNMS.INSTANCE.method$ByteTag$value(FastNMS.INSTANCE.method$CompoundTag$get(tag, "Slot"));
if (itemStack.isPresent()) {
newItems.add(Pair.of(slot, FastNMS.INSTANCE.field$CraftItemStack$handle(itemStack.get())));
changed = true;
} else {
newItems.add(Pair.of(slot, previousItem));
}
}
if (changed) {
Object listTag = FastNMS.INSTANCE.constructor$ListTag();
for (Pair<Byte, Object> newItem : newItems) {
Object newTag = FastNMS.INSTANCE.method$itemStack$save(newItem.right(), FastNMS.INSTANCE.constructor$CompoundTag());
Object slotTag = FastNMS.INSTANCE.constructor$ByteTag(newItem.left());
FastNMS.INSTANCE.method$CompoundTag$put(newTag, "Slot", slotTag);
FastNMS.INSTANCE.method$ListTag$add(listTag, 0, newTag);
}
wrapped.setTag(listTag, "BlockEntityTag", "Items");
forceReturn = true;
}
}
}
// todo 处理book
Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
// 不是自定义物品或修改过的原版物品
if (optionalCustomItem.isEmpty()) {
if (!Config.interceptItem()) {
return forceReturn ? Optional.of(wrapped) : Optional.empty();
}
return new OtherItem(wrapped, forceReturn).process(NetworkTextReplaceContext.of(player));
}
// 应用 client-bound-material
BukkitCustomItem customItem = (BukkitCustomItem) optionalCustomItem.get();
if (customItem.hasClientboundMaterial() && FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject()) != customItem.clientItem()) {
wrapped = wrapped.unsafeTransmuteCopy(customItem.clientItem(), wrapped.count());
forceReturn = true;
}
// 没有客户端侧组件
if (!customItem.hasClientBoundDataModifier()) {
if (!Config.interceptItem()) {
return forceReturn ? Optional.of(wrapped) : Optional.empty();
}
return new OtherItem(wrapped, forceReturn).process(NetworkTextReplaceContext.of(player));
}
// 应用client-bound-data
CompoundTag tag = new CompoundTag();
// 创建context
Tag argumentTag = wrapped.getTag(ArgumentsModifier.ARGUMENTS_TAG);
NetworkItemBuildContext context;
if (argumentTag instanceof CompoundTag arguments) {
ContextHolder.Builder builder = ContextHolder.builder();
for (Map.Entry<String, Tag> entry : arguments.entrySet()) {
builder.withParameter(ContextKey.direct(entry.getKey()), entry.getValue().getAsString());
}
context = NetworkItemBuildContext.of(player, builder);
} else {
context = NetworkItemBuildContext.of(player);
}
// 准备阶段
for (ItemDataModifier<ItemStack> modifier : customItem.clientBoundDataModifiers()) {
modifier.prepareNetworkItem(wrapped, context, tag);
}
// 应用阶段
for (ItemDataModifier<ItemStack> modifier : customItem.clientBoundDataModifiers()) {
modifier.apply(wrapped, context);
}
// 如果拦截物品的描述名称等
if (Config.interceptItem()) {
if (!tag.containsKey("display.Name")) {
processCustomName(wrapped, tag::put, context);
}
if (!tag.containsKey("display.Lore")) {
processLore(wrapped, tag::put, context);
}
}
// 如果tag不空则需要返回
if (!tag.isEmpty()) {
wrapped.setTag(tag, NETWORK_ITEM_TAG);
forceReturn = true;
}
return forceReturn ? Optional.of(wrapped) : Optional.empty();
} }
public static boolean processCustomName(Item<ItemStack> item, BiConsumer<String, CompoundTag> callback, Context context) { public static boolean processCustomName(Item<ItemStack> item, BiConsumer<String, CompoundTag> callback, Context context) {

View File

@@ -31,95 +31,201 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
@Override @Override
public Optional<Item<ItemStack>> c2s(Item<ItemStack> wrapped) { public Optional<Item<ItemStack>> c2s(Item<ItemStack> wrapped) {
Tag customData = wrapped.getSparrowNBTComponent(ComponentTypes.CUSTOM_DATA); boolean forceReturn = false;
if (!(customData instanceof CompoundTag compoundTag)) return Optional.empty();
// 处理收纳袋
if (wrapped.hasComponent(DataComponentTypes.BUNDLE_CONTENTS)) {
Object bundleContents = wrapped.getExactComponent(DataComponentTypes.BUNDLE_CONTENTS);
List<Object> newItems = new ArrayList<>();
boolean changed = false;
for (Object previousItem : FastNMS.INSTANCE.method$BundleContents$items(bundleContents)) {
Optional<ItemStack> itemStack = BukkitItemManager.instance().c2s(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(previousItem));
if (itemStack.isPresent()) {
newItems.add(FastNMS.INSTANCE.field$CraftItemStack$handle(itemStack.get()));
changed = true;
} else {
newItems.add(previousItem);
}
}
if (changed) {
wrapped.setExactComponent(DataComponentTypes.BUNDLE_CONTENTS, FastNMS.INSTANCE.constructor$BundleContents(newItems));
forceReturn = true;
}
}
// 处理潜影盒等
if (wrapped.hasComponent(DataComponentTypes.CONTAINER)) {
Object containerContents = wrapped.getExactComponent(DataComponentTypes.CONTAINER);
List<Object> newItems = new ArrayList<>();
boolean changed = false;
for (Object previousItem : FastNMS.INSTANCE.field$ItemContainerContents$items(containerContents)) {
Optional<ItemStack> itemStack = BukkitItemManager.instance().c2s(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(previousItem));
if (itemStack.isPresent()) {
newItems.add(FastNMS.INSTANCE.field$CraftItemStack$handle(itemStack.get()));
changed = true;
} else {
newItems.add(previousItem);
}
}
if (changed) {
wrapped.setExactComponent(DataComponentTypes.CONTAINER, FastNMS.INSTANCE.method$ItemContainerContents$fromItems(newItems));
forceReturn = true;
}
}
// 先尝试恢复client-bound-material
Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem(); Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
boolean hasDifferentMaterial = false;
if (optionalCustomItem.isPresent()) { if (optionalCustomItem.isPresent()) {
BukkitCustomItem customItem = (BukkitCustomItem) optionalCustomItem.get(); BukkitCustomItem customItem = (BukkitCustomItem) optionalCustomItem.get();
if (customItem.item() != FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject())) { if (customItem.item() != FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject())) {
wrapped = wrapped.unsafeTransmuteCopy(customItem.item(), wrapped.count()); wrapped = wrapped.unsafeTransmuteCopy(customItem.item(), wrapped.count());
hasDifferentMaterial = true; forceReturn = true;
} }
} }
CompoundTag networkData = compoundTag.getCompound(NETWORK_ITEM_TAG);
if (networkData == null) { // 获取custom data
if (hasDifferentMaterial) { Tag customData = wrapped.getSparrowNBTComponent(DataComponentTypes.CUSTOM_DATA);
return Optional.of(wrapped); if (customData instanceof CompoundTag compoundTag) {
} CompoundTag networkData = compoundTag.getCompound(NETWORK_ITEM_TAG);
return Optional.empty(); if (networkData != null) {
} forceReturn = true;
compoundTag.remove(NETWORK_ITEM_TAG); // 移除此tag
for (Map.Entry<String, Tag> entry : networkData.entrySet()) { compoundTag.remove(NETWORK_ITEM_TAG);
if (entry.getValue() instanceof CompoundTag tag) {
NetworkItemHandler.apply(entry.getKey(), tag, wrapped); // 恢复物品
for (Map.Entry<String, Tag> entry : networkData.entrySet()) {
if (entry.getValue() instanceof CompoundTag tag) {
NetworkItemHandler.apply(entry.getKey(), tag, wrapped);
}
}
// 如果清空了,则直接移除这个组件
if (compoundTag.isEmpty()) wrapped.resetComponent(DataComponentTypes.CUSTOM_DATA);
// 否则设置为新的
else wrapped.setNBTComponent(DataComponentTypes.CUSTOM_DATA, compoundTag);
} }
} }
if (compoundTag.isEmpty()) wrapped.resetComponent(ComponentTypes.CUSTOM_DATA);
else wrapped.setNBTComponent(ComponentTypes.CUSTOM_DATA, compoundTag); return forceReturn ? Optional.of(wrapped) : Optional.empty();
return Optional.of(wrapped);
} }
@Override @Override
public Optional<Item<ItemStack>> s2c(Item<ItemStack> wrapped, Player player) { public Optional<Item<ItemStack>> s2c(Item<ItemStack> wrapped, Player player) {
Item<ItemStack> original = wrapped; boolean forceReturn = false;
Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
if (optionalCustomItem.isEmpty()) { // 处理收纳袋
if (!Config.interceptItem()) return Optional.empty(); if (wrapped.hasComponent(DataComponentTypes.BUNDLE_CONTENTS)) {
return new OtherItem(wrapped, false).process(NetworkTextReplaceContext.of(player)); Object bundleContents = wrapped.getExactComponent(DataComponentTypes.BUNDLE_CONTENTS);
} else { List<Object> newItems = new ArrayList<>();
BukkitCustomItem customItem = (BukkitCustomItem) optionalCustomItem.get(); boolean changed = false;
Object serverItem = FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject()); for (Object previousItem : FastNMS.INSTANCE.method$BundleContents$items(bundleContents)) {
boolean hasDifferentMaterial = serverItem == customItem.item() && serverItem != customItem.clientItem(); Optional<ItemStack> itemStack = BukkitItemManager.instance().s2c(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(previousItem), player);
if (hasDifferentMaterial) { if (itemStack.isPresent()) {
wrapped = wrapped.unsafeTransmuteCopy(customItem.clientItem(), wrapped.count()); newItems.add(FastNMS.INSTANCE.field$CraftItemStack$handle(itemStack.get()));
} changed = true;
if (!customItem.hasClientBoundDataModifier()) {
if (!Config.interceptItem() && !hasDifferentMaterial) return Optional.empty();
return new OtherItem(wrapped, hasDifferentMaterial).process(NetworkTextReplaceContext.of(player));
} else {
CompoundTag customData = Optional.ofNullable(wrapped.getSparrowNBTComponent(ComponentTypes.CUSTOM_DATA)).map(CompoundTag.class::cast).orElse(new CompoundTag());
CompoundTag arguments = customData.getCompound(ArgumentsModifier.ARGUMENTS_TAG);
ItemBuildContext context;
if (arguments == null) {
context = ItemBuildContext.of(player);
} else { } else {
ContextHolder.Builder builder = ContextHolder.builder(); newItems.add(previousItem);
for (Map.Entry<String, Tag> entry : arguments.entrySet()) {
builder.withParameter(ContextKey.direct(entry.getKey()), entry.getValue().getAsString());
}
context = ItemBuildContext.of(player, builder);
} }
CompoundTag tag = new CompoundTag(); }
for (ItemDataModifier<ItemStack> modifier : customItem.clientBoundDataModifiers()) { if (changed) {
modifier.prepareNetworkItem(original, context, tag); wrapped.setExactComponent(DataComponentTypes.BUNDLE_CONTENTS, FastNMS.INSTANCE.constructor$BundleContents(newItems));
} forceReturn = true;
for (ItemDataModifier<ItemStack> modifier : customItem.clientBoundDataModifiers()) {
modifier.apply(wrapped, context);
}
if (Config.interceptItem()) {
if (!tag.containsKey(ComponentIds.ITEM_NAME)) {
if (VersionHelper.isOrAbove1_21_5()) processModernItemName(wrapped, () -> tag, context);
else processLegacyItemName(wrapped, () -> tag, context);
}
if (!tag.containsKey(ComponentIds.CUSTOM_NAME)) {
if (VersionHelper.isOrAbove1_21_5()) processModernCustomName(wrapped, () -> tag, context);
else processLegacyCustomName(wrapped, () -> tag, context);
}
if (!tag.containsKey(ComponentIds.LORE)) {
if (VersionHelper.isOrAbove1_21_5()) processModernLore(wrapped, () -> tag, context);
else processLegacyLore(wrapped, () -> tag, context);
}
}
if (tag.isEmpty()) {
if (hasDifferentMaterial) return Optional.of(wrapped);
return Optional.empty();
}
customData.put(NETWORK_ITEM_TAG, tag);
wrapped.setNBTComponent(ComponentTypes.CUSTOM_DATA, customData);
return Optional.of(wrapped);
} }
} }
// 处理潜影盒等
if (wrapped.hasComponent(DataComponentTypes.CONTAINER)) {
Object containerContents = wrapped.getExactComponent(DataComponentTypes.CONTAINER);
List<Object> newItems = new ArrayList<>();
for (Object previousItem : FastNMS.INSTANCE.field$ItemContainerContents$items(containerContents)) {
boolean changed = false;
Optional<ItemStack> itemStack = BukkitItemManager.instance().s2c(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(previousItem), player);
if (itemStack.isPresent()) {
newItems.add(FastNMS.INSTANCE.field$CraftItemStack$handle(itemStack.get()));
changed = true;
} else {
newItems.add(previousItem);
}
if (changed) {
wrapped.setExactComponent(DataComponentTypes.CONTAINER, FastNMS.INSTANCE.method$ItemContainerContents$fromItems(newItems));
forceReturn = true;
}
}
}
// todo 处理book
// 不是自定义物品或修改过的原版物品
Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
if (optionalCustomItem.isEmpty()) {
if (!Config.interceptItem()) {
return forceReturn ? Optional.of(wrapped) : Optional.empty();
}
return new OtherItem(wrapped, forceReturn).process(NetworkTextReplaceContext.of(player));
}
BukkitCustomItem customItem = (BukkitCustomItem) optionalCustomItem.get();
// 提前复制,这和物品类型相关
Item<ItemStack> original = wrapped;
// 应用 client-bound-material前提是服务端侧物品类型和客户端侧的不同
if (customItem.hasClientboundMaterial() && FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject()) != customItem.clientItem()) {
wrapped = wrapped.unsafeTransmuteCopy(customItem.clientItem(), wrapped.count());
forceReturn = true;
}
// 没有 client-bound-data
if (!customItem.hasClientBoundDataModifier()) {
if (!Config.interceptItem()) {
return forceReturn ? Optional.of(wrapped) : Optional.empty();
}
return new OtherItem(wrapped, forceReturn).process(NetworkTextReplaceContext.of(player));
}
// 获取custom data
CompoundTag customData = Optional.ofNullable(wrapped.getSparrowNBTComponent(DataComponentTypes.CUSTOM_DATA))
.map(CompoundTag.class::cast)
.orElseGet(CompoundTag::new);
CompoundTag arguments = customData.getCompound(ArgumentsModifier.ARGUMENTS_TAG);
// 创建context
NetworkItemBuildContext context;
if (arguments == null) {
context = NetworkItemBuildContext.of(player);
} else {
ContextHolder.Builder builder = ContextHolder.builder();
for (Map.Entry<String, Tag> entry : arguments.entrySet()) {
builder.withParameter(ContextKey.direct(entry.getKey()), entry.getValue().getAsString());
}
context = NetworkItemBuildContext.of(player, builder);
}
// 准备阶段
CompoundTag tag = new CompoundTag();
for (ItemDataModifier<ItemStack> modifier : customItem.clientBoundDataModifiers()) {
modifier.prepareNetworkItem(original, context, tag);
}
// 应用阶段
for (ItemDataModifier<ItemStack> modifier : customItem.clientBoundDataModifiers()) {
modifier.apply(wrapped, context);
}
// 如果拦截物品的描述名称等
if (Config.interceptItem()) {
if (!tag.containsKey(DataComponentIds.ITEM_NAME)) {
if (VersionHelper.isOrAbove1_21_5()) processModernItemName(wrapped, () -> tag, context);
else processLegacyItemName(wrapped, () -> tag, context);
}
if (!tag.containsKey(DataComponentIds.CUSTOM_NAME)) {
if (VersionHelper.isOrAbove1_21_5()) processModernCustomName(wrapped, () -> tag, context);
else processLegacyCustomName(wrapped, () -> tag, context);
}
if (!tag.containsKey(DataComponentIds.LORE)) {
if (VersionHelper.isOrAbove1_21_5()) processModernLore(wrapped, () -> tag, context);
else processLegacyLore(wrapped, () -> tag, context);
}
}
// 如果tag不空则需要返回
if (!tag.isEmpty()) {
customData.put(NETWORK_ITEM_TAG, tag);
wrapped.setNBTComponent(DataComponentTypes.CUSTOM_DATA, customData);
forceReturn = true;
}
return forceReturn ? Optional.of(wrapped) : Optional.empty();
} }
public static boolean processLegacyLore(Item<ItemStack> item, Supplier<CompoundTag> tag, Context context) { public static boolean processLegacyLore(Item<ItemStack> item, Supplier<CompoundTag> tag, Context context) {
@@ -143,7 +249,7 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
for (String line : lore) { for (String line : lore) {
listTag.add(new StringTag(line)); listTag.add(new StringTag(line));
} }
tag.get().put(ComponentIds.LORE, NetworkItemHandler.pack(Operation.ADD, listTag)); tag.get().put(DataComponentIds.LORE, NetworkItemHandler.pack(Operation.ADD, listTag));
return true; return true;
} }
} }
@@ -157,7 +263,7 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(line); Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(line);
if (!tokens.isEmpty()) { if (!tokens.isEmpty()) {
item.customNameJson(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens, context))); item.customNameJson(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens, context)));
tag.get().put(ComponentIds.CUSTOM_NAME, NetworkItemHandler.pack(Operation.ADD, new StringTag(line))); tag.get().put(DataComponentIds.CUSTOM_NAME, NetworkItemHandler.pack(Operation.ADD, new StringTag(line)));
return true; return true;
} }
} }
@@ -171,7 +277,7 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(line); Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(line);
if (!tokens.isEmpty()) { if (!tokens.isEmpty()) {
item.itemNameJson(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens, context))); item.itemNameJson(AdventureHelper.componentToJson(AdventureHelper.replaceText(AdventureHelper.jsonToComponent(line), tokens, context)));
tag.get().put(ComponentIds.ITEM_NAME, NetworkItemHandler.pack(Operation.ADD, new StringTag(line))); tag.get().put(DataComponentIds.ITEM_NAME, NetworkItemHandler.pack(Operation.ADD, new StringTag(line)));
return true; return true;
} }
} }
@@ -179,41 +285,38 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
} }
public static boolean processModernItemName(Item<ItemStack> item, Supplier<CompoundTag> tag, Context context) { public static boolean processModernItemName(Item<ItemStack> item, Supplier<CompoundTag> tag, Context context) {
Tag nameTag = item.getSparrowNBTComponent(ComponentTypes.ITEM_NAME); Tag nameTag = item.getSparrowNBTComponent(DataComponentTypes.ITEM_NAME);
if (nameTag == null) return false; if (nameTag == null) return false;
String tagStr = nameTag.getAsString(); Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(nameTag);
Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(tagStr);
if (!tokens.isEmpty()) { if (!tokens.isEmpty()) {
item.setNBTComponent(ComponentKeys.ITEM_NAME, AdventureHelper.componentToNbt(AdventureHelper.replaceText(AdventureHelper.nbtToComponent(nameTag), tokens, context))); item.setNBTComponent(DataComponentKeys.ITEM_NAME, AdventureHelper.componentToNbt(AdventureHelper.replaceText(AdventureHelper.nbtToComponent(nameTag), tokens, context)));
tag.get().put(ComponentIds.ITEM_NAME, NetworkItemHandler.pack(Operation.ADD, nameTag)); tag.get().put(DataComponentIds.ITEM_NAME, NetworkItemHandler.pack(Operation.ADD, nameTag));
return true; return true;
} }
return false; return false;
} }
public static boolean processModernCustomName(Item<ItemStack> item, Supplier<CompoundTag> tag, Context context) { public static boolean processModernCustomName(Item<ItemStack> item, Supplier<CompoundTag> tag, Context context) {
Tag nameTag = item.getSparrowNBTComponent(ComponentTypes.CUSTOM_NAME); Tag nameTag = item.getSparrowNBTComponent(DataComponentTypes.CUSTOM_NAME);
if (nameTag == null) return false; if (nameTag == null) return false;
String tagStr = nameTag.getAsString(); Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(nameTag);
Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(tagStr);
if (!tokens.isEmpty()) { if (!tokens.isEmpty()) {
item.setNBTComponent(ComponentKeys.CUSTOM_NAME, AdventureHelper.componentToNbt(AdventureHelper.replaceText(AdventureHelper.nbtToComponent(nameTag), tokens, context))); item.setNBTComponent(DataComponentKeys.CUSTOM_NAME, AdventureHelper.componentToNbt(AdventureHelper.replaceText(AdventureHelper.nbtToComponent(nameTag), tokens, context)));
tag.get().put(ComponentIds.CUSTOM_NAME, NetworkItemHandler.pack(Operation.ADD, nameTag)); tag.get().put(DataComponentIds.CUSTOM_NAME, NetworkItemHandler.pack(Operation.ADD, nameTag));
return true; return true;
} }
return false; return false;
} }
public static boolean processModernLore(Item<ItemStack> item, Supplier<CompoundTag> tagSupplier, Context context) { public static boolean processModernLore(Item<ItemStack> item, Supplier<CompoundTag> tagSupplier, Context context) {
Tag loreTag = item.getSparrowNBTComponent(ComponentTypes.LORE); Tag loreTag = item.getSparrowNBTComponent(DataComponentTypes.LORE);
boolean changed = false; boolean changed = false;
if (!(loreTag instanceof ListTag listTag)) { if (!(loreTag instanceof ListTag listTag)) {
return false; return false;
} }
ListTag newLore = new ListTag(); ListTag newLore = new ListTag();
for (Tag tag : listTag) { for (Tag tag : listTag) {
String tagStr = tag.getAsString(); Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(tag);
Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(tagStr);
if (tokens.isEmpty()) { if (tokens.isEmpty()) {
newLore.add(tag); newLore.add(tag);
} else { } else {
@@ -222,8 +325,8 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
} }
} }
if (changed) { if (changed) {
item.setNBTComponent(ComponentKeys.LORE, newLore); item.setNBTComponent(DataComponentKeys.LORE, newLore);
tagSupplier.get().put(ComponentIds.LORE, NetworkItemHandler.pack(Operation.ADD, listTag)); tagSupplier.get().put(DataComponentIds.LORE, NetworkItemHandler.pack(Operation.ADD, listTag));
return true; return true;
} }
return false; return false;
@@ -257,9 +360,11 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
this.globalChanged = true; this.globalChanged = true;
} }
if (this.globalChanged) { if (this.globalChanged) {
CompoundTag customData = Optional.ofNullable(this.item.getSparrowNBTComponent(ComponentTypes.CUSTOM_DATA)).map(CompoundTag.class::cast).orElse(new CompoundTag()); CompoundTag customData = Optional.ofNullable(this.item.getSparrowNBTComponent(DataComponentTypes.CUSTOM_DATA))
.map(CompoundTag.class::cast)
.orElseGet(CompoundTag::new);
customData.put(NETWORK_ITEM_TAG, getOrCreateTag()); customData.put(NETWORK_ITEM_TAG, getOrCreateTag());
this.item.setNBTComponent(ComponentKeys.CUSTOM_DATA, customData); this.item.setNBTComponent(DataComponentKeys.CUSTOM_DATA, customData);
return Optional.of(this.item); return Optional.of(this.item);
} else if (this.forceReturn) { } else if (this.forceReturn) {
return Optional.of(this.item); return Optional.of(this.item);

View File

@@ -3,12 +3,12 @@ package net.momirealms.craftengine.bukkit.item.behavior;
import net.momirealms.craftengine.bukkit.block.behavior.StrippableBlockBehavior; import net.momirealms.craftengine.bukkit.block.behavior.StrippableBlockBehavior;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager; 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.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.util.*; import net.momirealms.craftengine.bukkit.util.*;
import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock; import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock;
import net.momirealms.craftengine.core.block.BlockStateWrapper; import net.momirealms.craftengine.core.block.BlockStateWrapper;
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.entity.EquipmentSlot;
import net.momirealms.craftengine.core.entity.player.InteractionHand; import net.momirealms.craftengine.core.entity.player.InteractionHand;
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;
@@ -107,19 +107,7 @@ public class AxeItemBehavior extends ItemBehavior {
player.swingHand(context.getHand()); player.swingHand(context.getHand());
} }
// shrink item amount // shrink item amount
if (VersionHelper.isOrAbove1_20_5()) { item.hurtAndBreak(1, player, context.getHand() == InteractionHand.MAIN_HAND ? EquipmentSlot.MAIN_HAND : EquipmentSlot.OFF_HAND);
Object itemStack = item.getLiteralObject();
Object serverPlayer = player.serverPlayer();
Object equipmentSlot = context.getHand() == InteractionHand.MAIN_HAND ? CoreReflections.instance$EquipmentSlot$MAINHAND : CoreReflections.instance$EquipmentSlot$OFFHAND;
try {
CoreReflections.method$ItemStack$hurtAndBreak.invoke(itemStack, 1, serverPlayer, equipmentSlot);
} catch (ReflectiveOperationException e) {
CraftEngine.instance().logger().warn("Failed to hurt itemStack", e);
}
} else {
ItemStack itemStack = item.getItem();
itemStack.damage(1, bukkitPlayer);
}
} }
return InteractionResult.SUCCESS_AND_CANCEL; return InteractionResult.SUCCESS_AND_CANCEL;
} }

View File

@@ -88,7 +88,7 @@ public class FlintAndSteelItemBehavior extends ItemBehavior {
context.getHitResult(), (Item<ItemStack>) context.getItem())) { context.getHitResult(), (Item<ItemStack>) context.getItem())) {
return InteractionResult.PASS; return InteractionResult.PASS;
} }
// 且没有shift // 且没有shift或者忽略潜行的可交互方块
if (!player.isSecondaryUseActive()) { if (!player.isSecondaryUseActive()) {
player.playSound(firePos, FLINT_SOUND, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f)); player.playSound(firePos, FLINT_SOUND, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f));
} }

View File

@@ -12,7 +12,7 @@ import net.momirealms.craftengine.bukkit.util.LocationUtils;
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.FurnitureExtraData; import net.momirealms.craftengine.core.entity.furniture.FurnitureExtraData;
import net.momirealms.craftengine.core.entity.furniture.HitBox; 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;
@@ -109,11 +109,11 @@ public class FurnitureItemBehavior extends ItemBehavior {
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);
List<AABB> aabbs = new ArrayList<>(); List<AABB> aabbs = new ArrayList<>();
for (HitBox hitBox : placement.hitBoxes()) { for (HitBoxConfig hitBoxConfig : placement.hitBoxConfigs()) {
hitBox.initShapeForPlacement(finalPlacePosition.x(), finalPlacePosition.y(), finalPlacePosition.z(), (float) furnitureYaw, QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - furnitureYaw), 0).conjugate(), aabbs::add); hitBoxConfig.initShapeForPlacement(finalPlacePosition.x(), finalPlacePosition.y(), finalPlacePosition.z(), (float) furnitureYaw, QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - furnitureYaw), 0).conjugate(), aabbs::add);
} }
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(), finalPlacePosition.x(), finalPlacePosition.y(), finalPlacePosition.z())) { 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())) {
return InteractionResult.FAIL; return InteractionResult.FAIL;
} }
} }

View File

@@ -1,6 +1,5 @@
package net.momirealms.craftengine.bukkit.item.behavior; package net.momirealms.craftengine.bukkit.item.behavior;
import net.momirealms.craftengine.bukkit.block.BukkitBlockManager;
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.MFluids; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MFluids;
@@ -13,12 +12,10 @@ import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory; import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory;
import net.momirealms.craftengine.core.item.context.UseOnContext; import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.pack.Pack; 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.CraftEngine;
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException; import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
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.util.MiscUtils;
import net.momirealms.craftengine.core.util.ResourceConfigUtils; import net.momirealms.craftengine.core.util.ResourceConfigUtils;
import net.momirealms.craftengine.core.world.BlockHitResult; import net.momirealms.craftengine.core.world.BlockHitResult;
import net.momirealms.craftengine.core.world.BlockPos; import net.momirealms.craftengine.core.world.BlockPos;

View File

@@ -3,7 +3,7 @@ package net.momirealms.craftengine.bukkit.item.factory;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntArrayList;
import net.momirealms.craftengine.bukkit.item.ComponentItemWrapper; import net.momirealms.craftengine.bukkit.item.ComponentItemWrapper;
import net.momirealms.craftengine.bukkit.item.ComponentTypes; import net.momirealms.craftengine.bukkit.item.DataComponentTypes;
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.MBuiltInRegistries; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MBuiltInRegistries;
@@ -11,7 +11,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MRegistryOp
import net.momirealms.craftengine.bukkit.util.EnchantmentUtils; import net.momirealms.craftengine.bukkit.util.EnchantmentUtils;
import net.momirealms.craftengine.bukkit.util.KeyUtils; import net.momirealms.craftengine.bukkit.util.KeyUtils;
import net.momirealms.craftengine.core.attribute.AttributeModifier; import net.momirealms.craftengine.core.attribute.AttributeModifier;
import net.momirealms.craftengine.core.item.ComponentKeys; import net.momirealms.craftengine.core.item.DataComponentKeys;
import net.momirealms.craftengine.core.item.data.Enchantment; import net.momirealms.craftengine.core.item.data.Enchantment;
import net.momirealms.craftengine.core.item.data.FireworkExplosion; import net.momirealms.craftengine.core.item.data.FireworkExplosion;
import net.momirealms.craftengine.core.item.data.Trim; import net.momirealms.craftengine.core.item.data.Trim;
@@ -49,7 +49,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
protected Object getJavaTag(ComponentItemWrapper item, Object... path) { protected Object getJavaTag(ComponentItemWrapper item, Object... path) {
Map<String, Object> rootMap = (Map<String, Object>) item.getJavaComponent(ComponentTypes.CUSTOM_DATA).orElse(null); Map<String, Object> rootMap = (Map<String, Object>) item.getJavaComponent(DataComponentTypes.CUSTOM_DATA).orElse(null);
if (rootMap == null) return null; if (rootMap == null) return null;
Object currentObj = rootMap; Object currentObj = rootMap;
for (int i = 0; i < path.length; i++) { for (int i = 0; i < path.length; i++) {
@@ -70,7 +70,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
@SuppressWarnings("DuplicatedCode") @SuppressWarnings("DuplicatedCode")
@Override @Override
protected Object getExactTag(ComponentItemWrapper item, Object... path) { protected Object getExactTag(ComponentItemWrapper item, Object... path) {
Object customData = getExactComponent(item, ComponentTypes.CUSTOM_DATA); Object customData = getExactComponent(item, DataComponentTypes.CUSTOM_DATA);
if (customData == null) return null; if (customData == null) return null;
Object currentTag = FastNMS.INSTANCE.method$CustomData$getUnsafe(customData); Object currentTag = FastNMS.INSTANCE.method$CustomData$getUnsafe(customData);
for (int i = 0; i < path.length; i++) { for (int i = 0; i < path.length; i++) {
@@ -90,7 +90,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
@Override @Override
protected Tag getTag(ComponentItemWrapper item, Object... path) { protected Tag getTag(ComponentItemWrapper item, Object... path) {
CompoundTag rootTag = (CompoundTag) item.getSparrowNBTComponent(ComponentTypes.CUSTOM_DATA).orElse(null); CompoundTag rootTag = (CompoundTag) item.getSparrowNBTComponent(DataComponentTypes.CUSTOM_DATA).orElse(null);
if (rootTag == null) return null; if (rootTag == null) return null;
Tag currentTag = rootTag; Tag currentTag = rootTag;
for (int i = 0; i < path.length; i++) { for (int i = 0; i < path.length; i++) {
@@ -123,7 +123,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
valueTag = MRegistryOps.JAVA.convertTo(MRegistryOps.SPARROW_NBT, value); valueTag = MRegistryOps.JAVA.convertTo(MRegistryOps.SPARROW_NBT, value);
} }
CompoundTag rootTag = (CompoundTag) item.getSparrowNBTComponent(ComponentTypes.CUSTOM_DATA).orElseGet(CompoundTag::new); CompoundTag rootTag = (CompoundTag) item.getSparrowNBTComponent(DataComponentTypes.CUSTOM_DATA).orElseGet(CompoundTag::new);
if (path == null || path.length == 0) { if (path == null || path.length == 0) {
if (valueTag instanceof CompoundTag) { if (valueTag instanceof CompoundTag) {
@@ -151,7 +151,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
currentTag.put(finalKey, valueTag); currentTag.put(finalKey, valueTag);
} }
item.setSparrowNBTComponent(ComponentTypes.CUSTOM_DATA, rootTag); item.setSparrowNBTComponent(DataComponentTypes.CUSTOM_DATA, rootTag);
} }
@Override @Override
@@ -161,14 +161,14 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
@Override @Override
protected boolean removeTag(ComponentItemWrapper item, Object... path) { protected boolean removeTag(ComponentItemWrapper item, Object... path) {
CompoundTag rootTag = (CompoundTag) item.getSparrowNBTComponent(ComponentTypes.CUSTOM_DATA).orElse(null); CompoundTag rootTag = (CompoundTag) item.getSparrowNBTComponent(DataComponentTypes.CUSTOM_DATA).orElse(null);
if (rootTag == null || path == null || path.length == 0) return false; if (rootTag == null || path == null || path.length == 0) return false;
if (path.length == 1) { if (path.length == 1) {
String key = path[0].toString(); String key = path[0].toString();
if (rootTag.containsKey(key)) { if (rootTag.containsKey(key)) {
rootTag.remove(key); rootTag.remove(key);
item.setSparrowNBTComponent(ComponentTypes.CUSTOM_DATA, rootTag); item.setSparrowNBTComponent(DataComponentTypes.CUSTOM_DATA, rootTag);
return true; return true;
} }
return false; return false;
@@ -191,7 +191,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
String finalKey = path[path.length - 1].toString(); String finalKey = path[path.length - 1].toString();
if (parentTag.containsKey(finalKey)) { if (parentTag.containsKey(finalKey)) {
parentTag.remove(finalKey); parentTag.remove(finalKey);
item.setSparrowNBTComponent(ComponentTypes.CUSTOM_DATA, rootTag); item.setSparrowNBTComponent(DataComponentTypes.CUSTOM_DATA, rootTag);
return true; return true;
} }
return false; return false;
@@ -280,115 +280,115 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
@Override @Override
protected void customModelData(ComponentItemWrapper item, Integer data) { protected void customModelData(ComponentItemWrapper item, Integer data) {
if (data == null) { if (data == null) {
item.resetComponent(ComponentTypes.CUSTOM_MODEL_DATA); item.resetComponent(DataComponentTypes.CUSTOM_MODEL_DATA);
} else { } else {
item.setJavaComponent(ComponentTypes.CUSTOM_MODEL_DATA, data); item.setJavaComponent(DataComponentTypes.CUSTOM_MODEL_DATA, data);
} }
} }
@Override @Override
protected Optional<Integer> customModelData(ComponentItemWrapper item) { protected Optional<Integer> customModelData(ComponentItemWrapper item) {
return item.getJavaComponent(ComponentTypes.CUSTOM_MODEL_DATA); return item.getJavaComponent(DataComponentTypes.CUSTOM_MODEL_DATA);
} }
@Override @Override
protected void customNameJson(ComponentItemWrapper item, String json) { protected void customNameJson(ComponentItemWrapper item, String json) {
if (json == null) { if (json == null) {
item.resetComponent(ComponentTypes.CUSTOM_NAME); item.resetComponent(DataComponentTypes.CUSTOM_NAME);
} else { } else {
item.setJavaComponent(ComponentTypes.CUSTOM_NAME, json); item.setJavaComponent(DataComponentTypes.CUSTOM_NAME, json);
} }
} }
@Override @Override
protected Optional<String> customNameJson(ComponentItemWrapper item) { protected Optional<String> customNameJson(ComponentItemWrapper item) {
return item.getJavaComponent(ComponentTypes.CUSTOM_NAME); return item.getJavaComponent(DataComponentTypes.CUSTOM_NAME);
} }
@Override @Override
protected void itemNameJson(ComponentItemWrapper item, String json) { protected void itemNameJson(ComponentItemWrapper item, String json) {
if (json == null) { if (json == null) {
item.resetComponent(ComponentTypes.ITEM_NAME); item.resetComponent(DataComponentTypes.ITEM_NAME);
} else { } else {
item.setJavaComponent(ComponentTypes.ITEM_NAME, json); item.setJavaComponent(DataComponentTypes.ITEM_NAME, json);
} }
} }
@Override @Override
protected Optional<String> itemNameJson(ComponentItemWrapper item) { protected Optional<String> itemNameJson(ComponentItemWrapper item) {
return item.getJavaComponent(ComponentTypes.ITEM_NAME); return item.getJavaComponent(DataComponentTypes.ITEM_NAME);
} }
@Override @Override
protected void skull(ComponentItemWrapper item, String skullData) { protected void skull(ComponentItemWrapper item, String skullData) {
if (skullData == null) { if (skullData == null) {
item.resetComponent(ComponentTypes.PROFILE); item.resetComponent(DataComponentTypes.PROFILE);
} else { } else {
Map<String, Object> profile = Map.of("properties", List.of(Map.of("name", "textures", "value", skullData))); Map<String, Object> profile = Map.of("properties", List.of(Map.of("name", "textures", "value", skullData)));
item.setJavaComponent(ComponentTypes.PROFILE, profile); item.setJavaComponent(DataComponentTypes.PROFILE, profile);
} }
} }
@Override @Override
protected Optional<List<String>> loreJson(ComponentItemWrapper item) { protected Optional<List<String>> loreJson(ComponentItemWrapper item) {
return item.getJavaComponent(ComponentTypes.LORE); return item.getJavaComponent(DataComponentTypes.LORE);
} }
@Override @Override
protected void loreJson(ComponentItemWrapper item, List<String> lore) { protected void loreJson(ComponentItemWrapper item, List<String> lore) {
if (lore == null || lore.isEmpty()) { if (lore == null || lore.isEmpty()) {
item.resetComponent(ComponentTypes.LORE); item.resetComponent(DataComponentTypes.LORE);
} else { } else {
item.setJavaComponent(ComponentTypes.LORE, lore); item.setJavaComponent(DataComponentTypes.LORE, lore);
} }
} }
@Override @Override
protected boolean unbreakable(ComponentItemWrapper item) { protected boolean unbreakable(ComponentItemWrapper item) {
return item.hasComponent(ComponentTypes.UNBREAKABLE); return item.hasComponent(DataComponentTypes.UNBREAKABLE);
} }
@Override @Override
protected void unbreakable(ComponentItemWrapper item, boolean unbreakable) { protected void unbreakable(ComponentItemWrapper item, boolean unbreakable) {
if (unbreakable) { if (unbreakable) {
item.setJavaComponent(ComponentTypes.UNBREAKABLE, Map.of()); item.setJavaComponent(DataComponentTypes.UNBREAKABLE, Map.of());
} else { } else {
item.resetComponent(ComponentTypes.UNBREAKABLE); item.resetComponent(DataComponentTypes.UNBREAKABLE);
} }
} }
@Override @Override
protected Optional<Boolean> glint(ComponentItemWrapper item) { protected Optional<Boolean> glint(ComponentItemWrapper item) {
return Optional.ofNullable((Boolean) item.getComponentExact(ComponentTypes.ENCHANTMENT_GLINT_OVERRIDE)); return Optional.ofNullable((Boolean) item.getComponentExact(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE));
} }
@Override @Override
protected void glint(ComponentItemWrapper item, Boolean glint) { protected void glint(ComponentItemWrapper item, Boolean glint) {
if (glint == null) { if (glint == null) {
item.resetComponent(ComponentTypes.ENCHANTMENT_GLINT_OVERRIDE); item.resetComponent(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE);
} else { } else {
item.setJavaComponent(ComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, glint); item.setJavaComponent(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, glint);
} }
} }
@Override @Override
protected Optional<Integer> damage(ComponentItemWrapper item) { protected Optional<Integer> damage(ComponentItemWrapper item) {
return item.getJavaComponent(ComponentTypes.DAMAGE); return item.getJavaComponent(DataComponentTypes.DAMAGE);
} }
@Override @Override
protected void damage(ComponentItemWrapper item, Integer damage) { protected void damage(ComponentItemWrapper item, Integer damage) {
if (damage == null) { if (damage == null) {
item.resetComponent(ComponentTypes.DAMAGE); item.resetComponent(DataComponentTypes.DAMAGE);
} else { } else {
item.setJavaComponent(ComponentTypes.DAMAGE, damage); item.setJavaComponent(DataComponentTypes.DAMAGE, damage);
} }
} }
@Override @Override
protected Optional<Color> dyedColor(ComponentItemWrapper item) { protected Optional<Color> dyedColor(ComponentItemWrapper item) {
if (!item.hasComponent(ComponentTypes.DYED_COLOR)) return Optional.empty(); if (!item.hasComponent(DataComponentTypes.DYED_COLOR)) return Optional.empty();
Object javaObj = getJavaComponent(item, ComponentTypes.DYED_COLOR); Object javaObj = getJavaComponent(item, DataComponentTypes.DYED_COLOR);
if (javaObj instanceof Integer integer) { if (javaObj instanceof Integer integer) {
return Optional.of(Color.fromDecimal(integer)); return Optional.of(Color.fromDecimal(integer));
} else if (javaObj instanceof Map<?, ?> map) { } else if (javaObj instanceof Map<?, ?> map) {
@@ -400,30 +400,30 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
@Override @Override
protected void dyedColor(ComponentItemWrapper item, Color color) { protected void dyedColor(ComponentItemWrapper item, Color color) {
if (color == null) { if (color == null) {
item.resetComponent(ComponentTypes.DYED_COLOR); item.resetComponent(DataComponentTypes.DYED_COLOR);
} else { } else {
item.setJavaComponent(ComponentTypes.DYED_COLOR, color.color()); item.setJavaComponent(DataComponentTypes.DYED_COLOR, color.color());
} }
} }
@Override @Override
protected int maxDamage(ComponentItemWrapper item) { protected int maxDamage(ComponentItemWrapper item) {
Optional<Integer> damage = item.getJavaComponent(ComponentTypes.MAX_DAMAGE); Optional<Integer> damage = item.getJavaComponent(DataComponentTypes.MAX_DAMAGE);
return damage.orElseGet(() -> (int) item.getItem().getType().getMaxDurability()); return damage.orElseGet(() -> (int) item.getItem().getType().getMaxDurability());
} }
@Override @Override
protected void maxDamage(ComponentItemWrapper item, Integer damage) { protected void maxDamage(ComponentItemWrapper item, Integer damage) {
if (damage == null) { if (damage == null) {
item.resetComponent(ComponentTypes.MAX_DAMAGE); item.resetComponent(DataComponentTypes.MAX_DAMAGE);
} else { } else {
item.setJavaComponent(ComponentTypes.MAX_DAMAGE, damage); item.setJavaComponent(DataComponentTypes.MAX_DAMAGE, damage);
} }
} }
@Override @Override
protected Optional<Enchantment> getEnchantment(ComponentItemWrapper item, Key key) { protected Optional<Enchantment> getEnchantment(ComponentItemWrapper item, Key key) {
Object enchant = item.getComponentExact(ComponentTypes.ENCHANTMENTS); Object enchant = item.getComponentExact(DataComponentTypes.ENCHANTMENTS);
if (enchant == null) return Optional.empty(); if (enchant == null) return Optional.empty();
try { try {
Map<String, Integer> map = EnchantmentUtils.toMap(enchant); Map<String, Integer> map = EnchantmentUtils.toMap(enchant);
@@ -439,26 +439,26 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
@Override @Override
protected void enchantments(ComponentItemWrapper item, List<Enchantment> enchantments) { protected void enchantments(ComponentItemWrapper item, List<Enchantment> enchantments) {
if (enchantments == null || enchantments.isEmpty()) { if (enchantments == null || enchantments.isEmpty()) {
item.resetComponent(ComponentTypes.ENCHANTMENTS); item.resetComponent(DataComponentTypes.ENCHANTMENTS);
} else { } else {
Map<String, Integer> enchants = new HashMap<>(); Map<String, Integer> enchants = new HashMap<>();
for (Enchantment enchantment : enchantments) { for (Enchantment enchantment : enchantments) {
enchants.put(enchantment.id().toString(), enchantment.level()); enchants.put(enchantment.id().toString(), enchantment.level());
} }
item.setJavaComponent(ComponentTypes.ENCHANTMENTS, enchants); item.setJavaComponent(DataComponentTypes.ENCHANTMENTS, enchants);
} }
} }
@Override @Override
protected void storedEnchantments(ComponentItemWrapper item, List<Enchantment> enchantments) { protected void storedEnchantments(ComponentItemWrapper item, List<Enchantment> enchantments) {
if (enchantments == null || enchantments.isEmpty()) { if (enchantments == null || enchantments.isEmpty()) {
item.resetComponent(ComponentTypes.STORED_ENCHANTMENTS); item.resetComponent(DataComponentTypes.STORED_ENCHANTMENTS);
} else { } else {
Map<String, Integer> enchants = new HashMap<>(); Map<String, Integer> enchants = new HashMap<>();
for (Enchantment enchantment : enchantments) { for (Enchantment enchantment : enchantments) {
enchants.put(enchantment.id().toString(), enchantment.level()); enchants.put(enchantment.id().toString(), enchantment.level());
} }
item.setJavaComponent(ComponentTypes.STORED_ENCHANTMENTS, enchants); item.setJavaComponent(DataComponentTypes.STORED_ENCHANTMENTS, enchants);
} }
} }
@@ -469,39 +469,39 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
@Override @Override
protected int maxStackSize(ComponentItemWrapper item) { protected int maxStackSize(ComponentItemWrapper item) {
Optional<Integer> stackSize = item.getJavaComponent(ComponentTypes.MAX_STACK_SIZE); Optional<Integer> stackSize = item.getJavaComponent(DataComponentTypes.MAX_STACK_SIZE);
return stackSize.orElseGet(() -> item.getItem().getType().getMaxStackSize()); return stackSize.orElseGet(() -> item.getItem().getType().getMaxStackSize());
} }
@Override @Override
protected void maxStackSize(ComponentItemWrapper item, Integer maxStackSize) { protected void maxStackSize(ComponentItemWrapper item, Integer maxStackSize) {
if (maxStackSize == null) { if (maxStackSize == null) {
item.resetComponent(ComponentTypes.MAX_STACK_SIZE); item.resetComponent(DataComponentTypes.MAX_STACK_SIZE);
} else { } else {
item.setJavaComponent(ComponentTypes.MAX_STACK_SIZE, maxStackSize); item.setJavaComponent(DataComponentTypes.MAX_STACK_SIZE, maxStackSize);
} }
} }
@Override @Override
protected void repairCost(ComponentItemWrapper item, Integer data) { protected void repairCost(ComponentItemWrapper item, Integer data) {
if (data == null) { if (data == null) {
item.resetComponent(ComponentTypes.REPAIR_COST); item.resetComponent(DataComponentTypes.REPAIR_COST);
} else { } else {
item.setJavaComponent(ComponentTypes.REPAIR_COST, data); item.setJavaComponent(DataComponentTypes.REPAIR_COST, data);
} }
} }
@Override @Override
protected Optional<Integer> repairCost(ComponentItemWrapper item) { protected Optional<Integer> repairCost(ComponentItemWrapper item) {
return item.getJavaComponent(ComponentTypes.REPAIR_COST); return item.getJavaComponent(DataComponentTypes.REPAIR_COST);
} }
@Override @Override
protected void trim(ComponentItemWrapper item, Trim trim) { protected void trim(ComponentItemWrapper item, Trim trim) {
if (trim == null) { if (trim == null) {
item.resetComponent(ComponentTypes.TRIM); item.resetComponent(DataComponentTypes.TRIM);
} else { } else {
item.setJavaComponent(ComponentTypes.TRIM, Map.of( item.setJavaComponent(DataComponentTypes.TRIM, Map.of(
"pattern", trim.pattern().asString(), "pattern", trim.pattern().asString(),
"material", trim.material().asString() "material", trim.material().asString()
)); ));
@@ -510,7 +510,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
@Override @Override
protected Optional<Trim> trim(ComponentItemWrapper item) { protected Optional<Trim> trim(ComponentItemWrapper item) {
Optional<Object> trim = item.getJavaComponent(ComponentTypes.TRIM); Optional<Object> trim = item.getJavaComponent(DataComponentTypes.TRIM);
if (trim.isEmpty()) { if (trim.isEmpty()) {
return Optional.empty(); return Optional.empty();
} }
@@ -522,7 +522,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
protected Optional<FireworkExplosion> fireworkExplosion(ComponentItemWrapper item) { protected Optional<FireworkExplosion> fireworkExplosion(ComponentItemWrapper item) {
Optional<Object> optionalExplosion = item.getJavaComponent(ComponentTypes.FIREWORK_EXPLOSION); Optional<Object> optionalExplosion = item.getJavaComponent(DataComponentTypes.FIREWORK_EXPLOSION);
if (optionalExplosion.isEmpty()) return Optional.empty(); if (optionalExplosion.isEmpty()) return Optional.empty();
Map<String, Object> explosions = MiscUtils.castToMap(optionalExplosion.get(), false); Map<String, Object> explosions = MiscUtils.castToMap(optionalExplosion.get(), false);
FireworkExplosion.Shape shape = Optional.ofNullable(FireworkExplosion.Shape.byName((String) explosions.get("shape"))).orElse(FireworkExplosion.Shape.SMALL_BALL); FireworkExplosion.Shape shape = Optional.ofNullable(FireworkExplosion.Shape.byName((String) explosions.get("shape"))).orElse(FireworkExplosion.Shape.SMALL_BALL);
@@ -542,9 +542,9 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
@Override @Override
protected void fireworkExplosion(ComponentItemWrapper item, FireworkExplosion explosion) { protected void fireworkExplosion(ComponentItemWrapper item, FireworkExplosion explosion) {
if (explosion == null) { if (explosion == null) {
item.resetComponent(ComponentTypes.FIREWORK_EXPLOSION); item.resetComponent(DataComponentTypes.FIREWORK_EXPLOSION);
} else { } else {
item.setJavaComponent(ComponentTypes.FIREWORK_EXPLOSION, Map.of( item.setJavaComponent(DataComponentTypes.FIREWORK_EXPLOSION, Map.of(
"shape", explosion.shape().getName(), "shape", explosion.shape().getName(),
"has_trail", explosion.hasTrail(), "has_trail", explosion.hasTrail(),
"has_twinkle", explosion.hasTwinkle(), "has_twinkle", explosion.hasTwinkle(),
@@ -590,7 +590,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
@Override @Override
protected void attributeModifiers(ComponentItemWrapper item, List<AttributeModifier> modifierList) { protected void attributeModifiers(ComponentItemWrapper item, List<AttributeModifier> modifierList) {
CompoundTag compoundTag = (CompoundTag) item.getSparrowNBTComponent(ComponentKeys.ATTRIBUTE_MODIFIERS).orElseGet(CompoundTag::new); CompoundTag compoundTag = (CompoundTag) item.getSparrowNBTComponent(DataComponentKeys.ATTRIBUTE_MODIFIERS).orElseGet(CompoundTag::new);
ListTag modifiers = new ListTag(); ListTag modifiers = new ListTag();
compoundTag.put("modifiers", modifiers); compoundTag.put("modifiers", modifiers);
for (AttributeModifier modifier : modifierList) { for (AttributeModifier modifier : modifierList) {
@@ -607,6 +607,16 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
modifierTag.putString("operation", modifier.operation().id()); modifierTag.putString("operation", modifier.operation().id());
modifiers.add(modifierTag); modifiers.add(modifierTag);
} }
item.setSparrowNBTComponent(ComponentKeys.ATTRIBUTE_MODIFIERS, compoundTag); item.setSparrowNBTComponent(DataComponentKeys.ATTRIBUTE_MODIFIERS, compoundTag);
}
@Override
protected Optional<Map<String, String>> blockState(ComponentItemWrapper item) {
return item.getJavaComponent(DataComponentTypes.BLOCK_STATE);
}
@Override
protected void blockState(ComponentItemWrapper item, Map<String, String> state) {
item.setJavaComponent(DataComponentTypes.BLOCK_STATE, state);
} }
} }

View File

@@ -1,7 +1,7 @@
package net.momirealms.craftengine.bukkit.item.factory; package net.momirealms.craftengine.bukkit.item.factory;
import net.momirealms.craftengine.bukkit.item.ComponentItemWrapper; import net.momirealms.craftengine.bukkit.item.ComponentItemWrapper;
import net.momirealms.craftengine.bukkit.item.ComponentTypes; import net.momirealms.craftengine.bukkit.item.DataComponentTypes;
import net.momirealms.craftengine.core.item.data.JukeboxPlayable; import net.momirealms.craftengine.core.item.data.JukeboxPlayable;
import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.CraftEngine;
@@ -16,7 +16,7 @@ public class ComponentItemFactory1_21 extends ComponentItemFactory1_20_5 {
@Override @Override
protected Optional<JukeboxPlayable> jukeboxSong(ComponentItemWrapper item) { protected Optional<JukeboxPlayable> jukeboxSong(ComponentItemWrapper item) {
Optional<Map<String, Object>> map = item.getJavaComponent(ComponentTypes.JUKEBOX_PLAYABLE); Optional<Map<String, Object>> map = item.getJavaComponent(DataComponentTypes.JUKEBOX_PLAYABLE);
return map.map(song -> new JukeboxPlayable( return map.map(song -> new JukeboxPlayable(
(String) song.get("song"), (String) song.get("song"),
(boolean) song.getOrDefault("show_in_tooltip", true)) (boolean) song.getOrDefault("show_in_tooltip", true))
@@ -25,7 +25,7 @@ public class ComponentItemFactory1_21 extends ComponentItemFactory1_20_5 {
@Override @Override
protected void jukeboxSong(ComponentItemWrapper item, JukeboxPlayable data) { protected void jukeboxSong(ComponentItemWrapper item, JukeboxPlayable data) {
item.setJavaComponent(ComponentTypes.JUKEBOX_PLAYABLE, Map.of( item.setJavaComponent(DataComponentTypes.JUKEBOX_PLAYABLE, Map.of(
"song", data.song(), "song", data.song(),
"show_in_tooltip", true "show_in_tooltip", true
)); ));

View File

@@ -1,7 +1,7 @@
package net.momirealms.craftengine.bukkit.item.factory; package net.momirealms.craftengine.bukkit.item.factory;
import net.momirealms.craftengine.bukkit.item.ComponentItemWrapper; import net.momirealms.craftengine.bukkit.item.ComponentItemWrapper;
import net.momirealms.craftengine.bukkit.item.ComponentTypes; import net.momirealms.craftengine.bukkit.item.DataComponentTypes;
import net.momirealms.craftengine.core.entity.EquipmentSlot; import net.momirealms.craftengine.core.entity.EquipmentSlot;
import net.momirealms.craftengine.core.item.setting.EquipmentData; import net.momirealms.craftengine.core.item.setting.EquipmentData;
import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.CraftEngine;
@@ -21,43 +21,43 @@ public class ComponentItemFactory1_21_2 extends ComponentItemFactory1_21 {
@Override @Override
protected void tooltipStyle(ComponentItemWrapper item, String data) { protected void tooltipStyle(ComponentItemWrapper item, String data) {
if (data == null) { if (data == null) {
item.resetComponent(ComponentTypes.TOOLTIP_STYLE); item.resetComponent(DataComponentTypes.TOOLTIP_STYLE);
} else { } else {
item.setJavaComponent(ComponentTypes.TOOLTIP_STYLE, data); item.setJavaComponent(DataComponentTypes.TOOLTIP_STYLE, data);
} }
} }
@Override @Override
protected Optional<String> tooltipStyle(ComponentItemWrapper item) { protected Optional<String> tooltipStyle(ComponentItemWrapper item) {
return item.getJavaComponent(ComponentTypes.TOOLTIP_STYLE); return item.getJavaComponent(DataComponentTypes.TOOLTIP_STYLE);
} }
@Override @Override
protected void itemModel(ComponentItemWrapper item, String data) { protected void itemModel(ComponentItemWrapper item, String data) {
if (data == null) { if (data == null) {
item.resetComponent(ComponentTypes.ITEM_MODEL); item.resetComponent(DataComponentTypes.ITEM_MODEL);
} else { } else {
item.setJavaComponent(ComponentTypes.ITEM_MODEL, data); item.setJavaComponent(DataComponentTypes.ITEM_MODEL, data);
} }
} }
@Override @Override
protected Optional<String> itemModel(ComponentItemWrapper item) { protected Optional<String> itemModel(ComponentItemWrapper item) {
return item.getJavaComponent(ComponentTypes.ITEM_MODEL); return item.getJavaComponent(DataComponentTypes.ITEM_MODEL);
} }
@Override @Override
protected void equippable(ComponentItemWrapper item, EquipmentData data) { protected void equippable(ComponentItemWrapper item, EquipmentData data) {
if (data == null) { if (data == null) {
item.resetComponent(ComponentTypes.EQUIPPABLE); item.resetComponent(DataComponentTypes.EQUIPPABLE);
} else { } else {
item.setSparrowNBTComponent(ComponentTypes.EQUIPPABLE, data.toNBT()); item.setSparrowNBTComponent(DataComponentTypes.EQUIPPABLE, data.toNBT());
} }
} }
@Override @Override
protected Optional<EquipmentData> equippable(ComponentItemWrapper item) { protected Optional<EquipmentData> equippable(ComponentItemWrapper item) {
Optional<Object> optionalData = item.getJavaComponent(ComponentTypes.EQUIPPABLE); Optional<Object> optionalData = item.getJavaComponent(DataComponentTypes.EQUIPPABLE);
if (optionalData.isEmpty()) return Optional.empty(); if (optionalData.isEmpty()) return Optional.empty();
Map<String, Object> data = MiscUtils.castToMap(optionalData.get(), false); Map<String, Object> data = MiscUtils.castToMap(optionalData.get(), false);
String slot = data.get("slot").toString(); String slot = data.get("slot").toString();

View File

@@ -1,7 +1,7 @@
package net.momirealms.craftengine.bukkit.item.factory; package net.momirealms.craftengine.bukkit.item.factory;
import net.momirealms.craftengine.bukkit.item.ComponentItemWrapper; import net.momirealms.craftengine.bukkit.item.ComponentItemWrapper;
import net.momirealms.craftengine.bukkit.item.ComponentTypes; import net.momirealms.craftengine.bukkit.item.DataComponentTypes;
import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.CraftEngine;
import java.util.List; import java.util.List;
@@ -16,7 +16,7 @@ public class ComponentItemFactory1_21_4 extends ComponentItemFactory1_21_2 {
@Override @Override
protected Optional<Integer> customModelData(ComponentItemWrapper item) { protected Optional<Integer> customModelData(ComponentItemWrapper item) {
Optional<Object> optional = item.getJavaComponent(ComponentTypes.CUSTOM_MODEL_DATA); Optional<Object> optional = item.getJavaComponent(DataComponentTypes.CUSTOM_MODEL_DATA);
if (optional.isEmpty()) return Optional.empty(); if (optional.isEmpty()) return Optional.empty();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Map<String, Object> data = (Map<String, Object>) optional.get(); Map<String, Object> data = (Map<String, Object>) optional.get();
@@ -29,9 +29,9 @@ public class ComponentItemFactory1_21_4 extends ComponentItemFactory1_21_2 {
@Override @Override
protected void customModelData(ComponentItemWrapper item, Integer data) { protected void customModelData(ComponentItemWrapper item, Integer data) {
if (data == null) { if (data == null) {
item.resetComponent(ComponentTypes.CUSTOM_MODEL_DATA); item.resetComponent(DataComponentTypes.CUSTOM_MODEL_DATA);
} else { } else {
item.setJavaComponent(ComponentTypes.CUSTOM_MODEL_DATA, Map.of("floats", List.of(data.floatValue()))); item.setJavaComponent(DataComponentTypes.CUSTOM_MODEL_DATA, Map.of("floats", List.of(data.floatValue())));
} }
} }
} }

View File

@@ -4,9 +4,9 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.momirealms.craftengine.bukkit.item.ComponentItemWrapper; import net.momirealms.craftengine.bukkit.item.ComponentItemWrapper;
import net.momirealms.craftengine.bukkit.item.ComponentTypes; import net.momirealms.craftengine.bukkit.item.DataComponentTypes;
import net.momirealms.craftengine.core.attribute.AttributeModifier; import net.momirealms.craftengine.core.attribute.AttributeModifier;
import net.momirealms.craftengine.core.item.ComponentKeys; import net.momirealms.craftengine.core.item.DataComponentKeys;
import net.momirealms.craftengine.core.item.data.JukeboxPlayable; import net.momirealms.craftengine.core.item.data.JukeboxPlayable;
import net.momirealms.craftengine.core.plugin.CraftEngine; import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.util.AdventureHelper; import net.momirealms.craftengine.core.util.AdventureHelper;
@@ -30,23 +30,23 @@ public class ComponentItemFactory1_21_5 extends ComponentItemFactory1_21_4 {
@Override @Override
protected void customNameJson(ComponentItemWrapper item, String json) { protected void customNameJson(ComponentItemWrapper item, String json) {
if (json == null) { if (json == null) {
item.resetComponent(ComponentTypes.CUSTOM_NAME); item.resetComponent(DataComponentTypes.CUSTOM_NAME);
} else { } else {
item.setSparrowNBTComponent(ComponentTypes.CUSTOM_NAME, AdventureHelper.componentToNbt(AdventureHelper.jsonToComponent(json))); item.setSparrowNBTComponent(DataComponentTypes.CUSTOM_NAME, AdventureHelper.componentToNbt(AdventureHelper.jsonToComponent(json)));
} }
} }
@Override @Override
protected Optional<String> customNameJson(ComponentItemWrapper item) { protected Optional<String> customNameJson(ComponentItemWrapper item) {
return item.getJsonComponent(ComponentTypes.CUSTOM_NAME).map(it -> GsonHelper.get().toJson(it)); return item.getJsonComponent(DataComponentTypes.CUSTOM_NAME).map(it -> GsonHelper.get().toJson(it));
} }
@Override @Override
protected void customNameComponent(ComponentItemWrapper item, Component component) { protected void customNameComponent(ComponentItemWrapper item, Component component) {
if (component == null) { if (component == null) {
item.resetComponent(ComponentTypes.CUSTOM_NAME); item.resetComponent(DataComponentTypes.CUSTOM_NAME);
} else { } else {
item.setSparrowNBTComponent(ComponentTypes.CUSTOM_NAME, AdventureHelper.componentToNbt(component)); item.setSparrowNBTComponent(DataComponentTypes.CUSTOM_NAME, AdventureHelper.componentToNbt(component));
} }
} }
@@ -58,30 +58,30 @@ public class ComponentItemFactory1_21_5 extends ComponentItemFactory1_21_4 {
@Override @Override
protected void itemNameJson(ComponentItemWrapper item, String json) { protected void itemNameJson(ComponentItemWrapper item, String json) {
if (json == null) { if (json == null) {
item.resetComponent(ComponentTypes.ITEM_NAME); item.resetComponent(DataComponentTypes.ITEM_NAME);
} else { } else {
item.setSparrowNBTComponent(ComponentTypes.ITEM_NAME, AdventureHelper.componentToNbt(AdventureHelper.jsonToComponent(json))); item.setSparrowNBTComponent(DataComponentTypes.ITEM_NAME, AdventureHelper.componentToNbt(AdventureHelper.jsonToComponent(json)));
} }
} }
@Override @Override
protected void itemNameComponent(ComponentItemWrapper item, Component component) { protected void itemNameComponent(ComponentItemWrapper item, Component component) {
if (component == null) { if (component == null) {
item.resetComponent(ComponentTypes.ITEM_NAME); item.resetComponent(DataComponentTypes.ITEM_NAME);
} else { } else {
item.setSparrowNBTComponent(ComponentTypes.ITEM_NAME, AdventureHelper.componentToNbt(component)); item.setSparrowNBTComponent(DataComponentTypes.ITEM_NAME, AdventureHelper.componentToNbt(component));
} }
} }
@Override @Override
protected Optional<String> itemNameJson(ComponentItemWrapper item) { protected Optional<String> itemNameJson(ComponentItemWrapper item) {
return item.getJsonComponent(ComponentTypes.ITEM_NAME).map(it -> GsonHelper.get().toJson(it)); return item.getJsonComponent(DataComponentTypes.ITEM_NAME).map(it -> GsonHelper.get().toJson(it));
} }
@Override @Override
protected Optional<List<String>> loreJson(ComponentItemWrapper item) { protected Optional<List<String>> loreJson(ComponentItemWrapper item) {
if (!item.hasComponent(ComponentTypes.LORE)) return Optional.empty(); if (!item.hasComponent(DataComponentTypes.LORE)) return Optional.empty();
Optional<JsonElement> json = item.getJsonComponent(ComponentTypes.LORE); Optional<JsonElement> json = item.getJsonComponent(DataComponentTypes.LORE);
if (json.isEmpty()) return Optional.empty(); if (json.isEmpty()) return Optional.empty();
List<String> lore = new ArrayList<>(); List<String> lore = new ArrayList<>();
for (JsonElement jsonElement : (JsonArray) json.get()) { for (JsonElement jsonElement : (JsonArray) json.get()) {
@@ -93,40 +93,40 @@ public class ComponentItemFactory1_21_5 extends ComponentItemFactory1_21_4 {
@Override @Override
protected void loreComponent(ComponentItemWrapper item, List<Component> lore) { protected void loreComponent(ComponentItemWrapper item, List<Component> lore) {
if (lore == null || lore.isEmpty()) { if (lore == null || lore.isEmpty()) {
item.resetComponent(ComponentTypes.LORE); item.resetComponent(DataComponentTypes.LORE);
} else { } else {
List<Tag> loreTags = new ArrayList<>(); List<Tag> loreTags = new ArrayList<>();
for (Component component : lore) { for (Component component : lore) {
loreTags.add(AdventureHelper.componentToTag(component)); loreTags.add(AdventureHelper.componentToTag(component));
} }
item.setSparrowNBTComponent(ComponentTypes.LORE, new ListTag(loreTags)); item.setSparrowNBTComponent(DataComponentTypes.LORE, new ListTag(loreTags));
} }
} }
@Override @Override
protected void loreJson(ComponentItemWrapper item, List<String> lore) { protected void loreJson(ComponentItemWrapper item, List<String> lore) {
if (lore == null || lore.isEmpty()) { if (lore == null || lore.isEmpty()) {
item.resetComponent(ComponentTypes.LORE); item.resetComponent(DataComponentTypes.LORE);
} else { } else {
List<Tag> loreTags = new ArrayList<>(); List<Tag> loreTags = new ArrayList<>();
for (String json : lore) { for (String json : lore) {
loreTags.add(AdventureHelper.componentToTag(AdventureHelper.jsonToComponent(json))); loreTags.add(AdventureHelper.componentToTag(AdventureHelper.jsonToComponent(json)));
} }
item.setSparrowNBTComponent(ComponentTypes.LORE, new ListTag(loreTags)); item.setSparrowNBTComponent(DataComponentTypes.LORE, new ListTag(loreTags));
} }
} }
@Override @Override
protected Optional<JukeboxPlayable> jukeboxSong(ComponentItemWrapper item) { protected Optional<JukeboxPlayable> jukeboxSong(ComponentItemWrapper item) {
if (!item.hasComponent(ComponentTypes.JUKEBOX_PLAYABLE)) return Optional.empty(); if (!item.hasComponent(DataComponentTypes.JUKEBOX_PLAYABLE)) return Optional.empty();
String song = (String) item.getJavaComponent(ComponentTypes.JUKEBOX_PLAYABLE).orElse(null); String song = (String) item.getJavaComponent(DataComponentTypes.JUKEBOX_PLAYABLE).orElse(null);
if (song == null) return Optional.empty(); if (song == null) return Optional.empty();
return Optional.of(new JukeboxPlayable(song, true)); return Optional.of(new JukeboxPlayable(song, true));
} }
@Override @Override
protected void jukeboxSong(ComponentItemWrapper item, JukeboxPlayable data) { protected void jukeboxSong(ComponentItemWrapper item, JukeboxPlayable data) {
item.setJavaComponent(ComponentTypes.JUKEBOX_PLAYABLE, data.song()); item.setJavaComponent(DataComponentTypes.JUKEBOX_PLAYABLE, data.song());
} }
@Override @Override
@@ -151,6 +151,6 @@ public class ComponentItemFactory1_21_5 extends ComponentItemFactory1_21_4 {
} }
modifiers.add(modifierTag); modifiers.add(modifierTag);
} }
item.setSparrowNBTComponent(ComponentKeys.ATTRIBUTE_MODIFIERS, modifiers); item.setSparrowNBTComponent(DataComponentKeys.ATTRIBUTE_MODIFIERS, modifiers);
} }
} }

View File

@@ -303,6 +303,18 @@ public class UniversalItemFactory extends BukkitItemFactory<LegacyItemWrapper> {
} }
} }
@Override
protected Optional<Map<String, String>> blockState(LegacyItemWrapper item) {
Map<String, String> state = item.getJavaTag("BlockStateTag");
if (state == null) return Optional.empty();
return Optional.of(state);
}
@Override
protected void blockState(LegacyItemWrapper item, Map<String, String> state) {
item.setTag(state, "BlockStateTag");
}
@Override @Override
protected Optional<Trim> trim(LegacyItemWrapper item) { protected Optional<Trim> trim(LegacyItemWrapper item) {
String material = item.getJavaTag("Trim", "material"); String material = item.getJavaTag("Trim", "material");

View File

@@ -4,6 +4,7 @@ import io.papermc.paper.event.block.CompostItemEvent;
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors; import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
import net.momirealms.craftengine.bukkit.api.event.CustomBlockInteractEvent; import net.momirealms.craftengine.bukkit.api.event.CustomBlockInteractEvent;
import net.momirealms.craftengine.bukkit.entity.BukkitEntity; import net.momirealms.craftengine.bukkit.entity.BukkitEntity;
import net.momirealms.craftengine.bukkit.entity.BukkitItemEntity;
import net.momirealms.craftengine.bukkit.item.BukkitCustomItem; import net.momirealms.craftengine.bukkit.item.BukkitCustomItem;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS; import net.momirealms.craftengine.bukkit.nms.FastNMS;
@@ -21,6 +22,7 @@ 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.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.UseOnContext; import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.item.setting.FoodData; import net.momirealms.craftengine.core.item.setting.FoodData;
import net.momirealms.craftengine.core.item.updater.ItemUpdateResult; import net.momirealms.craftengine.core.item.updater.ItemUpdateResult;
@@ -141,7 +143,7 @@ public class ItemEventListener implements Listener {
Direction direction = DirectionUtils.toDirection(event.getBlockFace()); Direction direction = DirectionUtils.toDirection(event.getBlockFace());
BlockPos pos = LocationUtils.toBlockPos(block.getLocation()); BlockPos pos = LocationUtils.toBlockPos(block.getLocation());
Vec3d vec3d = new Vec3d(interactionPoint.getX(), interactionPoint.getY(), interactionPoint.getZ()); Vec3d vec3d = new Vec3d(interactionPoint.getX(), interactionPoint.getY(), interactionPoint.getZ());
hitResult = new BlockHitResult(vec3d, direction, pos, false); hitResult = new BlockHitResult(vec3d, direction, pos, false); // todo 需要检测玩家是否在方块内
} }
// 处理自定义方块 // 处理自定义方块
@@ -165,9 +167,8 @@ public class ItemEventListener implements Listener {
// fix client side issues // fix client side issues
if (action.isRightClick() && hitResult != null && if (action.isRightClick() && hitResult != null &&
InteractUtils.willConsume(player, BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().literalObject()), hitResult, itemInHand)) { InteractUtils.canPlaceVisualBlock(player, BlockStateUtils.fromBlockData(immutableBlockState.vanillaBlockState().literalObject()), hitResult, itemInHand)) {
player.updateInventory(); player.updateInventory();
//PlayerUtils.resendItemInHand(player);
} }
Cancellable dummy = Cancellable.dummy(); Cancellable dummy = Cancellable.dummy();
@@ -285,15 +286,13 @@ public class ItemEventListener implements Listener {
} }
// custom item // custom item
else { else {
if (optionalCustomItem.get().settings().canPlaceRelatedVanillaBlock()) { if (optionalCustomItem.get().settings().disableVanillaBehavior()) {
// 如果用户设置了允许放置对应的原版方块,那么直接返回。 // 不能在BlockPlaceEvent里检测是因为种农作物不触发相关事件
// 这种情况下最好是return以避免同时触发多个behavior发生冲突 // 允许尝试放置方块
// 当用户选择其作为原版方块放下时,自定义行为可能已经不重要了?
return;
} else {
// todo 实际上这里的处理并不正确,因为判断玩家是否能够放置那个方块需要更加细节的判断。比如玩家无法对着树叶放置火把,但是交互事件依然触发,此情况下不可丢弃自定义行为。
if (serverPlayer.isSecondaryUseActive() || !InteractUtils.isInteractable(player, blockData, hitResult, itemInHand)) { if (serverPlayer.isSecondaryUseActive() || !InteractUtils.isInteractable(player, blockData, hitResult, itemInHand)) {
event.setCancelled(true); if (InteractUtils.canPlaceBlock(new BlockPlaceContext(new UseOnContext(serverPlayer, hand, itemInHand, hitResult)))) {
event.setCancelled(true);
}
} }
} }
} }
@@ -578,16 +577,31 @@ public class ItemEventListener implements Listener {
} }
} }
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH) @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onPickUpItem(EntityPickupItemEvent event) { public void onPickUpItem(EntityPickupItemEvent event) {
if (!Config.triggerUpdatePickUp()) return;
if (!(event.getEntity() instanceof Player player)) return; if (!(event.getEntity() instanceof Player player)) return;
org.bukkit.entity.Item itemDrop = event.getItem(); org.bukkit.entity.Item itemDrop = event.getItem();
ItemStack itemStack = itemDrop.getItemStack(); ItemStack itemStack = itemDrop.getItemStack();
Item<ItemStack> wrapped = this.itemManager.wrap(itemStack); Item<ItemStack> wrapped = this.itemManager.wrap(itemStack);
ItemUpdateResult result = this.itemManager.updateItem(wrapped, () -> ItemBuildContext.of(BukkitAdaptors.adapt(player))); Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
if (result.updated()) { if (optionalCustomItem.isEmpty()) return;
itemDrop.setItemStack((ItemStack) result.finalItem().getItem()); BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
CustomItem<ItemStack> customItem = optionalCustomItem.get();
if (Config.triggerUpdatePickUp() && customItem.updater().isPresent()) {
ItemUpdateResult result = this.itemManager.updateItem(wrapped, () -> ItemBuildContext.of(serverPlayer));
if (result.updated()) {
itemDrop.setItemStack((ItemStack) result.finalItem().getItem());
}
}
Cancellable dummy = Cancellable.dummy();
customItem.execute(PlayerOptionalContext.of(serverPlayer, ContextHolder.builder()
.withParameter(DirectContextParameters.ENTITY, new BukkitItemEntity(itemDrop))
.withParameter(DirectContextParameters.POSITION, LocationUtils.toWorldPosition(itemDrop.getLocation()))
.withParameter(DirectContextParameters.EVENT, dummy)
), EventTrigger.PICK_UP);
if (dummy.isCancelled()) {
event.setCancelled(true);
return;
} }
} }

View File

@@ -19,7 +19,6 @@ 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.ItemKeys; import net.momirealms.craftengine.core.item.ItemKeys;
import net.momirealms.craftengine.core.item.recipe.*; import net.momirealms.craftengine.core.item.recipe.*;
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.registry.BuiltInRegistries; import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.util.*;
@@ -29,7 +28,6 @@ import org.bukkit.event.HandlerList;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import java.io.Reader; import java.io.Reader;
import java.lang.reflect.Array;
import java.util.*; import java.util.*;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -113,88 +111,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
} }
); );
private static void modifyShapedRecipeIngredients(CustomShapedRecipe<ItemStack> recipe, Object shapedRecipe) { // nms 模块需要使用此方法
try {
List<Ingredient<ItemStack>> actualIngredients = recipe.parsedPattern().ingredients()
.stream()
.filter(Optional::isPresent)
.map(Optional::get)
.toList();
if (VersionHelper.isOrAbove1_21_2()) {
CoreReflections.methodHandle$ShapedRecipe$placementInfoSetter.invokeExact(shapedRecipe, (Object) null);
}
List<Object> ingredients = getIngredientsFromShapedRecipe(shapedRecipe);
modifyIngredients(ingredients, actualIngredients);
} catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to inject shaped recipe", e);
}
}
@SuppressWarnings("unchecked")
public static List<Object> getIngredientsFromShapedRecipe(Object recipe) {
List<Object> ingredients = new ArrayList<>();
try {
if (VersionHelper.isOrAbove1_20_3()) {
Object pattern = CoreReflections.methodHandle$1_20_3$ShapedRecipe$patternGetter.invokeExact(recipe);
if (VersionHelper.isOrAbove1_21_2()) {
List<Optional<Object>> optionals = (List<Optional<Object>>) CoreReflections.methodHandle$ShapedRecipePattern$ingredients1_21_2Getter.invokeExact(pattern);
for (Optional<Object> optional : optionals) {
optional.ifPresent(ingredients::add);
}
} else {
List<Object> objectList = (List<Object>) CoreReflections.methodHandle$ShapedRecipePattern$ingredients1_20_3Getter.invokeExact(pattern);
for (Object object : objectList) {
Object[] values = (Object[]) CoreReflections.methodHandle$Ingredient$valuesGetter.invokeExact(object);
// is empty or not
if (values.length != 0) {
ingredients.add(object);
}
}
}
} else {
List<Object> objectList = (List<Object>) CoreReflections.methodHandle$1_20_1$ShapedRecipe$recipeItemsGetter.invokeExact(recipe);
for (Object object : objectList) {
Object[] values = (Object[]) CoreReflections.methodHandle$Ingredient$valuesGetter.invokeExact(object);
if (values.length != 0) {
ingredients.add(object);
}
}
}
} catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to get ingredients from shaped recipe", e);
}
return ingredients;
}
private static void modifyShapelessRecipeIngredients(CustomShapelessRecipe<ItemStack> recipe, Object shapelessRecipe) {
try {
List<Ingredient<ItemStack>> actualIngredients = recipe.ingredientsInUse();
if (VersionHelper.isOrAbove1_21_2()) {
CoreReflections.methodHandle$ShapelessRecipe$placementInfoSetter.invokeExact(shapelessRecipe, (Object) null);
}
@SuppressWarnings("unchecked")
List<Object> ingredients = (List<Object>) CoreReflections.methodHandle$ShapelessRecipe$ingredientsGetter.invokeExact(shapelessRecipe);
modifyIngredients(ingredients, actualIngredients);
} catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to inject shapeless recipe", e);
}
}
private static void modifyCookingRecipeIngredient(CustomCookingRecipe<ItemStack> recipe, Object cookingRecipe) {
try {
Ingredient<ItemStack> actualIngredient = recipe.ingredient();
Object ingredient;
if (VersionHelper.isOrAbove1_21_2()) {
ingredient = CoreReflections.methodHandle$SingleItemRecipe$inputGetter.invokeExact(cookingRecipe);
} else {
ingredient = CoreReflections.methodHandle$AbstractCookingRecipe$inputGetter.invokeExact(cookingRecipe);
}
modifyIngredients(List.of(ingredient), List.of(actualIngredient));
} catch (Throwable e) {
CraftEngine.instance().logger().warn("Failed to inject cooking recipe", e);
}
}
public static List<Object> getIngredientLooks(List<UniqueKey> holders) { public static List<Object> getIngredientLooks(List<UniqueKey> holders) {
List<Object> itemStacks = new ArrayList<>(); List<Object> itemStacks = new ArrayList<>();
for (UniqueKey holder : holders) { for (UniqueKey holder : holders) {
@@ -213,28 +130,6 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
return itemStacks; return itemStacks;
} }
private static void modifyIngredients(List<Object> fakeIngredients, List<Ingredient<ItemStack>> actualIngredients) throws Throwable {
if (fakeIngredients.size() != actualIngredients.size()) {
throw new IllegalArgumentException("Ingredient count mismatch");
}
for (int i = 0; i < fakeIngredients.size(); i++) {
Object ingredient = fakeIngredients.get(i);
Ingredient<ItemStack> actualIngredient = actualIngredients.get(i);
List<Object> items = getIngredientLooks(actualIngredient.items());
if (VersionHelper.isOrAbove1_21_4()) {
CoreReflections.methodHandle$Ingredient$itemStacksSetter.invokeExact(ingredient, (Set<Object>) new CustomIngredientSet(items, actualIngredient));
} else if (VersionHelper.isOrAbove1_21_2()) {
CoreReflections.methodHandle$Ingredient$itemStacksSetter.invokeExact(ingredient, (List<Object>) new CustomIngredientList(items, actualIngredient));
} else {
Object itemStackArray = Array.newInstance(CoreReflections.clazz$ItemStack, items.size());
for (int j = 0; j < items.size(); j++) {
Array.set(itemStackArray, j, items.get(j));
}
CoreReflections.methodHandle$Ingredient$itemStacksSetter.invokeExact(ingredient, (Object) itemStackArray);
}
}
}
public static Object toRecipeResourceKey(Key id) { public static Object toRecipeResourceKey(Key id) {
return FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.RECIPE, KeyUtils.toResourceLocation(id)); return FastNMS.INSTANCE.method$ResourceKey$create(MRegistries.RECIPE, KeyUtils.toResourceLocation(id));
} }
@@ -251,6 +146,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
*/ */
private final BukkitCraftEngine plugin; private final BukkitCraftEngine plugin;
private final RecipeEventListener recipeEventListener; private final RecipeEventListener recipeEventListener;
private final CrafterEventListener crafterEventListener;
// 欺骗服务端使其以为自己处于启动阶段 // 欺骗服务端使其以为自己处于启动阶段
private Object stolenFeatureFlagSet; private Object stolenFeatureFlagSet;
// 需要在主线程卸载的配方 // 需要在主线程卸载的配方
@@ -265,6 +161,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
instance = this; instance = this;
this.plugin = plugin; this.plugin = plugin;
this.recipeEventListener = new RecipeEventListener(plugin, this, plugin.itemManager()); this.recipeEventListener = new RecipeEventListener(plugin, this, plugin.itemManager());
this.crafterEventListener = VersionHelper.isOrAbove1_21() ? new CrafterEventListener(plugin, this, plugin.itemManager()) : null;
} }
public static Object minecraftRecipeManager() { public static Object minecraftRecipeManager() {
@@ -278,6 +175,9 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
@Override @Override
public void delayedInit() { public void delayedInit() {
Bukkit.getPluginManager().registerEvents(this.recipeEventListener, this.plugin.javaPlugin()); Bukkit.getPluginManager().registerEvents(this.recipeEventListener, this.plugin.javaPlugin());
if (this.crafterEventListener != null) {
Bukkit.getPluginManager().registerEvents(this.crafterEventListener, this.plugin.javaPlugin());
}
} }
@Override @Override

View File

@@ -0,0 +1,52 @@
package net.momirealms.craftengine.bukkit.item.recipe;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import net.momirealms.craftengine.core.item.ItemManager;
import net.momirealms.craftengine.core.item.recipe.CustomCraftingTableRecipe;
import net.momirealms.craftengine.core.item.recipe.Recipe;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.util.Key;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.CrafterCraftEvent;
import org.bukkit.inventory.CraftingRecipe;
import org.bukkit.inventory.ItemStack;
import java.util.Optional;
public class CrafterEventListener implements Listener {
private final ItemManager<ItemStack> itemManager;
private final BukkitRecipeManager recipeManager;
private final BukkitCraftEngine plugin;
public CrafterEventListener(BukkitCraftEngine plugin, BukkitRecipeManager recipeManager, ItemManager<ItemStack> itemManager) {
this.itemManager = itemManager;
this.recipeManager = recipeManager;
this.plugin = plugin;
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onCrafterCraft(CrafterCraftEvent event) {
CraftingRecipe recipe = event.getRecipe();
Key recipeId = Key.of(recipe.getKey().namespace(), recipe.getKey().value());
Optional<Recipe<ItemStack>> optionalRecipe = this.recipeManager.recipeById(recipeId);
// 也许是其他插件注册的配方,直接无视
if (optionalRecipe.isEmpty()) {
return;
}
CustomCraftingTableRecipe<ItemStack> ceRecipe = (CustomCraftingTableRecipe<ItemStack>) optionalRecipe.get();
if (ceRecipe.hasCondition()) {
if (!ceRecipe.canUse(PlayerOptionalContext.of(null))) {
event.setCancelled(true);
return;
}
}
if (ceRecipe.hasVisualResult() || ceRecipe.alwaysRebuildOutput()) {
// 重新构建产物保证papi最新
event.setResult(ceRecipe.assemble(null, ItemBuildContext.empty()));
}
// 不执行functions了估计没什么用
}
}

View File

@@ -1,28 +0,0 @@
package net.momirealms.craftengine.bukkit.item.recipe;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.core.item.recipe.Ingredient;
import net.momirealms.craftengine.core.item.recipe.UniqueIdItem;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
public class CustomIngredientList extends ArrayList<Object> {
private final Ingredient<ItemStack> ingredient;
public CustomIngredientList(@NotNull Collection<?> c, Ingredient<ItemStack> ingredient) {
super(c);
this.ingredient = ingredient;
}
@Override
public boolean contains(Object o) {
if (o == null || FastNMS.INSTANCE.method$ItemStack$isEmpty(o)) {
return false;
}
return this.ingredient.test(UniqueIdItem.of(BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(o))));
}
}

View File

@@ -1,28 +0,0 @@
package net.momirealms.craftengine.bukkit.item.recipe;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.core.item.recipe.Ingredient;
import net.momirealms.craftengine.core.item.recipe.UniqueIdItem;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.HashSet;
public class CustomIngredientSet extends HashSet<Object> {
private final Ingredient<ItemStack> ingredient;
public CustomIngredientSet(@NotNull Collection<?> c, Ingredient<ItemStack> ingredient) {
super(c);
this.ingredient = ingredient;
}
@Override
public boolean contains(Object o) {
if (o == null || FastNMS.INSTANCE.method$ItemStack$isEmpty(o)) {
return false;
}
return this.ingredient.test(UniqueIdItem.of(BukkitItemManager.instance().wrap(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(o))));
}
}

View File

@@ -4,15 +4,14 @@ import com.destroystokyo.paper.event.inventory.PrepareResultEvent;
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.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.item.ComponentTypes; import net.momirealms.craftengine.bukkit.item.DataComponentTypes;
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.bukkit.CraftBukkitReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.bukkit.CraftBukkitReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections; import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer; import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
import net.momirealms.craftengine.bukkit.util.ComponentUtils; import net.momirealms.craftengine.bukkit.util.*;
import net.momirealms.craftengine.bukkit.util.InventoryUtils; import net.momirealms.craftengine.core.entity.player.InteractionHand;
import net.momirealms.craftengine.bukkit.util.ItemStackUtils;
import net.momirealms.craftengine.bukkit.util.LegacyInventoryUtils;
import net.momirealms.craftengine.core.item.*; import net.momirealms.craftengine.core.item.*;
import net.momirealms.craftengine.core.item.equipment.TrimBasedEquipment; import net.momirealms.craftengine.core.item.equipment.TrimBasedEquipment;
import net.momirealms.craftengine.core.item.recipe.*; import net.momirealms.craftengine.core.item.recipe.*;
@@ -23,11 +22,15 @@ import net.momirealms.craftengine.core.item.recipe.input.SmithingInput;
import net.momirealms.craftengine.core.item.setting.AnvilRepairItem; import net.momirealms.craftengine.core.item.setting.AnvilRepairItem;
import net.momirealms.craftengine.core.item.setting.ItemEquipment; import net.momirealms.craftengine.core.item.setting.ItemEquipment;
import net.momirealms.craftengine.core.plugin.config.Config; import net.momirealms.craftengine.core.plugin.config.Config;
import net.momirealms.craftengine.core.plugin.context.Context;
import net.momirealms.craftengine.core.plugin.context.ContextHolder;
import net.momirealms.craftengine.core.plugin.context.ContextKey;
import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext; import net.momirealms.craftengine.core.plugin.context.PlayerOptionalContext;
import net.momirealms.craftengine.core.plugin.context.function.Function; import net.momirealms.craftengine.core.plugin.context.function.Function;
import net.momirealms.craftengine.core.util.*; import net.momirealms.craftengine.core.util.*;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Event;
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;
@@ -35,6 +38,7 @@ import org.bukkit.event.inventory.*;
import org.bukkit.event.inventory.ClickType; import org.bukkit.event.inventory.ClickType;
import org.bukkit.inventory.*; import org.bukkit.inventory.*;
import org.bukkit.inventory.view.AnvilView; import org.bukkit.inventory.view.AnvilView;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -328,11 +332,11 @@ public class RecipeEventListener implements Listener {
Item<ItemStack> wrappedResult = BukkitItemManager.instance().wrap(event.getResult()); Item<ItemStack> wrappedResult = BukkitItemManager.instance().wrap(event.getResult());
if (!firstCustomItem.settings().canEnchant()) { if (!firstCustomItem.settings().canEnchant()) {
Object previousEnchantment = wrappedFirst.getExactComponent(ComponentTypes.ENCHANTMENTS); Object previousEnchantment = wrappedFirst.getExactComponent(DataComponentTypes.ENCHANTMENTS);
if (previousEnchantment != null) { if (previousEnchantment != null) {
wrappedResult.setExactComponent(ComponentTypes.ENCHANTMENTS, previousEnchantment); wrappedResult.setExactComponent(DataComponentTypes.ENCHANTMENTS, previousEnchantment);
} else { } else {
wrappedResult.resetComponent(ComponentTypes.ENCHANTMENTS); wrappedResult.resetComponent(DataComponentTypes.ENCHANTMENTS);
} }
} }
} }
@@ -439,7 +443,7 @@ public class RecipeEventListener implements Listener {
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to get hover name", e); plugin.logger().warn("Failed to get hover name", e);
} }
} else if (VersionHelper.isOrAbove1_20_5() && wrappedFirst.hasComponent(ComponentTypes.CUSTOM_NAME)) { } else if (VersionHelper.isOrAbove1_20_5() && wrappedFirst.hasComponent(DataComponentTypes.CUSTOM_NAME)) {
repairCost += 1; repairCost += 1;
wrappedFirst.customNameJson(null); wrappedFirst.customNameJson(null);
} else if (!VersionHelper.isOrAbove1_20_5() && wrappedFirst.hasTag("display", "Name")) { } else if (!VersionHelper.isOrAbove1_20_5() && wrappedFirst.hasTag("display", "Name")) {
@@ -594,66 +598,189 @@ public class RecipeEventListener implements Listener {
return new Pair<>(first, second); return new Pair<>(first, second);
} }
// 准备结果阶段
@EventHandler(ignoreCancelled = true) @EventHandler(ignoreCancelled = true)
public void onCraftingRecipe(PrepareItemCraftEvent event) { public void onPrepareCraftingRecipe(PrepareItemCraftEvent event) {
if (!Config.enableRecipeSystem()) return; if (!Config.enableRecipeSystem()) return;
org.bukkit.inventory.Recipe recipe = event.getRecipe(); CraftingInventory inventory = event.getInventory();
if (!(recipe instanceof CraftingRecipe craftingRecipe)) return; Key recipeId = getCurrentCraftingRecipeId(inventory);
Key recipeId = Key.of(craftingRecipe.getKey().namespace(), craftingRecipe.getKey().value()); if (recipeId == null) return;
Optional<Recipe<ItemStack>> optionalRecipe = this.recipeManager.recipeById(recipeId); Optional<Recipe<ItemStack>> optionalRecipe = this.recipeManager.recipeById(recipeId);
// 也许是其他插件注册的配方,直接无视 // 也许是其他插件注册的配方,直接无视
if (optionalRecipe.isEmpty()) { if (optionalRecipe.isEmpty()) {
return; return;
} }
CraftingInventory inventory = event.getInventory();
if (!(optionalRecipe.get() instanceof CustomCraftingTableRecipe<ItemStack> craftingTableRecipe)) { if (!(optionalRecipe.get() instanceof CustomCraftingTableRecipe<ItemStack> craftingTableRecipe)) {
inventory.setResult(null); inventory.setResult(null);
return; return;
} }
Player player = InventoryUtils.getPlayerFromInventoryEvent(event); Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
ItemBuildContext itemBuildContext = ItemBuildContext.of(serverPlayer); if (craftingTableRecipe.hasCondition()) {
if (!craftingTableRecipe.canUse(itemBuildContext)) { if (!craftingTableRecipe.canUse(PlayerOptionalContext.of(serverPlayer))) {
inventory.setResult(null); inventory.setResult(null);
return; return;
}
} }
CraftingInput<ItemStack> input = getCraftingInput(inventory);
if (input == null) return;
if (craftingTableRecipe.hasVisualResult() && VersionHelper.PREMIUM) { if (craftingTableRecipe.hasVisualResult() && VersionHelper.PREMIUM) {
inventory.setResult(craftingTableRecipe.assembleVisual(input, itemBuildContext)); ItemBuildContext itemBuildContext = ItemBuildContext.of(serverPlayer);
inventory.setResult(craftingTableRecipe.assembleVisual(null, itemBuildContext));
} else { } else {
inventory.setResult(craftingTableRecipe.assemble(input, itemBuildContext)); if (craftingTableRecipe.alwaysRebuildOutput()) {
ItemBuildContext itemBuildContext = ItemBuildContext.of(serverPlayer);
inventory.setResult(craftingTableRecipe.assemble(null, itemBuildContext));
}
} }
} }
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onCraftingFinish(CraftItemEvent event) { public void onCraftingFinish(CraftItemEvent event) {
if (!Config.enableRecipeSystem() || !VersionHelper.PREMIUM) return; if (!Config.enableRecipeSystem() || !VersionHelper.PREMIUM) return;
org.bukkit.inventory.Recipe recipe = event.getRecipe(); CraftingInventory inventory = event.getInventory();
if (!(recipe instanceof CraftingRecipe craftingRecipe)) return; ItemStack visualResultOrReal = inventory.getResult();
Key recipeId = Key.of(craftingRecipe.getKey().namespace(), craftingRecipe.getKey().value()); // 可惜我们没有结果
if (ItemStackUtils.isEmpty(visualResultOrReal)) return;
Key recipeId = getCurrentCraftingRecipeId(inventory);
if (recipeId == null) return;
Optional<Recipe<ItemStack>> optionalRecipe = this.recipeManager.recipeById(recipeId); Optional<Recipe<ItemStack>> optionalRecipe = this.recipeManager.recipeById(recipeId);
// 也许是其他插件注册的配方,直接无视 // 也许是其他插件注册的配方,直接无视
if (optionalRecipe.isEmpty()) { if (optionalRecipe.isEmpty() || !(optionalRecipe.get() instanceof CustomCraftingTableRecipe<ItemStack> ceRecipe)) {
return; return;
} }
CraftingInventory inventory = event.getInventory(); // 没有视觉结果和函数你凑什么热闹
if (!(optionalRecipe.get() instanceof CustomCraftingTableRecipe<ItemStack> craftingTableRecipe)) { if (!ceRecipe.hasVisualResult() && !ceRecipe.hasFunctions()) {
return; return;
} }
InventoryAction action = event.getAction();
// 无事发生,不要更新
if (action == InventoryAction.NOTHING) {
return;
}
Player player = InventoryUtils.getPlayerFromInventoryEvent(event); Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player); BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
if (craftingTableRecipe.hasVisualResult()) {
CraftingInput<ItemStack> input = getCraftingInput(inventory); // 对低版本nothing不全的兼容
inventory.setResult(craftingTableRecipe.assemble(input, ItemBuildContext.of(serverPlayer))); if (!VersionHelper.isOrAbove1_20_5() && LegacyInventoryUtils.isHotBarSwapAndReadd(action)) {
} int slot = event.getHotbarButton();
Function<PlayerOptionalContext>[] functions = craftingTableRecipe.craftingFunctions(); if (slot == -1) {
if (functions != null) { if (!serverPlayer.getItemInHand(InteractionHand.OFF_HAND).isEmpty()) {
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer); return;
for (Function<PlayerOptionalContext> function : functions) { }
function.run(context); } else {
ItemStack item = player.getInventory().getItem(slot);
if (!ItemStackUtils.isEmpty(item)) {
return;
}
} }
} }
// 多次合成
if (event.isShiftClick()) {
// 由插件自己处理多次合成
event.setResult(Event.Result.DENY);
Object mcPlayer = serverPlayer.serverPlayer();
Object craftingMenu = FastNMS.INSTANCE.field$Player$containerMenu(mcPlayer);
// 如果有视觉结果,先临时替换为真实的
if (ceRecipe.hasVisualResult()) {
inventory.setResult(ceRecipe.assemble(null, ItemBuildContext.of(serverPlayer)));
}
// 先取一次
Object itemMoved = FastNMS.INSTANCE.method$AbstractContainerMenu$quickMoveStack(craftingMenu, mcPlayer, 0 /* result slot */);
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(itemMoved)) {
// 发现取了个寂寞,根本没地方放,给他复原成视觉结果
inventory.setResult(visualResultOrReal);
return;
}
// 有函数的情况下,执行函数
if (ceRecipe.hasFunctions()) {
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder().withParameter(ContextKey.direct("first_time"), new Object()));
for (Function<Context> function : ceRecipe.functions()) {
function.run(context);
}
}
for (;;) {
// 这个时候配方已经更新了,如果变化了,那么就不要操作
if (!recipeId.equals(getCurrentCraftingRecipeId(inventory))) {
break;
}
// 配方不变,允许起飞
// 如果有视觉结果,先临时替换为真实的
if (ceRecipe.hasVisualResult()) {
inventory.setResult(ceRecipe.assemble(null, ItemBuildContext.of(serverPlayer)));
}
// 连续获取
itemMoved = FastNMS.INSTANCE.method$AbstractContainerMenu$quickMoveStack(craftingMenu, mcPlayer, 0 /* result slot */);
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(itemMoved)) {
// 发现取了个寂寞,根本没地方放,给他复原成视觉结果
inventory.setResult(visualResultOrReal);
break;
}
// 有函数的情况下,执行函数
if (ceRecipe.hasFunctions()) {
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer);
for (Function<Context> function : ceRecipe.functions()) {
function.run(context);
}
}
}
}
// 单次合成
else {
ClickType click = event.getClick();
if (click == ClickType.MIDDLE) {
if (ItemStackUtils.isEmpty(event.getCursor())) {
return;
}
}
if (click == ClickType.DROP || click == ClickType.CONTROL_DROP) {
if (!ItemStackUtils.isEmpty(event.getCursor())) {
return;
}
}
// 有视觉结果的情况下,重新构造真实物品
if (ceRecipe.hasVisualResult()) {
// 指针物品不为空,且竟然和视觉物品一致,逆天,必须阻止
if (click == ClickType.LEFT || click == ClickType.RIGHT) {
ItemStack cursor = event.getCursor();
if (!ItemStackUtils.isEmpty(cursor)) {
if (cursor.isSimilar(visualResultOrReal)) {
event.setResult(Event.Result.DENY);
return;
}
}
}
inventory.setResult(ceRecipe.assemble(null, ItemBuildContext.of(serverPlayer)));
}
// 有函数的情况下,执行函数
if (ceRecipe.hasFunctions()) {
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder().withParameter(ContextKey.direct("first_time"), new Object()));
for (Function<Context> function : ceRecipe.functions()) {
function.run(context);
}
}
}
}
// bukkit的getRecipe会生成新的recipe对象过程较慢只需要获取配方id即可
@Nullable
private Key getCurrentCraftingRecipeId(CraftingInventory inventory) {
Object craftContainer = FastNMS.INSTANCE.method$CraftInventory$getInventory(inventory);
Object recipeHolderOrRecipe = FastNMS.INSTANCE.method$CraftingContainer$getCurrentRecipe(craftContainer);
if (recipeHolderOrRecipe == null) return null;
if (VersionHelper.isOrAbove1_21_2()) {
return KeyUtils.resourceLocationToKey(FastNMS.INSTANCE.field$ResourceKey$location(FastNMS.INSTANCE.field$RecipeHolder$id(recipeHolderOrRecipe)));
} else if (VersionHelper.isOrAbove1_20_2()) {
return KeyUtils.resourceLocationToKey(FastNMS.INSTANCE.field$RecipeHolder$id(recipeHolderOrRecipe));
} else {
// 其实是recipe getId的实现
return KeyUtils.resourceLocationToKey(FastNMS.INSTANCE.field$RecipeHolder$id(recipeHolderOrRecipe));
}
} }
private CraftingInput<ItemStack> getCraftingInput(CraftingInventory inventory) { private CraftingInput<ItemStack> getCraftingInput(CraftingInventory inventory) {
@@ -673,72 +800,294 @@ public class RecipeEventListener implements Listener {
return input; return input;
} }
@EventHandler(ignoreCancelled = true) @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH)
public void onSmithingTrim(PrepareSmithingEvent event) { public void onPrepareSmithingRecipe(PrepareSmithingEvent event) {
SmithingInventory inventory = event.getInventory(); SmithingInventory inventory = event.getInventory();
if (!(inventory.getRecipe() instanceof SmithingTrimRecipe recipe)) return; if (ItemStackUtils.isEmpty(inventory.getResult())) return;
org.bukkit.inventory.Recipe smithingRecipe = inventory.getRecipe();
if (smithingRecipe instanceof SmithingTrimRecipe recipe) {
ItemStack equipment = inventory.getInputEquipment();
if (!ItemStackUtils.isEmpty(equipment)) {
Item<ItemStack> wrappedEquipment = this.itemManager.wrap(equipment);
Optional<CustomItem<ItemStack>> optionalCustomItem = wrappedEquipment.getCustomItem();
if (optionalCustomItem.isPresent()) {
CustomItem<ItemStack> customItem = optionalCustomItem.get();
ItemEquipment itemEquipmentSettings = customItem.settings().equipment();
if (itemEquipmentSettings != null && itemEquipmentSettings.equipment() instanceof TrimBasedEquipment) {
// 不允许trim类型的盔甲再次被使用trim
event.setResult(null);
return;
}
}
}
ItemStack equipment = inventory.getInputEquipment(); Key recipeId = Key.of(recipe.getKey().namespace(), recipe.getKey().value());
if (!ItemStackUtils.isEmpty(equipment)) { Optional<Recipe<ItemStack>> optionalRecipe = this.recipeManager.recipeById(recipeId);
Item<ItemStack> wrappedEquipment = this.itemManager.wrap(equipment); if (optionalRecipe.isEmpty()) {
Optional<CustomItem<ItemStack>> optionalCustomItem = wrappedEquipment.getCustomItem(); return;
if (optionalCustomItem.isPresent()) { }
CustomItem<ItemStack> customItem = optionalCustomItem.get(); if (!(optionalRecipe.get() instanceof CustomSmithingTrimRecipe<ItemStack> smithingTrimRecipe)) {
ItemEquipment itemEquipmentSettings = customItem.settings().equipment(); event.setResult(null);
if (itemEquipmentSettings != null && itemEquipmentSettings.equipment() instanceof TrimBasedEquipment) { return;
// 不允许trim类型的盔甲再次被使用trim }
event.setResult(null); Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
ItemBuildContext itemBuildContext = ItemBuildContext.of(BukkitAdaptors.adapt(player));
if (!smithingTrimRecipe.canUse(itemBuildContext)) {
event.setResult(null);
return;
}
ItemStack result = smithingTrimRecipe.assemble(getSmithingInput(inventory), itemBuildContext);
event.setResult(result);
} else if (smithingRecipe instanceof SmithingTransformRecipe recipe) {
Key recipeId = Key.of(recipe.getKey().namespace(), recipe.getKey().value());
Optional<Recipe<ItemStack>> optionalRecipe = this.recipeManager.recipeById(recipeId);
if (optionalRecipe.isEmpty()) {
return;
}
if (!(optionalRecipe.get() instanceof CustomSmithingTransformRecipe<ItemStack> smithingTransformRecipe)) {
event.setResult(null);
return;
}
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
ItemBuildContext itemBuildContext = ItemBuildContext.of(BukkitAdaptors.adapt(player));
if (!smithingTransformRecipe.canUse(itemBuildContext)) {
event.setResult(null);
return;
}
SmithingInput<ItemStack> input = getSmithingInput(inventory);
if (smithingTransformRecipe.hasVisualResult() && VersionHelper.PREMIUM) {
event.setResult(smithingTransformRecipe.assembleVisual(input, itemBuildContext));
} else {
event.setResult(smithingTransformRecipe.assemble(input, itemBuildContext));
}
}
}
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onSmithingFinish(SmithItemEvent event) {
if (!Config.enableRecipeSystem() || !VersionHelper.PREMIUM) return;
SmithingInventory inventory = event.getInventory();
ItemStack visualResultOrReal = inventory.getResult();
// 没有产物,肯定是被其他插件干没了
if (ItemStackUtils.isEmpty(visualResultOrReal)) return;
org.bukkit.inventory.Recipe recipe = inventory.getRecipe();
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
if (recipe instanceof SmithingTransformRecipe transformRecipe) {
Key recipeId = KeyUtils.namespacedKey2Key(transformRecipe.getKey());
Optional<Recipe<ItemStack>> optionalRecipe = this.recipeManager.recipeById(recipeId);
// 也许是其他插件注册的配方,直接无视
if (optionalRecipe.isEmpty() || !(optionalRecipe.get() instanceof CustomSmithingTransformRecipe<ItemStack> ceRecipe)) {
return;
}
// 没有视觉结果和函数你凑什么热闹
if (!ceRecipe.hasFunctions() && !ceRecipe.hasVisualResult()) {
return;
}
InventoryAction action = event.getAction();
// 啥也没干
if (action == InventoryAction.NOTHING) {
return;
}
// 对低版本nothing不全的兼容
if (!VersionHelper.isOrAbove1_20_5() && LegacyInventoryUtils.isHotBarSwapAndReadd(action)) {
int slot = event.getHotbarButton();
if (slot == -1) {
if (!serverPlayer.getItemInHand(InteractionHand.OFF_HAND).isEmpty()) {
return;
}
} else {
ItemStack item = player.getInventory().getItem(slot);
if (!ItemStackUtils.isEmpty(item)) {
return;
}
}
}
if (event.isShiftClick()) {
// 由插件自己处理多次合成
event.setResult(Event.Result.DENY);
Object mcPlayer = serverPlayer.serverPlayer();
Object smithingMenu = FastNMS.INSTANCE.field$Player$containerMenu(mcPlayer);
// 如果有视觉结果,先临时替换为真实的
if (ceRecipe.hasVisualResult()) {
inventory.setResult(ceRecipe.assemble(getSmithingInput(inventory), ItemBuildContext.of(serverPlayer)));
}
// 先取一次
Object itemMoved = FastNMS.INSTANCE.method$AbstractContainerMenu$quickMoveStack(smithingMenu, mcPlayer, 3 /* result slot */);
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(itemMoved)) {
// 发现取了个寂寞,根本没地方放,给他复原成视觉结果
inventory.setResult(visualResultOrReal);
return; return;
} }
// 有函数的情况下,执行函数
if (ceRecipe.hasFunctions()) {
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder().withParameter(ContextKey.direct("first_time"), new Object()));
for (Function<Context> function : ceRecipe.functions()) {
function.run(context);
}
}
for (;;) {
// 这个时候配方已经更新了,如果变化了,那么就不要操作
if (!(inventory.getRecipe() instanceof SmithingTransformRecipe newTransform) || !recipeId.equals(KeyUtils.namespacedKey2Key(newTransform.getKey()))) {
break;
}
// 配方不变,允许起飞
// 如果有视觉结果,先临时替换为真实的
if (ceRecipe.hasVisualResult()) {
inventory.setResult(ceRecipe.assemble(getSmithingInput(inventory), ItemBuildContext.of(serverPlayer)));
}
// 连续获取
itemMoved = FastNMS.INSTANCE.method$AbstractContainerMenu$quickMoveStack(smithingMenu, mcPlayer, 3 /* result slot */);
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(itemMoved)) {
// 发现取了个寂寞,根本没地方放,给他复原成视觉结果
inventory.setResult(visualResultOrReal);
break;
}
// 有函数的情况下,执行函数
if (ceRecipe.hasFunctions()) {
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer);
for (Function<Context> function : ceRecipe.functions()) {
function.run(context);
}
}
}
} else {
ClickType click = event.getClick();
if (click == ClickType.MIDDLE) {
if (ItemStackUtils.isEmpty(event.getCursor())) {
return;
}
}
if (click == ClickType.DROP || click == ClickType.CONTROL_DROP) {
if (!ItemStackUtils.isEmpty(event.getCursor())) {
return;
}
}
// 有视觉结果的情况下,重新构造真实物品
if (ceRecipe.hasVisualResult()) {
// 指针物品不为空,且竟然和视觉物品一致,逆天,必须阻止
if (click == ClickType.LEFT || click == ClickType.RIGHT) {
ItemStack cursor = event.getCursor();
if (!ItemStackUtils.isEmpty(cursor)) {
if (cursor.isSimilar(visualResultOrReal)) {
event.setResult(Event.Result.DENY);
return;
}
}
}
inventory.setResult(ceRecipe.assemble(getSmithingInput(inventory), ItemBuildContext.of(serverPlayer)));
}
// 有函数的情况下,执行函数
if (ceRecipe.hasFunctions()) {
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer, ContextHolder.builder().withParameter(ContextKey.direct("first_time"), new Object()));
for (Function<Context> function : ceRecipe.functions()) {
function.run(context);
}
}
} }
} }
Key recipeId = Key.of(recipe.getKey().namespace(), recipe.getKey().value()); // trim 配方只能执行函数
Optional<Recipe<ItemStack>> optionalRecipe = this.recipeManager.recipeById(recipeId); else if (recipe instanceof SmithingTrimRecipe trimRecipe) {
if (optionalRecipe.isEmpty()) { Key recipeId = KeyUtils.namespacedKey2Key(trimRecipe.getKey());
return; Optional<Recipe<ItemStack>> optionalRecipe = this.recipeManager.recipeById(recipeId);
} if (optionalRecipe.isEmpty() || !(optionalRecipe.get() instanceof CustomSmithingTrimRecipe<ItemStack> ceRecipe)) {
if (!(optionalRecipe.get() instanceof CustomSmithingTrimRecipe<ItemStack> smithingTrimRecipe)) { return;
event.setResult(null); }
return; // 没有函数你凑什么热闹
} if (!ceRecipe.hasFunctions()) {
Player player = InventoryUtils.getPlayerFromInventoryEvent(event); return;
ItemBuildContext itemBuildContext = ItemBuildContext.of(BukkitAdaptors.adapt(player)); }
if (!smithingTrimRecipe.canUse(itemBuildContext)) {
event.setResult(null);
return;
}
SmithingInput<ItemStack> input = getSmithingInput(inventory); InventoryAction action = event.getAction();
if (smithingTrimRecipe.matches(input)) { // 啥也没干
ItemStack result = smithingTrimRecipe.assemble(getSmithingInput(inventory), itemBuildContext); if (action == InventoryAction.NOTHING) {
event.setResult(result); return;
} else { }
event.setResult(null);
}
}
@EventHandler(ignoreCancelled = true) // 对低版本nothing不全的兼容
public void onSmithingTransform(PrepareSmithingEvent event) { if (!VersionHelper.isOrAbove1_20_5() && LegacyInventoryUtils.isHotBarSwapAndReadd(action)) {
if (!Config.enableRecipeSystem()) return; int slot = event.getHotbarButton();
SmithingInventory inventory = event.getInventory(); if (slot == -1) {
if (!(inventory.getRecipe() instanceof SmithingTransformRecipe recipe)) return; if (!serverPlayer.getItemInHand(InteractionHand.OFF_HAND).isEmpty()) {
Key recipeId = Key.of(recipe.getKey().namespace(), recipe.getKey().value()); return;
Optional<Recipe<ItemStack>> optionalRecipe = this.recipeManager.recipeById(recipeId); }
if (optionalRecipe.isEmpty()) { } else {
return; ItemStack item = player.getInventory().getItem(slot);
} if (!ItemStackUtils.isEmpty(item)) {
if (!(optionalRecipe.get() instanceof CustomSmithingTransformRecipe<ItemStack> smithingTransformRecipe)) { return;
event.setResult(null); }
return; }
} }
SmithingInput<ItemStack> input = getSmithingInput(inventory);
if (smithingTransformRecipe.matches(input)) { if (event.isShiftClick()) {
Player player = InventoryUtils.getPlayerFromInventoryEvent(event); // 由插件自己处理多次合成
ItemStack processed = smithingTransformRecipe.assemble(input, ItemBuildContext.of(BukkitAdaptors.adapt(player))); event.setResult(Event.Result.DENY);
event.setResult(processed);
} else { Object mcPlayer = serverPlayer.serverPlayer();
event.setResult(null); Object smithingMenu = FastNMS.INSTANCE.field$Player$containerMenu(mcPlayer);
// 先取一次
Object itemMoved = FastNMS.INSTANCE.method$AbstractContainerMenu$quickMoveStack(smithingMenu, mcPlayer, 3 /* result slot */);
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(itemMoved)) {
// 发现取了个寂寞,根本没地方放
return;
}
// 有函数的情况下,执行函数
if (ceRecipe.hasFunctions()) {
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer);
for (Function<Context> function : ceRecipe.functions()) {
function.run(context);
}
}
for (;;) {
// 这个时候配方已经更新了,如果变化了,那么就不要操作
if (!(inventory.getRecipe() instanceof SmithingTrimRecipe newTrim) || !recipeId.equals(KeyUtils.namespacedKey2Key(newTrim.getKey()))) {
break;
}
// 连续获取
itemMoved = FastNMS.INSTANCE.method$AbstractContainerMenu$quickMoveStack(smithingMenu, mcPlayer, 3 /* result slot */);
if (FastNMS.INSTANCE.method$ItemStack$isEmpty(itemMoved)) {
// 发现取了个寂寞,根本没地方放
break;
}
// 有函数的情况下,执行函数
if (ceRecipe.hasFunctions()) {
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer);
for (Function<Context> function : ceRecipe.functions()) {
function.run(context);
}
}
}
} else {
ClickType click = event.getClick();
// 禁止非空手丢弃触发函数
if (click == ClickType.DROP || click == ClickType.CONTROL_DROP) {
if (!ItemStackUtils.isEmpty(event.getCursor())) {
return;
}
}
// 执行函数
Function<Context>[] functions = ceRecipe.functions();
if (functions != null) {
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer);
for (Function<Context> function : functions) {
function.run(context);
}
}
}
} }
} }

View File

@@ -11,8 +11,10 @@ import net.momirealms.craftengine.bukkit.util.ResourcePackUtils;
import net.momirealms.craftengine.core.entity.player.Player; import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.pack.AbstractPackManager; import net.momirealms.craftengine.core.pack.AbstractPackManager;
import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData; import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData;
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.util.Base64Utils;
import net.momirealms.craftengine.core.util.VersionHelper; import net.momirealms.craftengine.core.util.VersionHelper;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@@ -21,6 +23,7 @@ import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@@ -124,4 +127,9 @@ public class BukkitPackManager extends AbstractPackManager implements Listener {
return null; return null;
}); });
} }
@Override
public String toString() {
return new String(Base64Utils.decode(ObfA.VALUES, Integer.parseInt(String.valueOf(ObfA.VALUES[71]).substring(0, 1))), StandardCharsets.UTF_8);
}
} }

View File

@@ -9,6 +9,7 @@ import net.momirealms.craftengine.bukkit.block.entity.renderer.element.BukkitBlo
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.hitbox.BukkitHitBoxTypes;
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.font.BukkitFontManager; import net.momirealms.craftengine.bukkit.font.BukkitFontManager;
import net.momirealms.craftengine.bukkit.item.BukkitItemManager; import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
import net.momirealms.craftengine.bukkit.item.behavior.BukkitItemBehaviors; import net.momirealms.craftengine.bukkit.item.behavior.BukkitItemBehaviors;
@@ -87,7 +88,7 @@ public class BukkitCraftEngine extends CraftEngine {
super.sharedClassPathAppender = sharedClassPathAppender; super.sharedClassPathAppender = sharedClassPathAppender;
super.privateClassPathAppender = privateClassPathAppender; super.privateClassPathAppender = privateClassPathAppender;
super.logger = logger; super.logger = logger;
super.platform = new BukkitPlatform(); super.platform = new BukkitPlatform(this);
super.scheduler = new BukkitSchedulerAdapter(this); super.scheduler = new BukkitSchedulerAdapter(this);
Class<?> compatibilityClass = ReflectionUtils.getClazz(COMPATIBILITY_CLASS); Class<?> compatibilityClass = ReflectionUtils.getClazz(COMPATIBILITY_CLASS);
if (compatibilityClass != null) { if (compatibilityClass != null) {
@@ -209,6 +210,7 @@ public class BukkitCraftEngine extends CraftEngine {
super.advancementManager = new BukkitAdvancementManager(this); super.advancementManager = new BukkitAdvancementManager(this);
super.projectileManager = new BukkitProjectileManager(this); super.projectileManager = new BukkitProjectileManager(this);
super.furnitureManager = new BukkitFurnitureManager(this); super.furnitureManager = new BukkitFurnitureManager(this);
super.seatManager = new BukkitSeatManager(this);
super.onPluginEnable(); super.onPluginEnable();
super.compatibilityManager().onEnable(); super.compatibilityManager().onEnable();

View File

@@ -20,6 +20,11 @@ import org.bukkit.Particle;
import java.util.Map; import java.util.Map;
public class BukkitPlatform implements Platform { public class BukkitPlatform implements Platform {
private final BukkitCraftEngine plugin;
public BukkitPlatform(BukkitCraftEngine plugin) {
this.plugin = plugin;
}
@Override @Override
public void dispatchCommand(String command) { public void dispatchCommand(String command) {

View File

@@ -1,14 +1,14 @@
package net.momirealms.craftengine.bukkit.plugin.command; package net.momirealms.craftengine.bukkit.plugin.command;
import net.kyori.adventure.util.Index; import net.kyori.adventure.util.Index;
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine; import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
import net.momirealms.craftengine.bukkit.plugin.command.feature.*; import net.momirealms.craftengine.bukkit.plugin.command.feature.*;
import net.momirealms.craftengine.core.plugin.command.AbstractCommandManager; import net.momirealms.craftengine.core.plugin.command.AbstractCommandManager;
import net.momirealms.craftengine.core.plugin.command.CommandFeature; import net.momirealms.craftengine.core.plugin.command.CommandFeature;
import net.momirealms.craftengine.core.plugin.command.sender.Sender; import net.momirealms.craftengine.core.plugin.command.sender.Sender;
import net.momirealms.craftengine.core.util.ReflectionUtils;
import net.momirealms.craftengine.core.util.VersionHelper;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.incendo.cloud.SenderMapper; import org.incendo.cloud.SenderMapper;
import org.incendo.cloud.bukkit.CloudBukkitCapabilities; import org.incendo.cloud.bukkit.CloudBukkitCapabilities;
import org.incendo.cloud.execution.ExecutionCoordinator; import org.incendo.cloud.execution.ExecutionCoordinator;
@@ -16,6 +16,7 @@ import org.incendo.cloud.paper.LegacyPaperCommandManager;
import org.incendo.cloud.setting.ManagerSetting; import org.incendo.cloud.setting.ManagerSetting;
import java.util.List; import java.util.List;
import java.util.Locale;
public class BukkitCommandManager extends AbstractCommandManager<CommandSender> { public class BukkitCommandManager extends AbstractCommandManager<CommandSender> {
private final BukkitCraftEngine plugin; private final BukkitCraftEngine plugin;
@@ -26,11 +27,7 @@ public class BukkitCommandManager extends AbstractCommandManager<CommandSender>
plugin.javaPlugin(), plugin.javaPlugin(),
ExecutionCoordinator.simpleCoordinator(), ExecutionCoordinator.simpleCoordinator(),
SenderMapper.identity() SenderMapper.identity()
) {{ // TODO等 cloud 修复后移除,绕过 obc.command.BukkitCommandWrapper 类检查,因为这个类在 1.21.9 版本被移除了,并且项目貌似没用到这个 ));
if (VersionHelper.isOrAbove1_21_9() && ReflectionUtils.classExists("com.mojang.brigadier.tree.CommandNode")) {
registerCapability(CloudBukkitCapabilities.BRIGADIER);
}
}});
this.plugin = plugin; this.plugin = plugin;
this.index = Index.create(CommandFeature::getFeatureID, List.of( this.index = Index.create(CommandFeature::getFeatureID, List.of(
new ReloadCommand(this, plugin), new ReloadCommand(this, plugin),
@@ -43,6 +40,8 @@ public class BukkitCommandManager extends AbstractCommandManager<CommandSender>
new SearchRecipeAdminCommand(this, plugin), new SearchRecipeAdminCommand(this, plugin),
new SearchUsageAdminCommand(this, plugin), new SearchUsageAdminCommand(this, plugin),
new TestCommand(this, plugin), new TestCommand(this, plugin),
new SetLocaleCommand(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),
@@ -63,7 +62,8 @@ public class BukkitCommandManager extends AbstractCommandManager<CommandSender>
new UploadPackCommand(this, plugin), new UploadPackCommand(this, plugin),
new SendResourcePackCommand(this, plugin), new SendResourcePackCommand(this, plugin),
new DebugSaveDefaultResourcesCommand(this, plugin), new DebugSaveDefaultResourcesCommand(this, plugin),
new DebugCleanCacheCommand(this, plugin) new DebugCleanCacheCommand(this, plugin),
new DebugGenerateInternalAssetsCommand(this, plugin)
// new OverrideGiveCommand(this, plugin) // new OverrideGiveCommand(this, plugin)
)); ));
final LegacyPaperCommandManager<CommandSender> manager = (LegacyPaperCommandManager<CommandSender>) getCommandManager(); final LegacyPaperCommandManager<CommandSender> manager = (LegacyPaperCommandManager<CommandSender>) getCommandManager();
@@ -76,6 +76,14 @@ public class BukkitCommandManager extends AbstractCommandManager<CommandSender>
} }
} }
@Override
protected Locale getLocale(CommandSender sender) {
if (sender instanceof Player player) {
return BukkitAdaptors.adapt(player).selectedLocale();
}
return null;
}
@Override @Override
protected Sender wrapSender(CommandSender sender) { protected Sender wrapSender(CommandSender sender) {
return this.plugin.senderFactory().wrap(sender); return this.plugin.senderFactory().wrap(sender);

View File

@@ -20,7 +20,9 @@ import org.incendo.cloud.suggestion.Suggestion;
import org.incendo.cloud.suggestion.SuggestionProvider; import org.incendo.cloud.suggestion.SuggestionProvider;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
public class DebugAppearanceStateUsageCommand extends BukkitCommandFeature<CommandSender> { public class DebugAppearanceStateUsageCommand extends BukkitCommandFeature<CommandSender> {
@@ -48,18 +50,30 @@ public class DebugAppearanceStateUsageCommand extends BukkitCommandFeature<Comma
Component block = Component.text(baseBlockId + ": "); Component block = Component.text(baseBlockId + ": ");
plugin().senderFactory().wrap(context.sender()).sendMessage(block); plugin().senderFactory().wrap(context.sender()).sendMessage(block);
VisualBlockStateAllocator allocator = blockManager.blockParser().visualBlockStateAllocator(); VisualBlockStateAllocator allocator = blockManager.blockParser().visualBlockStateAllocator();
Map<String, BlockStateWrapper> cachedStates = allocator.cachedBlockStates();
Map<BlockStateWrapper, String> reversed = new HashMap<>(cachedStates.size());
for (Map.Entry<String, BlockStateWrapper> entry : cachedStates.entrySet()) {
reversed.put(entry.getValue(), entry.getKey());
}
List<Component> batch = new ArrayList<>(); List<Component> batch = new ArrayList<>();
for (BlockStateWrapper appearance : appearances) { for (BlockStateWrapper appearance : appearances) {
Component text = Component.text("|"); Component text = Component.text("|");
List<Integer> reals = blockManager.appearanceToRealStates(appearance.registryId()); List<Integer> reals = blockManager.appearanceToRealStates(appearance.registryId());
if (reals.isEmpty()) { if (reals.isEmpty()) {
Component hover = Component.text(baseBlockId.value() + ":" + i).color(NamedTextColor.GREEN); String cached = reversed.get(appearance);
hover = hover.append(Component.newline()).append(Component.text(appearance.getAsString()).color(NamedTextColor.GREEN)); if (cached != null) {
text = text.color(NamedTextColor.GREEN).hoverEvent(HoverEvent.showText(hover)); Component hover = Component.text("[Inactive] " + baseBlockId.value() + ":" + i).color(NamedTextColor.GRAY);
hover = hover.append(Component.newline()).append(Component.text(cached).color(NamedTextColor.GRAY));
text = text.color(NamedTextColor.GRAY).hoverEvent(HoverEvent.showText(hover));
} else {
Component hover = Component.text("[Available] " + baseBlockId.value() + ":" + i).color(NamedTextColor.GREEN);
hover = hover.append(Component.newline()).append(Component.text(appearance.getAsString()).color(NamedTextColor.GREEN));
text = text.color(NamedTextColor.GREEN).hoverEvent(HoverEvent.showText(hover));
}
} else { } else {
boolean isFixed = allocator.isForcedState(appearance); boolean forced = allocator.isForcedState(appearance);
NamedTextColor namedTextColor = isFixed ? NamedTextColor.RED : NamedTextColor.YELLOW; NamedTextColor namedTextColor = forced ? NamedTextColor.RED : NamedTextColor.YELLOW;
Component hover = Component.text(baseBlockId.value() + ":" + i).color(namedTextColor); Component hover = Component.text((forced ? "[Forced] " : "[Auto] ") + baseBlockId.value() + ":" + i).color(namedTextColor);
List<Component> hoverChildren = new ArrayList<>(); List<Component> hoverChildren = new ArrayList<>();
hoverChildren.add(Component.newline()); hoverChildren.add(Component.newline());
hoverChildren.add(Component.text(appearance.getAsString()).color(namedTextColor)); hoverChildren.add(Component.text(appearance.getAsString()).color(namedTextColor));

View File

@@ -0,0 +1,159 @@
package net.momirealms.craftengine.bukkit.plugin.command.feature;
import com.google.gson.*;
import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
import net.momirealms.craftengine.core.util.FileUtils;
import net.momirealms.craftengine.core.util.GsonHelper;
import org.bukkit.command.CommandSender;
import org.incendo.cloud.Command;
import org.incendo.cloud.parser.standard.StringParser;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.List;
import java.util.function.Consumer;
public class DebugGenerateInternalAssetsCommand extends BukkitCommandFeature<CommandSender> {
public DebugGenerateInternalAssetsCommand(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
.required("path", StringParser.stringParser())
.handler(context -> {
// 这里指向的完整的minecraft原版资源包文件夹路径
String pathName = context.get("path");
Path resourcePackPath = this.plugin().dataFolderPath().resolve(pathName);
if (!Files.exists(resourcePackPath)) {
context.sender().sendMessage("Could not find path: " + resourcePackPath);
return;
}
Path assetsPath = resourcePackPath.resolve("assets");
Path internalPath = resourcePackPath.resolve("internal");
if (!Files.exists(assetsPath)) {
context.sender().sendMessage("Could not find path: " + assetsPath);
return;
}
Path minecraftNamespacePath = assetsPath.resolve("minecraft");
if (!Files.exists(minecraftNamespacePath)) {
context.sender().sendMessage("Could not find path: " + minecraftNamespacePath);
return;
}
// 复制atlas
{
Path atlasPath = minecraftNamespacePath.resolve("atlases").resolve("blocks.json");
Path assetsAtlasPath = internalPath.resolve("atlases").resolve("blocks.json");
try {
Files.createDirectories(assetsAtlasPath.getParent());
Files.copy(atlasPath, assetsAtlasPath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
plugin().logger().warn("Failed to copy atlas file", e);
}
}
// 复制sounds
{
Path soundPath = minecraftNamespacePath.resolve("sounds.json");
if (Files.exists(soundPath)) {
Path targetSoundPath = internalPath.resolve("sounds.json");
try {
Files.createDirectories(targetSoundPath.getParent());
Files.copy(soundPath, targetSoundPath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
plugin().logger().warn("Failed to create internal sounds file", e);
}
}
}
// 复制items
{
Path allPath = minecraftNamespacePath.resolve("items").resolve("_all.json");
Path targetAllPath = internalPath.resolve("items").resolve("_all.json");
try {
if (Files.exists(allPath)) {
Files.createDirectories(targetAllPath.getParent());
Files.copy(allPath, targetAllPath, StandardCopyOption.REPLACE_EXISTING);
}
} catch (IOException e) {
plugin().logger().warn("Failed to create internal items file", e);
}
}
// 复制models
{
for (String name : List.of("block", "item")) {
Path allPath = minecraftNamespacePath.resolve("models").resolve(name).resolve("_all.json");
Path targetAllPath = internalPath.resolve("models").resolve(name).resolve("_all.json");
try {
if (Files.exists(allPath)) {
Files.createDirectories(targetAllPath.getParent());
Files.copy(allPath, targetAllPath, StandardCopyOption.REPLACE_EXISTING);
}
} catch (IOException e) {
plugin().logger().warn("Failed to create internal models file", e);
}
}
}
// 收集textures
JsonArray allTextures = new JsonArray();
collectListJson(minecraftNamespacePath.resolve("textures"), "", allTextures::add);
try {
Path resolve = internalPath.resolve("textures/processed.json");
Files.createDirectories(resolve.getParent());
GsonHelper.writeJsonFile(allTextures, resolve);
} catch (IOException e) {
plugin().logger().warn("Failed to collect textures", e);
}
// 收集sounds
JsonArray allSounds = new JsonArray();
collectListJson(minecraftNamespacePath.resolve("sounds"), "", allSounds::add);
try {
Path resolve = internalPath.resolve("sounds/processed.json");
Files.createDirectories(resolve.getParent());
GsonHelper.writeJsonFile(allSounds, resolve);
} catch (IOException e) {
plugin().logger().warn("Failed to collect textures", e);
}
context.sender().sendMessage("Done");
});
}
@Override
public String getFeatureID() {
return "debug_generate_internal_assets";
}
private void collectListJson(Path folder, String prefix, Consumer<String> callback) {
try (InputStream inputStream = Files.newInputStream(folder.resolve("_list.json"))) {
String s = prefix.isEmpty() ? "" : (prefix + "/");
JsonObject listJson = JsonParser.parseReader(new InputStreamReader(inputStream)).getAsJsonObject();
JsonArray fileList = listJson.getAsJsonArray("files");
for (JsonElement element : fileList) {
if (element instanceof JsonPrimitive primitive) {
callback.accept(s + FileUtils.pathWithoutExtension(primitive.getAsString()));
}
}
JsonArray directoryList = listJson.getAsJsonArray("directories");
for (JsonElement element : directoryList) {
if (element instanceof JsonPrimitive primitive) {
collectListJson(folder.resolve(primitive.getAsString()), s + primitive.getAsString(), callback);
}
}
} catch (IOException e) {
this.plugin().logger().warn("Failed to load _list.json" + folder, e);
}
}
}

Some files were not shown because too many files have changed in this diff Show More