mirror of
https://github.com/Xiao-MoMi/craft-engine.git
synced 2025-12-19 15:09:15 +00:00
@@ -29,18 +29,18 @@ subprojects {
|
||||
|
||||
filesMatching(arrayListOf("commands.yml", "config.yml")) {
|
||||
expand(
|
||||
Pair("project_version", rootProject.properties["project_version"]),
|
||||
Pair("config_version", rootProject.properties["config_version"]),
|
||||
Pair("lang_version", rootProject.properties["lang_version"])
|
||||
Pair("project_version", rootProject.properties["project_version"]!!),
|
||||
Pair("config_version", rootProject.properties["config_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")
|
||||
}.standardOutput.asText.map { it.trim() }.getOrElse("Unknown")
|
||||
|
||||
fun builder() = project.providers.exec {
|
||||
fun builder(): String = project.providers.exec {
|
||||
commandLine("git", "config", "user.name")
|
||||
}.standardOutput.asText.map { it.trim() }.getOrElse("Unknown")
|
||||
@@ -80,7 +80,7 @@ tasks.withType<JavaCompile> {
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives(tasks.shadowJar)
|
||||
implementation(tasks.shadowJar)
|
||||
}
|
||||
|
||||
tasks {
|
||||
|
||||
@@ -17,6 +17,7 @@ repositories {
|
||||
maven("https://repo.hiusers.com/releases") // zaphkiel
|
||||
maven("https://jitpack.io") // sxitem slimefun
|
||||
maven("https://repo.codemc.io/repository/maven-public/") // quickshop
|
||||
maven("https://repo.nexomc.com/releases/") // nexo
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -41,6 +42,8 @@ dependencies {
|
||||
// MMOItems
|
||||
compileOnly("net.Indyuce:MMOItems-API:6.10-SNAPSHOT")
|
||||
compileOnly("io.lumine:MythicLib-dist:1.6.2-SNAPSHOT")
|
||||
// Nexo
|
||||
compileOnly("com.nexomc:nexo:1.13.0")
|
||||
// LuckPerms
|
||||
compileOnly("net.luckperms:api:5.4")
|
||||
// viaversion
|
||||
@@ -63,6 +66,8 @@ dependencies {
|
||||
compileOnly("com.github.Zrips:Jobs:v5.2.2.3")
|
||||
// CustomFishing
|
||||
compileOnly("net.momirealms:custom-fishing:2.3.3")
|
||||
// CustomNameplates
|
||||
compileOnly("net.momirealms:custom-nameplates:3.0.33")
|
||||
// eco
|
||||
compileOnly("com.willfp:eco:6.70.1")
|
||||
compileOnly("com.willfp:EcoJobs:3.56.1")
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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.compatibility.item.*;
|
||||
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.skript.SkriptHook;
|
||||
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.worldedit.WorldEditBlockRegister;
|
||||
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.LevelerProvider;
|
||||
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.context.Context;
|
||||
import net.momirealms.craftengine.core.plugin.context.condition.AlwaysFalseCondition;
|
||||
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.VersionHelper;
|
||||
import net.momirealms.craftengine.core.world.WorldManager;
|
||||
@@ -43,6 +48,8 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
|
||||
private final BukkitCraftEngine plugin;
|
||||
private final Map<String, ModelProvider> modelProviders;
|
||||
private final Map<String, LevelerProvider> levelerProviders;
|
||||
private final Map<String, TagResolverProvider> tagResolverProviders;
|
||||
private TagResolverProvider[] tagResolverProviderArray = null;
|
||||
private boolean hasPlaceholderAPI;
|
||||
|
||||
public BukkitCompatibilityManager(BukkitCraftEngine plugin) {
|
||||
@@ -52,6 +59,7 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
|
||||
"BetterModel", BetterModelModel::new
|
||||
));
|
||||
this.levelerProviders = new HashMap<>();
|
||||
this.tagResolverProviders = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -146,6 +154,12 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
|
||||
new QuickShopItemExpressionHandler(this.plugin).register();
|
||||
logHook("QuickShop-Hikari");
|
||||
}
|
||||
if (this.isPluginEnabled("CustomNameplates")) {
|
||||
registerTagResolverProvider(new CustomNameplateProviders.Background());
|
||||
registerTagResolverProvider(new CustomNameplateProviders.Nameplate());
|
||||
registerTagResolverProvider(new CustomNameplateProviders.Bubble());
|
||||
logHook("CustomNameplates");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -158,6 +172,13 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
|
||||
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) {
|
||||
this.plugin.logger().info("[Compatibility] " + plugin + " hooked");
|
||||
}
|
||||
@@ -291,6 +312,10 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
|
||||
itemManager.registerExternalItemSource(new SlimefunSource());
|
||||
logHook("Slimefun");
|
||||
}
|
||||
if (this.isPluginEnabled("Nexo")) {
|
||||
itemManager.registerExternalItemSource(new NexoItemSource());
|
||||
logHook("Nexo");
|
||||
}
|
||||
}
|
||||
|
||||
private Plugin getPlugin(String name) {
|
||||
@@ -314,7 +339,9 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
|
||||
|
||||
@Override
|
||||
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
|
||||
@@ -326,4 +353,15 @@ public class BukkitCompatibilityManager implements CompatibilityManager {
|
||||
public int getPlayerProtocolVersion(UUID 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package net.momirealms.craftengine.bukkit.compatibility.quickshop;
|
||||
|
||||
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.Registry;
|
||||
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.plugin.BukkitCraftEngine;
|
||||
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.inventory.ItemStack;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -29,5 +29,5 @@ tasks.withType<JavaCompile> {
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives(tasks.shadowJar)
|
||||
implementation(tasks.shadowJar)
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import org.bukkit.event.Listener;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public class DismountListener1_20 implements Listener {
|
||||
public final class DismountListener1_20 implements Listener {
|
||||
private final BiConsumer<Player, Entity> consumer;
|
||||
|
||||
public DismountListener1_20(BiConsumer<Player, Entity> consumer) {
|
||||
|
||||
@@ -8,7 +8,8 @@ import org.bukkit.entity.Player;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public class LegacyAttributeUtils {
|
||||
public final class LegacyAttributeUtils {
|
||||
private LegacyAttributeUtils() {}
|
||||
|
||||
public static void setMaxHealth(ArmorStand entity) {
|
||||
Objects.requireNonNull(entity.getAttribute(Attribute.GENERIC_MAX_HEALTH)).setBaseValue(0.01);
|
||||
|
||||
@@ -4,7 +4,8 @@ import com.mojang.authlib.GameProfile;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class LegacyAuthLibUtils {
|
||||
public final class LegacyAuthLibUtils {
|
||||
private LegacyAuthLibUtils() {}
|
||||
|
||||
public static String getName(GameProfile profile) {
|
||||
return profile.getName();
|
||||
|
||||
@@ -8,7 +8,8 @@ import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||
|
||||
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) {
|
||||
return world.spawnEntity(loc, type, CreatureSpawnEvent.SpawnReason.CUSTOM, function::accept);
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
package net.momirealms.craftengine.bukkit.util;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.inventory.InventoryAction;
|
||||
import org.bukkit.event.inventory.InventoryEvent;
|
||||
import org.bukkit.event.inventory.PrepareAnvilEvent;
|
||||
import org.bukkit.inventory.AnvilInventory;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.bukkit.inventory.Merchant;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class LegacyInventoryUtils {
|
||||
public final class LegacyInventoryUtils {
|
||||
private LegacyInventoryUtils() {}
|
||||
|
||||
public static Inventory getTopInventory(Player player) {
|
||||
return player.getOpenInventory().getTopInventory();
|
||||
@@ -67,7 +71,20 @@ public class LegacyInventoryUtils {
|
||||
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) {
|
||||
return (Player) event.getView().getPlayer();
|
||||
}
|
||||
|
||||
public static boolean isHotBarSwapAndReadd(InventoryAction action) {
|
||||
return action == InventoryAction.HOTBAR_MOVE_AND_READD;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ bukkit {
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives(tasks.shadowJar)
|
||||
implementation(tasks.shadowJar)
|
||||
}
|
||||
|
||||
tasks {
|
||||
|
||||
@@ -77,6 +77,9 @@ paper {
|
||||
register("ViaVersion") { required = false }
|
||||
register("QuickShop-Hikari") { required = false }
|
||||
|
||||
// external tag
|
||||
register("CustomNameplates") { required = false }
|
||||
|
||||
// external models
|
||||
register("ModelEngine") { required = false }
|
||||
register("BetterModel") { required = false }
|
||||
@@ -90,6 +93,7 @@ paper {
|
||||
register("HeadDatabase") { required = false }
|
||||
register("SX-Item") { required = false }
|
||||
register("Slimefun") { required = false }
|
||||
register("Nexo") { required = false }
|
||||
|
||||
// leveler
|
||||
register("AuraSkills") { required = false }
|
||||
@@ -126,11 +130,12 @@ paper {
|
||||
register("PreciousStones") { required = false }
|
||||
register("hClaims") { required = false }
|
||||
register("Factions") { required = false }
|
||||
register("NoBuildPlus") { required = false }
|
||||
}
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives(tasks.shadowJar)
|
||||
implementation(tasks.shadowJar)
|
||||
}
|
||||
|
||||
tasks {
|
||||
|
||||
@@ -17,7 +17,7 @@ import java.lang.reflect.Method;
|
||||
import java.util.Objects;
|
||||
|
||||
@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$ComponentLogger = Objects.requireNonNull(
|
||||
ReflectionUtils.getClazz(
|
||||
@@ -29,7 +29,8 @@ public class PaperCraftEngineBootstrap implements PluginBootstrap {
|
||||
clazz$PluginProviderContext, clazz$ComponentLogger, new String[] { "getLogger" }
|
||||
)
|
||||
);
|
||||
protected BukkitCraftEngine plugin;
|
||||
|
||||
BukkitCraftEngine plugin;
|
||||
|
||||
@Override
|
||||
public void bootstrap(@NotNull BootstrapContext context) {
|
||||
|
||||
@@ -2,7 +2,7 @@ package net.momirealms.craftengine.bukkit.plugin;
|
||||
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public class PaperCraftEnginePlugin extends JavaPlugin {
|
||||
public final class PaperCraftEnginePlugin extends JavaPlugin {
|
||||
private final PaperCraftEngineBootstrap bootstrap;
|
||||
|
||||
public PaperCraftEnginePlugin(PaperCraftEngineBootstrap bootstrap) {
|
||||
|
||||
@@ -22,7 +22,7 @@ import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
public class BukkitAdvancementManager extends AbstractAdvancementManager {
|
||||
public final class BukkitAdvancementManager extends AbstractAdvancementManager {
|
||||
private final BukkitCraftEngine plugin;
|
||||
private final AdvancementParser advancementParser;
|
||||
private final Map<Key, JsonElement> advancements = new HashMap<>();
|
||||
|
||||
@@ -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.BukkitFurnitureManager;
|
||||
import net.momirealms.craftengine.bukkit.entity.seat.BukkitSeatManager;
|
||||
import net.momirealms.craftengine.bukkit.nms.CollisionEntity;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
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.world.World;
|
||||
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||
import net.momirealms.sparrow.nbt.CompoundTag;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -159,8 +161,7 @@ public final class CraftEngineFurniture {
|
||||
* @return is seat or not
|
||||
*/
|
||||
public static boolean isSeat(@NotNull Entity entity) {
|
||||
Integer baseEntityId = entity.getPersistentDataContainer().get(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER);
|
||||
return baseEntityId != null;
|
||||
return entity.getPersistentDataContainer().has(BukkitSeatManager.SEAT_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,9 +183,12 @@ public final class CraftEngineFurniture {
|
||||
*/
|
||||
@Nullable
|
||||
public static BukkitFurniture getLoadedFurnitureBySeat(@NotNull Entity seat) {
|
||||
Integer baseEntityId = seat.getPersistentDataContainer().get(BukkitFurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY, PersistentDataType.INTEGER);
|
||||
if (baseEntityId == null) return null;
|
||||
return BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(baseEntityId);
|
||||
if (isSeat(seat)) {
|
||||
CompoundTag seatExtraData = BukkitSeatManager.instance().getSeatExtraData(seat);
|
||||
int entityId = seatExtraData.getInt("entity_id");
|
||||
BukkitFurnitureManager.instance().loadedFurnitureByRealEntityId(entityId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,7 +21,7 @@ import java.nio.file.Path;
|
||||
* if you need the cache to recognize updates.
|
||||
* </p>
|
||||
*/
|
||||
public class AsyncResourcePackCacheEvent extends Event {
|
||||
public final class AsyncResourcePackCacheEvent extends Event {
|
||||
private static final HandlerList HANDLER_LIST = new HandlerList();
|
||||
private final PackCacheData cacheData;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
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 final Path generatedPackPath;
|
||||
private final Path zipFilePath;
|
||||
|
||||
@@ -5,7 +5,7 @@ import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
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 final BukkitCraftEngine plugin;
|
||||
private static boolean firstFlag = true;
|
||||
|
||||
@@ -12,7 +12,7 @@ import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.player.PlayerEvent;
|
||||
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 boolean cancelled;
|
||||
private final CustomBlock customBlock;
|
||||
|
||||
@@ -10,7 +10,7 @@ import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.player.PlayerEvent;
|
||||
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 boolean cancelled;
|
||||
private final CustomBlock customBlock;
|
||||
|
||||
@@ -15,7 +15,7 @@ import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
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 boolean cancelled;
|
||||
private final CustomBlock customBlock;
|
||||
|
||||
@@ -11,7 +11,7 @@ import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.player.PlayerEvent;
|
||||
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 final CustomBlock customBlock;
|
||||
private final ImmutableBlockState state;
|
||||
|
||||
@@ -8,7 +8,7 @@ import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.player.PlayerEvent;
|
||||
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 boolean cancelled;
|
||||
private final BukkitFurniture furniture;
|
||||
|
||||
@@ -12,7 +12,7 @@ import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.player.PlayerEvent;
|
||||
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 boolean cancelled;
|
||||
private final CustomFurniture furniture;
|
||||
|
||||
@@ -8,7 +8,7 @@ import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.player.PlayerEvent;
|
||||
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 boolean cancelled;
|
||||
private final BukkitFurniture furniture;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.momirealms.craftengine.bukkit.api.event;
|
||||
|
||||
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 org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -9,21 +10,29 @@ import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.player.PlayerEvent;
|
||||
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 boolean cancelled;
|
||||
private final BukkitFurniture furniture;
|
||||
private final InteractionHand hand;
|
||||
private final Location interactionPoint;
|
||||
private final HitBox hitBox;
|
||||
|
||||
public FurnitureInteractEvent(@NotNull Player player,
|
||||
@NotNull BukkitFurniture furniture,
|
||||
@NotNull InteractionHand hand,
|
||||
@NotNull Location interactionPoint) {
|
||||
@NotNull Location interactionPoint,
|
||||
@NotNull HitBox hitBox) {
|
||||
super(player);
|
||||
this.furniture = furniture;
|
||||
this.hand = hand;
|
||||
this.interactionPoint = interactionPoint;
|
||||
this.hitBox = hitBox;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public HitBox hitBox() {
|
||||
return hitBox;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -9,7 +9,7 @@ import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.player.PlayerEvent;
|
||||
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 final Location location;
|
||||
private final BukkitFurniture furniture;
|
||||
|
||||
@@ -6,6 +6,9 @@ import net.momirealms.craftengine.bukkit.block.behavior.UnsafeCompositeBlockBeha
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.BukkitCraftEngine;
|
||||
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.minecraft.*;
|
||||
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.plugin.CraftEngine;
|
||||
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.function.Function;
|
||||
import net.momirealms.craftengine.core.plugin.logger.Debugger;
|
||||
import net.momirealms.craftengine.core.registry.Holder;
|
||||
import net.momirealms.craftengine.core.sound.SoundData;
|
||||
import net.momirealms.craftengine.core.sound.SoundSet;
|
||||
import net.momirealms.craftengine.core.util.*;
|
||||
import net.momirealms.craftengine.core.world.chunk.PalettedContainer;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
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.Material;
|
||||
import org.bukkit.event.HandlerList;
|
||||
@@ -61,6 +66,8 @@ public final class BukkitBlockManager extends AbstractBlockManager {
|
||||
private Set<Object> missingHitSounds = Set.of();
|
||||
private Set<Object> missingStepSounds = Set.of();
|
||||
private Set<Key> missingInteractSoundBlocks = Set.of();
|
||||
// 缓存的VisualBlockStatePacket
|
||||
private VisualBlockStatePacket cachedVisualBlockStatePacket;
|
||||
|
||||
public BukkitBlockManager(BukkitCraftEngine plugin) {
|
||||
super(plugin, RegistryUtils.currentBlockRegistrySize(), Config.serverSideBlocks());
|
||||
@@ -120,6 +127,11 @@ public final class BukkitBlockManager extends AbstractBlockManager {
|
||||
public void delayedLoad() {
|
||||
this.plugin.networkManager().registerBlockStatePacketListeners(this.blockStateMappings); // 重置方块映射表
|
||||
super.delayedLoad();
|
||||
this.cachedVisualBlockStatePacket = VisualBlockStatePacket.create();
|
||||
for (BukkitServerPlayer player : BukkitNetworkManager.instance().onlineUsers()) {
|
||||
if (!player.clientModEnabled()) continue;
|
||||
PayloadHelper.sendData(player, this.cachedVisualBlockStatePacket);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -322,9 +334,6 @@ public final class BukkitBlockManager extends AbstractBlockManager {
|
||||
// 注册服务端侧的真实方块
|
||||
private void registerServerSideCustomBlocks(int count) {
|
||||
// 这个会影响全局调色盘
|
||||
if (MiscUtils.ceilLog2(this.vanillaBlockStateCount + count) == MiscUtils.ceilLog2(this.vanillaBlockStateCount)) {
|
||||
PalettedContainer.NEED_DOWNGRADE = false;
|
||||
}
|
||||
try {
|
||||
unfreezeRegistry();
|
||||
for (int i = 0; i < count; i++) {
|
||||
@@ -359,6 +368,10 @@ public final class BukkitBlockManager extends AbstractBlockManager {
|
||||
return this.cachedUpdateTagsPacket;
|
||||
}
|
||||
|
||||
public VisualBlockStatePacket cachedVisualBlockStatePacket() {
|
||||
return this.cachedVisualBlockStatePacket;
|
||||
}
|
||||
|
||||
private void markVanillaNoteBlocks() {
|
||||
try {
|
||||
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() {
|
||||
try {
|
||||
Map<Object, Material> magicMap = (Map<Object, Material>) CraftBukkitReflections.field$CraftMagicNumbers$BLOCK_MATERIAL.get(null);
|
||||
for (DelegatingBlock customBlock : this.customBlocks) {
|
||||
magicMap.put(customBlock, Material.STONE);
|
||||
Set<String> invalid = new HashSet<>();
|
||||
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) {
|
||||
this.plugin.logger().warn("Failed to deceive bukkit magic blocks", e);
|
||||
@@ -510,7 +541,7 @@ public final class BukkitBlockManager extends AbstractBlockManager {
|
||||
@Override
|
||||
protected CustomBlock createCustomBlock(@NotNull Holder.Reference<CustomBlock> holder,
|
||||
@NotNull BlockStateVariantProvider variantProvider,
|
||||
@NotNull Map<EventTrigger, List<Function<PlayerOptionalContext>>> events,
|
||||
@NotNull Map<EventTrigger, List<Function<Context>>> events,
|
||||
@Nullable LootTable<?> lootTable) {
|
||||
return new BukkitCustomBlock(holder, variantProvider, events, lootTable);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import net.momirealms.craftengine.core.block.AbstractCustomBlock;
|
||||
import net.momirealms.craftengine.core.block.BlockStateVariantProvider;
|
||||
import net.momirealms.craftengine.core.block.CustomBlock;
|
||||
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.function.Function;
|
||||
import net.momirealms.craftengine.core.registry.Holder;
|
||||
@@ -19,7 +19,7 @@ public final class BukkitCustomBlock extends AbstractCustomBlock {
|
||||
public BukkitCustomBlock(
|
||||
@NotNull Holder.Reference<CustomBlock> holder,
|
||||
@NotNull BlockStateVariantProvider variantProvider,
|
||||
@NotNull Map<EventTrigger, List<Function<PlayerOptionalContext>>> events,
|
||||
@NotNull Map<EventTrigger, List<Function<Context>>> events,
|
||||
@Nullable LootTable<?> lootTable
|
||||
) {
|
||||
super(holder, variantProvider, events, lootTable);
|
||||
|
||||
@@ -3,18 +3,31 @@ package net.momirealms.craftengine.bukkit.block;
|
||||
import net.momirealms.craftengine.bukkit.util.BlockStateUtils;
|
||||
import net.momirealms.craftengine.core.block.AbstractBlockStateWrapper;
|
||||
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.properties.Property;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class BukkitCustomBlockStateWrapper extends AbstractBlockStateWrapper {
|
||||
public class BukkitCustomBlockStateWrapper extends AbstractBlockStateWrapper implements CustomBlockStateWrapper {
|
||||
|
||||
public BukkitCustomBlockStateWrapper(Object blockState, int registryId) {
|
||||
super(blockState, registryId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockStateWrapper visualBlockState() {
|
||||
return getImmutableBlockState().map(ImmutableBlockState::vanillaBlockState).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCustom() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key ownerId() {
|
||||
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);
|
||||
}
|
||||
|
||||
@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
|
||||
public String getAsString() {
|
||||
return getImmutableBlockState().map(ImmutableBlockState::toString).orElseGet(() -> BlockStateUtils.fromBlockData(super.blockState).getAsString());
|
||||
|
||||
@@ -8,6 +8,8 @@ import net.momirealms.craftengine.core.block.BlockStateWrapper;
|
||||
import net.momirealms.craftengine.core.block.StatePropertyAccessor;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class BukkitVanillaBlockStateWrapper extends AbstractBlockStateWrapper {
|
||||
private final StatePropertyAccessor accessor;
|
||||
|
||||
@@ -16,6 +18,11 @@ public class BukkitVanillaBlockStateWrapper extends AbstractBlockStateWrapper {
|
||||
this.accessor = FastNMS.INSTANCE.createStatePropertyAccessor(blockState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCustom() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key ownerId() {
|
||||
return BlockStateUtils.getBlockOwnerIdFromState(super.blockState);
|
||||
@@ -31,6 +38,11 @@ public class BukkitVanillaBlockStateWrapper extends AbstractBlockStateWrapper {
|
||||
return this.accessor.hasProperty(propertyName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getPropertyNames() {
|
||||
return this.accessor.getPropertyNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAsString() {
|
||||
return BlockStateUtils.fromBlockData(super.blockState).getAsString();
|
||||
|
||||
@@ -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 CHIME_BLOCK = Key.from("craftengine:chime_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() {
|
||||
register(EMPTY, (block, args) -> EmptyBlockBehavior.INSTANCE);
|
||||
@@ -84,5 +85,6 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
|
||||
register(ATTACHED_STEM_BLOCK, AttachedStemBlockBehavior.FACTORY);
|
||||
register(CHIME_BLOCK, ChimeBlockBehavior.FACTORY);
|
||||
register(BUDDING_BLOCK, BuddingBlockBehavior.FACTORY);
|
||||
register(SEAT_BLOCK, SeatBlockBehavior.FACTORY);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,11 +75,11 @@ public class DirectionalAttachedBlockBehavior extends BukkitBlockBehavior {
|
||||
if (behavior == null) return false;
|
||||
Direction direction;
|
||||
if (isSixDirection) {
|
||||
direction = ((Direction) state.get(behavior.facingProperty)).opposite();
|
||||
direction = (Direction) state.get(behavior.facingProperty);
|
||||
} 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 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)
|
||||
|
||||
@@ -124,7 +124,7 @@ public class FallingBlockBehavior extends BukkitBlockBehavior {
|
||||
SoundData fallSound = null;
|
||||
SoundData destroySound = 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);
|
||||
}
|
||||
return new FallingBlockBehavior(block, hurtAmount, hurtMax, fallSound, destroySound);
|
||||
|
||||
@@ -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")));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ public class SimpleParticleBlockBehavior extends BukkitBlockBehavior implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends BlockEntity> BlockEntityType<T> blockEntityType() {
|
||||
public <T extends BlockEntity> BlockEntityType<T> blockEntityType(ImmutableBlockState state) {
|
||||
return EntityBlockBehavior.blockEntityTypeHelper(BukkitBlockEntityTypes.SIMPLE_PARTICLE);
|
||||
}
|
||||
|
||||
|
||||
@@ -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.SimpleStorageBlockEntity;
|
||||
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.util.BlockStateUtils;
|
||||
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.world.BlockPos;
|
||||
import net.momirealms.craftengine.core.world.CEWorld;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
@@ -71,15 +73,23 @@ public class SimpleStorageBlockBehavior extends BukkitBlockBehavior implements E
|
||||
public InteractionResult useWithoutItem(UseOnContext context, ImmutableBlockState state) {
|
||||
CEWorld world = context.getLevel().storageWorld();
|
||||
net.momirealms.craftengine.core.entity.player.Player player = context.getPlayer();
|
||||
BlockEntity blockEntity = world.getBlockEntityAtIfLoaded(context.getClickedPos());
|
||||
if (player != null && blockEntity instanceof SimpleStorageBlockEntity entity) {
|
||||
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()));
|
||||
});
|
||||
if (player == null) {
|
||||
return InteractionResult.SUCCESS_AND_CANCEL;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -106,7 +116,7 @@ public class SimpleStorageBlockBehavior extends BukkitBlockBehavior implements E
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends BlockEntity> BlockEntityType<T> blockEntityType() {
|
||||
public <T extends BlockEntity> BlockEntityType<T> blockEntityType(ImmutableBlockState state) {
|
||||
return EntityBlockBehavior.blockEntityTypeHelper(BukkitBlockEntityTypes.SIMPLE_STORAGE);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
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.ImmutableBlockState;
|
||||
import net.momirealms.craftengine.core.block.behavior.AbstractBlockBehavior;
|
||||
@@ -47,7 +46,7 @@ public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <T extends BlockBehavior> Optional<T> getAs(Class<T> tClass) {
|
||||
public <T> Optional<T> getAs(Class<T> tClass) {
|
||||
for (AbstractBlockBehavior behavior : this.behaviors) {
|
||||
if (tClass.isInstance(behavior)) {
|
||||
return Optional.of((T) behavior);
|
||||
@@ -74,13 +73,18 @@ public class UnsafeCompositeBlockBehavior extends BukkitBlockBehavior
|
||||
|
||||
@Override
|
||||
public InteractionResult useOnBlock(UseOnContext context, ImmutableBlockState state) {
|
||||
boolean hasPass = false;
|
||||
for (AbstractBlockBehavior behavior : this.behaviors) {
|
||||
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 super.useOnBlock(context, state);
|
||||
return hasPass ? InteractionResult.PASS : super.useOnBlock(context, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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) {
|
||||
int currentHeight = 1;
|
||||
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 nextState = FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, nextPos);
|
||||
Optional<ImmutableBlockState> optionalBelowCustomState = BlockStateUtils.getOptionalCustomBlockState(nextState);
|
||||
|
||||
@@ -47,7 +47,7 @@ public class WallTorchParticleBlockBehavior extends BukkitBlockBehavior implemen
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends BlockEntity> BlockEntityType<T> blockEntityType() {
|
||||
public <T extends BlockEntity> BlockEntityType<T> blockEntityType(ImmutableBlockState state) {
|
||||
return EntityBlockBehavior.blockEntityTypeHelper(BukkitBlockEntityTypes.WALL_TORCH_PARTICLE);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,10 @@ import net.momirealms.craftengine.core.block.entity.BlockEntityTypeKeys;
|
||||
import net.momirealms.craftengine.core.block.entity.BlockEntityTypes;
|
||||
|
||||
public class BukkitBlockEntityTypes extends BlockEntityTypes {
|
||||
public static final BlockEntityType<SimpleStorageBlockEntity> SIMPLE_STORAGE = register(BlockEntityTypeKeys.SIMPLE_STORAGE, SimpleStorageBlockEntity::new);
|
||||
public static final BlockEntityType<SimpleParticleBlockEntity> SIMPLE_PARTICLE = register(BlockEntityTypeKeys.SIMPLE_PARTICLE, SimpleParticleBlockEntity::new);
|
||||
public static final BlockEntityType<WallTorchParticleBlockEntity> WALL_TORCH_PARTICLE = register(BlockEntityTypeKeys.WALL_TORCH_PARTICLE, WallTorchParticleBlockEntity::new);
|
||||
public static final BlockEntityType<SimpleStorageBlockEntity> SIMPLE_STORAGE = register(BlockEntityTypeKeys.SIMPLE_STORAGE);
|
||||
public static final BlockEntityType<SimpleParticleBlockEntity> SIMPLE_PARTICLE = register(BlockEntityTypeKeys.SIMPLE_PARTICLE);
|
||||
public static final BlockEntityType<WallTorchParticleBlockEntity> WALL_TORCH_PARTICLE = register(BlockEntityTypeKeys.WALL_TORCH_PARTICLE);
|
||||
public static final BlockEntityType<SeatBlockEntity> SEAT = register(BlockEntityTypeKeys.SEAT);
|
||||
|
||||
private BukkitBlockEntityTypes() {}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -85,7 +85,7 @@ public class SimpleStorageBlockEntity extends BlockEntity {
|
||||
}
|
||||
if (VersionHelper.isOrAbove1_20_5()) {
|
||||
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));
|
||||
} else {
|
||||
Object nmsTag = MRegistryOps.SPARROW_NBT.convertTo(MRegistryOps.NBT, itemTag);
|
||||
|
||||
@@ -9,6 +9,8 @@ public class BukkitBlockEntityElementConfigs extends BlockEntityElementConfigs {
|
||||
register(TEXT_DISPLAY, TextDisplayBlockEntityElementConfig.FACTORY);
|
||||
}
|
||||
|
||||
private BukkitBlockEntityElementConfigs() {}
|
||||
|
||||
public static void init() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo
|
||||
private final Quaternionf rotation;
|
||||
private final ItemDisplayContext displayContext;
|
||||
private final Billboard billboard;
|
||||
private final float shadowRadius;
|
||||
private final float shadowStrength;
|
||||
|
||||
public ItemDisplayBlockEntityElementConfig(Function<Player, Item<?>> item,
|
||||
Vector3f scale,
|
||||
@@ -43,7 +45,9 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo
|
||||
float yRot,
|
||||
Quaternionf rotation,
|
||||
ItemDisplayContext displayContext,
|
||||
Billboard billboard) {
|
||||
Billboard billboard,
|
||||
float shadowRadius,
|
||||
float shadowStrength) {
|
||||
this.item = item;
|
||||
this.scale = scale;
|
||||
this.position = position;
|
||||
@@ -53,6 +57,8 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo
|
||||
this.rotation = rotation;
|
||||
this.displayContext = displayContext;
|
||||
this.billboard = billboard;
|
||||
this.shadowRadius = shadowRadius;
|
||||
this.shadowStrength = shadowStrength;
|
||||
this.lazyMetadataPacket = player -> {
|
||||
List<Object> dataValues = new ArrayList<>();
|
||||
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.Translation.addEntityDataIfNotDefaultValue(this.translation, dataValues);
|
||||
ItemDisplayEntityData.DisplayType.addEntityDataIfNotDefaultValue(this.displayContext.id(), dataValues);
|
||||
ItemDisplayEntityData.ShadowRadius.addEntityDataIfNotDefaultValue(this.shadowRadius, dataValues);
|
||||
ItemDisplayEntityData.ShadowStrength.addEntityDataIfNotDefaultValue(this.shadowStrength, dataValues);
|
||||
return dataValues;
|
||||
};
|
||||
}
|
||||
@@ -106,6 +114,14 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo
|
||||
return rotation;
|
||||
}
|
||||
|
||||
public float shadowRadius() {
|
||||
return shadowRadius;
|
||||
}
|
||||
|
||||
public float shadowStrength() {
|
||||
return shadowStrength;
|
||||
}
|
||||
|
||||
public List<Object> metadataValues(Player player) {
|
||||
return this.lazyMetadataPacket.apply(player);
|
||||
}
|
||||
@@ -125,7 +141,9 @@ public class ItemDisplayBlockEntityElementConfig implements BlockEntityElementCo
|
||||
ResourceConfigUtils.getAsFloat(arguments.getOrDefault("yaw", 0f), "yaw"),
|
||||
ResourceConfigUtils.getAsQuaternionf(arguments.getOrDefault("rotation", 0f), "rotation"),
|
||||
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")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import java.lang.ref.WeakReference;
|
||||
import java.util.UUID;
|
||||
|
||||
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) {
|
||||
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) {
|
||||
FastNMS.INSTANCE.method$SynchedEntityData$set(entityData(), data.entityDataAccessor(), value, force);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
this.platformEntity().remove();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -7,13 +7,13 @@ import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import java.util.Optional;
|
||||
|
||||
public class AbstractMinecartData<T> extends VehicleEntityData<T> {
|
||||
// 1.20~1.21.2
|
||||
public static final AbstractMinecartData<Integer> DisplayBlock = of(AbstractMinecartData.class, EntityDataValue.Serializers$INT, BlockStateUtils.blockStateToId(MBlocks.AIR$defaultState), !VersionHelper.isOrAbove1_21_3());
|
||||
// 1.21.3+
|
||||
public static final AbstractMinecartData<Optional<Object>> CustomDisplayBlock = of(AbstractMinecartData.class, EntityDataValue.Serializers$OPTIONAL_BLOCK_STATE, Optional.empty(), VersionHelper.isOrAbove1_21_3());
|
||||
// 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_5());
|
||||
// 1.21.5+
|
||||
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);
|
||||
// 1.20~1.21.2
|
||||
public static final AbstractMinecartData<Boolean> CustomDisplay = of(AbstractMinecartData.class, EntityDataValue.Serializers$BOOLEAN, false, !VersionHelper.isOrAbove1_21_3());
|
||||
// 1.20~1.21.4
|
||||
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) {
|
||||
if (!condition) return null;
|
||||
|
||||
@@ -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.FurnitureSettings;
|
||||
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.function.Function;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
@@ -20,7 +20,7 @@ public class BukkitCustomFurniture extends AbstractCustomFurniture {
|
||||
protected BukkitCustomFurniture(@NotNull Key id,
|
||||
@NotNull FurnitureSettings settings,
|
||||
@NotNull Map<AnchorType, Placement> placements,
|
||||
@NotNull Map<EventTrigger, List<Function<PlayerOptionalContext>>> events,
|
||||
@NotNull Map<EventTrigger, List<Function<Context>>> events,
|
||||
@Nullable LootTable<?> lootTable) {
|
||||
super(id, settings, placements, events, lootTable);
|
||||
}
|
||||
@@ -33,7 +33,7 @@ public class BukkitCustomFurniture extends AbstractCustomFurniture {
|
||||
private Key id;
|
||||
private Map<AnchorType, Placement> placements;
|
||||
private FurnitureSettings settings;
|
||||
private Map<EventTrigger, List<Function<PlayerOptionalContext>>> events;
|
||||
private Map<EventTrigger, List<Function<Context>>> events;
|
||||
private LootTable<?> lootTable;
|
||||
|
||||
@Override
|
||||
@@ -66,7 +66,7 @@ public class BukkitCustomFurniture extends AbstractCustomFurniture {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder events(Map<EventTrigger, List<Function<PlayerOptionalContext>>> events) {
|
||||
public Builder events(Map<EventTrigger, List<Function<Context>>> events) {
|
||||
this.events = events;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -5,20 +5,16 @@ import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import net.momirealms.craftengine.bukkit.entity.BukkitEntity;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.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.core.entity.furniture.*;
|
||||
import net.momirealms.craftengine.core.entity.seat.Seat;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.util.ArrayUtils;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.QuaternionUtils;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||
import net.momirealms.craftengine.core.world.collision.AABB;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.attribute.Attribute;
|
||||
import org.bukkit.entity.*;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -30,7 +26,6 @@ import java.lang.ref.WeakReference;
|
||||
import java.util.*;
|
||||
|
||||
public class BukkitFurniture implements Furniture {
|
||||
private final Key id;
|
||||
private final CustomFurniture furniture;
|
||||
private final CustomFurniture.Placement placement;
|
||||
private FurnitureExtraData extraData;
|
||||
@@ -44,13 +39,10 @@ public class BukkitFurniture implements Furniture {
|
||||
// cache
|
||||
private final List<Integer> fakeEntityIds;
|
||||
private final List<Integer> entityIds;
|
||||
private final Map<Integer, HitBox> hitBoxes = new Int2ObjectArrayMap<>();
|
||||
private final Map<Integer, AABB> aabb = new Int2ObjectArrayMap<>();
|
||||
private final Map<Integer, BukkitHitBox> hitBoxes = new Int2ObjectArrayMap<>();
|
||||
private final Map<Integer, HitBoxPart> hitBoxParts = new Int2ObjectArrayMap<>();
|
||||
private final boolean minimized;
|
||||
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
|
||||
private Object cachedSpawnPacket;
|
||||
private Object cachedMinimizedSpawnPacket;
|
||||
@@ -58,37 +50,40 @@ public class BukkitFurniture implements Furniture {
|
||||
public BukkitFurniture(Entity baseEntity,
|
||||
CustomFurniture furniture,
|
||||
FurnitureExtraData extraData) {
|
||||
this.id = furniture.id();
|
||||
this.extraData = extraData;
|
||||
this.baseEntityId = baseEntity.getEntityId();
|
||||
|
||||
this.location = baseEntity.getLocation();
|
||||
this.baseEntity = new WeakReference<>(baseEntity);
|
||||
this.furniture = furniture;
|
||||
this.minimized = furniture.settings().minimized();
|
||||
this.placement = furniture.getValidPlacement(extraData.anchorType().orElseGet(furniture::getAnyAnchorType));
|
||||
|
||||
List<Integer> fakeEntityIds = new IntArrayList();
|
||||
List<Integer> mainEntityIds = new IntArrayList();
|
||||
mainEntityIds.add(this.baseEntityId);
|
||||
|
||||
this.placement = furniture.getValidPlacement(extraData.anchorType().orElseGet(furniture::getAnyAnchorType));
|
||||
// bind external furniture
|
||||
// 绑定外部模型
|
||||
Optional<ExternalModel> optionalExternal = placement.externalModel();
|
||||
if (optionalExternal.isPresent()) {
|
||||
try {
|
||||
optionalExternal.get().bindModel(new BukkitEntity(baseEntity));
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn("Failed to load external model for furniture " + id, e);
|
||||
CraftEngine.instance().logger().warn("Failed to load external model for furniture " + id(), e);
|
||||
}
|
||||
this.hasExternalModel = true;
|
||||
} else {
|
||||
this.hasExternalModel = false;
|
||||
}
|
||||
|
||||
|
||||
Quaternionf conjugated = QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - this.location.getYaw()), 0).conjugate();
|
||||
List<Object> packets = new ArrayList<>();
|
||||
List<Object> minimizedPackets = new ArrayList<>();
|
||||
List<Collider> colliders = new ArrayList<>();
|
||||
List<Collider> colliders = new ArrayList<>(4);
|
||||
WorldPosition position = position();
|
||||
|
||||
|
||||
// 初始化家具的元素
|
||||
for (FurnitureElement element : placement.elements()) {
|
||||
int entityId = CoreReflections.instance$Entity$ENTITY_COUNTER.incrementAndGet();
|
||||
fakeEntityIds.add(entityId);
|
||||
@@ -97,28 +92,41 @@ public class BukkitFurniture implements Furniture {
|
||||
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) {
|
||||
fakeEntityIds.add(entityId);
|
||||
mainEntityIds.add(entityId);
|
||||
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 {
|
||||
this.cachedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(packets);
|
||||
if (this.minimized) {
|
||||
this.cachedMinimizedSpawnPacket = FastNMS.INSTANCE.constructor$ClientboundBundlePacket(minimizedPackets);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
CraftEngine.instance().logger().warn("Failed to init spawn packets for furniture " + id, e);
|
||||
CraftEngine.instance().logger().warn("Failed to init spawn packets for furniture " + id(), e);
|
||||
}
|
||||
|
||||
|
||||
this.fakeEntityIds = fakeEntityIds;
|
||||
this.entityIds = mainEntityIds;
|
||||
this.colliderEntities = colliders.toArray(new Collider[0]);
|
||||
@@ -186,57 +194,25 @@ public class BukkitFurniture implements Furniture {
|
||||
return;
|
||||
}
|
||||
this.baseEntity().remove();
|
||||
this.destroyColliders();
|
||||
this.destroySeats();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyColliders() {
|
||||
for (Collider entity : this.colliderEntities) {
|
||||
if (entity != null)
|
||||
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
|
||||
public void destroySeats() {
|
||||
for (WeakReference<Entity> entity : this.seats) {
|
||||
Entity e = entity.get();
|
||||
if (e != null) {
|
||||
e.remove();
|
||||
for (HitBox hitBox : this.hitBoxes.values()) {
|
||||
for (Seat<HitBox> seat : hitBox.seats()) {
|
||||
seat.destroy();
|
||||
}
|
||||
}
|
||||
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
|
||||
@@ -263,14 +239,14 @@ public class BukkitFurniture implements Furniture {
|
||||
return this.colliderEntities;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public HitBox hitBoxByEntityId(int id) {
|
||||
@Override
|
||||
public @Nullable HitBox hitBoxByEntityId(int id) {
|
||||
return this.hitBoxes.get(id);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public AABB aabbByEntityId(int id) {
|
||||
return this.aabb.get(id);
|
||||
@Override
|
||||
public @Nullable HitBoxPart hitBoxPartByEntityId(int id) {
|
||||
return this.hitBoxParts.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -280,7 +256,7 @@ public class BukkitFurniture implements Furniture {
|
||||
|
||||
@Override
|
||||
public @NotNull Key id() {
|
||||
return this.id;
|
||||
return this.furniture.id();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -293,11 +269,6 @@ public class BukkitFurniture implements Furniture {
|
||||
return hasExternalModel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnSeatEntityForPlayer(net.momirealms.craftengine.core.entity.player.Player player, Seat seat) {
|
||||
spawnSeatEntityForPlayer((Player) player.platformPlayer(), seat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FurnitureExtraData extraData() {
|
||||
return this.extraData;
|
||||
@@ -317,49 +288,4 @@ public class BukkitFurniture implements Furniture {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -38,14 +38,18 @@ public class BukkitFurnitureElement extends AbstractFurnitureElement {
|
||||
Vector3f translation,
|
||||
Vector3f position,
|
||||
Quaternionf rotation,
|
||||
float shadowRadius,
|
||||
float shadowStrength,
|
||||
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<>();
|
||||
ItemDisplayEntityData.Scale.addEntityDataIfNotDefaultValue(scale(), this.commonValues);
|
||||
ItemDisplayEntityData.RotationLeft.addEntityDataIfNotDefaultValue(rotation(), this.commonValues);
|
||||
ItemDisplayEntityData.BillboardConstraints.addEntityDataIfNotDefaultValue(billboard().id(), this.commonValues);
|
||||
ItemDisplayEntityData.Translation.addEntityDataIfNotDefaultValue(translation(), this.commonValues);
|
||||
ItemDisplayEntityData.DisplayType.addEntityDataIfNotDefaultValue(transform().id(), this.commonValues);
|
||||
ItemDisplayEntityData.ShadowRadius.addEntityDataIfNotDefaultValue(shadowRadius, this.commonValues);
|
||||
ItemDisplayEntityData.ShadowStrength.addEntityDataIfNotDefaultValue(shadowStrength, this.commonValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -53,7 +57,7 @@ public class BukkitFurnitureElement extends AbstractFurnitureElement {
|
||||
WorldPosition position = furniture.position();
|
||||
Vector3f offset = conjugated.transform(new Vector3f(position()));
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||
entityId, UUID.randomUUID(), position.x() + offset.x, position.y() + offset.y, position.z() - offset.z, 0, position.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
|
||||
));
|
||||
if (applyDyedColor()) {
|
||||
@@ -102,6 +106,8 @@ public class BukkitFurnitureElement extends AbstractFurnitureElement {
|
||||
private Vector3f translation;
|
||||
private Vector3f position;
|
||||
private Quaternionf rotation;
|
||||
private float shadowRadius;
|
||||
private float shadowStrength;
|
||||
|
||||
@Override
|
||||
public Builder applyDyedColor(boolean applyDyedColor) {
|
||||
@@ -151,9 +157,21 @@ public class BukkitFurnitureElement extends AbstractFurnitureElement {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder shadowStrength(float shadowStrength) {
|
||||
this.shadowStrength = shadowStrength;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder shadowRadius(float shadowRadius) {
|
||||
this.shadowRadius = shadowRadius;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FurnitureElement build() {
|
||||
return new BukkitFurnitureElement(item, billboard, transform, scale, translation, position, rotation, applyDyedColor);
|
||||
return new BukkitFurnitureElement(item, billboard, transform, scale, translation, position, rotation, shadowRadius, shadowStrength, applyDyedColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package net.momirealms.craftengine.bukkit.entity.furniture;
|
||||
|
||||
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.FastNMS;
|
||||
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.sound.SoundData;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.entity.*;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
@@ -37,8 +33,6 @@ import java.util.function.BiConsumer;
|
||||
public class BukkitFurnitureManager extends AbstractFurnitureManager {
|
||||
public static final NamespacedKey FURNITURE_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_KEY);
|
||||
public static final NamespacedKey FURNITURE_EXTRA_DATA_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_EXTRA_DATA_KEY);
|
||||
public static final NamespacedKey FURNITURE_SEAT_BASE_ENTITY_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_SEAT_BASE_ENTITY_KEY);
|
||||
public static final NamespacedKey FURNITURE_SEAT_VECTOR_3F_KEY = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_SEAT_VECTOR_3F_KEY);
|
||||
public static final NamespacedKey FURNITURE_COLLISION = KeyUtils.toNamespacedKey(FurnitureManager.FURNITURE_COLLISION);
|
||||
public static Class<?> COLLISION_ENTITY_CLASS = Interaction.class;
|
||||
public static Object NMS_COLLISION_ENTITY_TYPE = 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> furnitureByEntityId = new ConcurrentHashMap<>(512, 0.5f);
|
||||
// Event listeners
|
||||
private final Listener dismountListener;
|
||||
private final FurnitureEventListener furnitureEventListener;
|
||||
|
||||
public static BukkitFurnitureManager instance() {
|
||||
@@ -60,7 +53,6 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
|
||||
instance = this;
|
||||
this.plugin = plugin;
|
||||
this.furnitureEventListener = new FurnitureEventListener(this);
|
||||
this.dismountListener = VersionHelper.isOrAbove1_20_3() ? new DismountListener1_20_3(this) : new DismountListener1_20(this::handleDismount);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,7 +87,6 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
|
||||
COLLISION_ENTITY_CLASS = Config.colliderType() == ColliderType.INTERACTION ? Interaction.class : Boat.class;
|
||||
NMS_COLLISION_ENTITY_TYPE = Config.colliderType() == ColliderType.INTERACTION ? MEntityTypes.INTERACTION : MEntityTypes.OAK_BOAT;
|
||||
COLLISION_ENTITY_TYPE = Config.colliderType();
|
||||
Bukkit.getPluginManager().registerEvents(this.dismountListener, this.plugin.javaPlugin());
|
||||
Bukkit.getPluginManager().registerEvents(this.furnitureEventListener, this.plugin.javaPlugin());
|
||||
if (VersionHelper.isFolia()) {
|
||||
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
|
||||
public void disable() {
|
||||
HandlerList.unregisterAll(this.dismountListener);
|
||||
HandlerList.unregisterAll(this.furnitureEventListener);
|
||||
unload();
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
Entity vehicle = player.getVehicle();
|
||||
if (vehicle != null) {
|
||||
tryLeavingSeat(player, vehicle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -327,84 +311,7 @@ public class BukkitFurnitureManager extends AbstractFurnitureManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HitBox defaultHitBox() {
|
||||
return InteractionHitBox.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;
|
||||
protected HitBoxConfig defaultHitBox() {
|
||||
return InteractionHitBoxConfig.DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,24 @@
|
||||
package net.momirealms.craftengine.bukkit.entity.furniture;
|
||||
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.EntityDismountEvent;
|
||||
|
||||
public class DismountListener1_20_3 implements Listener {
|
||||
private final BukkitFurnitureManager manager;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public DismountListener1_20_3(final BukkitFurnitureManager manager) {
|
||||
this.manager = manager;
|
||||
public class DismountListener1_20_3 implements Listener {
|
||||
private final BiConsumer<Player, Entity> consumer;
|
||||
|
||||
public DismountListener1_20_3(final BiConsumer<Player, Entity> consumer) {
|
||||
this.consumer = consumer;
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onDismount(EntityDismountEvent event) {
|
||||
if (event.getEntity() instanceof Player player) {
|
||||
this.manager.handleDismount(player, event.getDismounted());
|
||||
this.consumer.accept(player, event.getDismounted());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,21 +2,15 @@ package net.momirealms.craftengine.bukkit.entity.furniture;
|
||||
|
||||
import com.destroystokyo.paper.event.entity.EntityAddToWorldEvent;
|
||||
import com.destroystokyo.paper.event.entity.EntityRemoveFromWorldEvent;
|
||||
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.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.EntitiesLoadEvent;
|
||||
import org.bukkit.event.world.WorldLoadEvent;
|
||||
import org.bukkit.event.world.WorldUnloadEvent;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -100,35 +94,4 @@ public class FurnitureEventListener implements Listener {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ public class BukkitHitBoxTypes extends HitBoxTypes {
|
||||
public static void init() {}
|
||||
|
||||
static {
|
||||
register(INTERACTION, InteractionHitBox.FACTORY);
|
||||
register(SHULKER, ShulkerHitBox.FACTORY);
|
||||
register(HAPPY_GHAST, HappyGhastHitBox.FACTORY);
|
||||
register(CUSTOM, CustomHitBox.FACTORY);
|
||||
register(INTERACTION, InteractionHitBoxConfig.FACTORY);
|
||||
register(SHULKER, ShulkerHitBoxConfig.FACTORY);
|
||||
register(HAPPY_GHAST, HappyGhastHitBoxConfig.FACTORY);
|
||||
register(CUSTOM, CustomHitBoxConfig.FACTORY);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.NetworkReflections;
|
||||
import net.momirealms.craftengine.core.entity.furniture.*;
|
||||
import net.momirealms.craftengine.core.entity.seat.SeatConfig;
|
||||
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||
@@ -23,13 +24,13 @@ import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class CustomHitBox extends AbstractHitBox {
|
||||
public class CustomHitBoxConfig extends AbstractHitBoxConfig {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final float scale;
|
||||
private final EntityType entityType;
|
||||
private final List<Object> cachedValues = new ArrayList<>();
|
||||
|
||||
public 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);
|
||||
this.scale = scale;
|
||||
this.entityType = type;
|
||||
@@ -39,11 +40,11 @@ public class CustomHitBox extends AbstractHitBox {
|
||||
}
|
||||
|
||||
public EntityType entityType() {
|
||||
return entityType;
|
||||
return this.entityType;
|
||||
}
|
||||
|
||||
public float scale() {
|
||||
return scale;
|
||||
return this.scale;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -52,11 +53,11 @@ public class CustomHitBox extends AbstractHitBox {
|
||||
}
|
||||
|
||||
@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()));
|
||||
try {
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||
entityId[0], UUID.randomUUID(), position.x() + offset.x, position.y() + offset.y, position.z() - offset.z, 0, position.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
|
||||
), 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()};
|
||||
}
|
||||
|
||||
public static class Factory implements HitBoxFactory {
|
||||
public static class Factory implements HitBoxConfigFactory {
|
||||
|
||||
@Override
|
||||
public HitBox create(Map<String, Object> arguments) {
|
||||
public HitBoxConfig create(Map<String, Object> arguments) {
|
||||
Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position");
|
||||
float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", 1), "scale");
|
||||
String type = (String) arguments.getOrDefault("entity-type", "slime");
|
||||
@@ -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 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.NetworkReflections;
|
||||
import net.momirealms.craftengine.core.entity.furniture.*;
|
||||
import net.momirealms.craftengine.core.entity.seat.SeatConfig;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
import net.momirealms.craftengine.core.world.World;
|
||||
import net.momirealms.craftengine.core.world.WorldPosition;
|
||||
import net.momirealms.craftengine.core.world.collision.AABB;
|
||||
@@ -22,13 +24,13 @@ import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class HappyGhastHitBox extends AbstractHitBox {
|
||||
public class HappyGhastHitBoxConfig extends AbstractHitBoxConfig {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
private final double scale;
|
||||
private final boolean hardCollision;
|
||||
private final List<Object> cachedValues = new ArrayList<>();
|
||||
|
||||
public 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);
|
||||
this.scale = scale;
|
||||
this.hardCollision = hardCollision;
|
||||
@@ -43,21 +45,26 @@ public class HappyGhastHitBox extends AbstractHitBox {
|
||||
}
|
||||
|
||||
public double scale() {
|
||||
return scale;
|
||||
return this.scale;
|
||||
}
|
||||
|
||||
public boolean hardCollision() {
|
||||
return hardCollision;
|
||||
return this.hardCollision;
|
||||
}
|
||||
|
||||
@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()));
|
||||
try {
|
||||
double x = position.x();
|
||||
double y = position.y();
|
||||
double z = position.z();
|
||||
float yaw = position.xRot();
|
||||
float yaw = position.yRot();
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundAddEntityPacket(
|
||||
entityIds[0], UUID.randomUUID(), x + offset.x, y + offset.y, z - offset.z, 0, yaw,
|
||||
MEntityTypes.HAPPY_GHAST, 0, CoreReflections.instance$Vec3$Zero, 0
|
||||
@@ -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);
|
||||
Object level = world.serverWorld();
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -109,10 +116,10 @@ public class HappyGhastHitBox extends AbstractHitBox {
|
||||
return new int[] {entityIdSupplier.get()};
|
||||
}
|
||||
|
||||
public static class Factory implements HitBoxFactory {
|
||||
public static class Factory implements HitBoxConfigFactory {
|
||||
|
||||
@Override
|
||||
public HitBox create(Map<String, Object> arguments) {
|
||||
public HitBoxConfig create(Map<String, Object> arguments) {
|
||||
if (!VersionHelper.isOrAbove1_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 canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", true), "can-be-hit-by-projectile");
|
||||
boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building");
|
||||
return new HappyGhastHitBox(
|
||||
HitBoxFactory.getSeats(arguments),
|
||||
return new HappyGhastHitBoxConfig(
|
||||
SeatConfig.fromObj(arguments.get("seats")),
|
||||
ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position"),
|
||||
scale, canUseOn, blocksBuilding, canBeHitByProjectile, hardCollision
|
||||
);
|
||||
@@ -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.MEntityTypes;
|
||||
import net.momirealms.craftengine.core.entity.furniture.*;
|
||||
import net.momirealms.craftengine.core.entity.seat.SeatConfig;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||
import net.momirealms.craftengine.core.world.Vec3d;
|
||||
@@ -22,15 +23,15 @@ import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
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 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 boolean responsive;
|
||||
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);
|
||||
this.size = size;
|
||||
this.responsive = responsive;
|
||||
@@ -40,11 +41,11 @@ public class InteractionHitBox extends AbstractHitBox {
|
||||
}
|
||||
|
||||
public boolean responsive() {
|
||||
return responsive;
|
||||
return this.responsive;
|
||||
}
|
||||
|
||||
public Vector3f size() {
|
||||
return size;
|
||||
return this.size;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -53,22 +54,26 @@ public class InteractionHitBox extends AbstractHitBox {
|
||||
}
|
||||
|
||||
@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()));
|
||||
double x = position.x();
|
||||
double y = position.y();
|
||||
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(
|
||||
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
|
||||
), true);
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityId[0], List.copyOf(this.cachedValues)), true);
|
||||
if (canUseItemOn()) {
|
||||
aabb.accept(entityId[0], AABB.fromInteraction(new Vec3d(x + offset.x, y + offset.y, z - offset.z), this.size.x, this.size.y));
|
||||
}
|
||||
aabb.accept(new HitBoxPart(entityId[0], AABB.fromInteraction(vec3d, this.size.x, this.size.y), vec3d));
|
||||
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);
|
||||
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()};
|
||||
}
|
||||
|
||||
public static class Factory implements HitBoxFactory {
|
||||
public static class Factory implements HitBoxConfigFactory {
|
||||
|
||||
@Override
|
||||
public HitBox create(Map<String, Object> arguments) {
|
||||
public HitBoxConfig create(Map<String, Object> arguments) {
|
||||
Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position");
|
||||
float width;
|
||||
float height;
|
||||
@@ -100,15 +105,15 @@ public class InteractionHitBox extends AbstractHitBox {
|
||||
width = Float.parseFloat(split[0]);
|
||||
height = Float.parseFloat(split[1]);
|
||||
} else {
|
||||
width = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("width", "1"), "width");
|
||||
height = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("height", "1"), "height");
|
||||
width = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("width", 1), "width");
|
||||
height = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("height", 1), "height");
|
||||
}
|
||||
boolean canUseOn = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-use-item-on", false), "can-use-item-on");
|
||||
boolean interactive = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("interactive", true), "interactive");
|
||||
boolean canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", false), "can-be-hit-by-projectile");
|
||||
boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building");
|
||||
return new InteractionHitBox(
|
||||
HitBoxFactory.getSeats(arguments),
|
||||
return new InteractionHitBoxConfig(
|
||||
SeatConfig.fromObj(arguments.get("seats")),
|
||||
position,
|
||||
new Vector3f(width, height, width),
|
||||
interactive, canUseOn, blocksBuilding, canBeHitByProjectile
|
||||
@@ -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.util.DirectionUtils;
|
||||
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.world.Vec3d;
|
||||
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.Supplier;
|
||||
|
||||
public class ShulkerHitBox extends AbstractHitBox {
|
||||
public class ShulkerHitBoxConfig extends AbstractHitBoxConfig {
|
||||
public static final Factory FACTORY = new Factory();
|
||||
// 1.20.6+
|
||||
private final float scale;
|
||||
@@ -35,7 +36,7 @@ public class ShulkerHitBox extends AbstractHitBox {
|
||||
private final DirectionalShulkerSpawner spawner;
|
||||
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);
|
||||
this.direction = direction;
|
||||
this.scale = scale;
|
||||
@@ -65,7 +66,8 @@ public class ShulkerHitBox extends AbstractHitBox {
|
||||
), true);
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true);
|
||||
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);
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[2], List.copyOf(cachedInteractionValues)), true);
|
||||
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);
|
||||
packets.accept(FastNMS.INSTANCE.constructor$ClientboundSetEntityDataPacket(entityIds[3], List.copyOf(cachedInteractionValues)), true);
|
||||
if (canUseOn) {
|
||||
aabb.accept(entityIds[2], AABB.fromInteraction(new Vec3d(x + offset.x, y + offset.y, z - offset.z), scale, scale));
|
||||
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 vec3d1 = new Vec3d(x + offset.x, y + offset.y, z - offset.z);
|
||||
Vec3d vec3d2 = new Vec3d(x + offset.x + shulkerDirection.stepX() * distance, y + offset.y, z - offset.z + shulkerDirection.stepZ() * distance);
|
||||
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);
|
||||
Object level = world.serverWorld();
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -200,13 +205,18 @@ public class ShulkerHitBox extends AbstractHitBox {
|
||||
}
|
||||
|
||||
@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()));
|
||||
try {
|
||||
double x = position.x();
|
||||
double y = position.y();
|
||||
double z = position.z();
|
||||
float yaw = position.xRot();
|
||||
float yaw = position.yRot();
|
||||
double originalY = y + offset.y;
|
||||
double integerPart = Math.floor(originalY);
|
||||
double fractionalPart = originalY - integerPart;
|
||||
@@ -251,7 +261,16 @@ public class ShulkerHitBox extends AbstractHitBox {
|
||||
@FunctionalInterface
|
||||
interface DirectionalShulkerSpawner {
|
||||
|
||||
void accept(int[] entityIds, World world, double x, double y, double z, float yaw, Vector3f offset, BiConsumer<Object, Boolean> packets, Consumer<Collider> collider, 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
|
||||
@@ -276,10 +295,10 @@ public class ShulkerHitBox extends AbstractHitBox {
|
||||
}
|
||||
}
|
||||
|
||||
public static class Factory implements HitBoxFactory {
|
||||
public static class Factory implements HitBoxConfigFactory {
|
||||
|
||||
@Override
|
||||
public HitBox create(Map<String, Object> arguments) {
|
||||
public HitBoxConfig create(Map<String, Object> arguments) {
|
||||
Vector3f position = ResourceConfigUtils.getAsVector3f(arguments.getOrDefault("position", "0"), "position");
|
||||
float scale = ResourceConfigUtils.getAsFloat(arguments.getOrDefault("scale", "1"), "scale");
|
||||
byte peek = (byte) ResourceConfigUtils.getAsInt(arguments.getOrDefault("peek", 0), "peek");
|
||||
@@ -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 canBeHitByProjectile = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("can-be-hit-by-projectile", true), "can-be-hit-by-projectile");
|
||||
boolean blocksBuilding = ResourceConfigUtils.getAsBoolean(arguments.getOrDefault("blocks-building", true), "blocks-building");
|
||||
return new ShulkerHitBox(
|
||||
HitBoxFactory.getSeats(arguments),
|
||||
return new ShulkerHitBoxConfig(
|
||||
SeatConfig.fromObj(arguments.get("seats")),
|
||||
position, directionEnum,
|
||||
scale, peek, interactionEntity, interactive, canUseItemOn, blocksBuilding, canBeHitByProjectile
|
||||
);
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import net.momirealms.craftengine.core.item.*;
|
||||
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
|
||||
import net.momirealms.craftengine.core.item.modifier.ItemDataModifier;
|
||||
import net.momirealms.craftengine.core.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.function.Function;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
@@ -26,7 +26,7 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
|
||||
List<ItemBehavior> behaviors,
|
||||
List<ItemDataModifier<ItemStack>> modifiers, List<ItemDataModifier<ItemStack>> clientBoundModifiers,
|
||||
ItemSettings settings,
|
||||
Map<EventTrigger, List<Function<PlayerOptionalContext>>> events,
|
||||
Map<EventTrigger, List<Function<Context>>> events,
|
||||
ItemUpdateConfig updater) {
|
||||
super(isVanillaItem, id, materialKey, clientBoundMaterialKey, behaviors, modifiers, clientBoundModifiers, settings, events, updater);
|
||||
this.item = item;
|
||||
@@ -54,11 +54,15 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
|
||||
}
|
||||
|
||||
public Object clientItem() {
|
||||
return clientItem;
|
||||
return this.clientItem;
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -72,7 +76,7 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
|
||||
private final Object item;
|
||||
private Key clientBoundItemKey;
|
||||
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<ItemDataModifier<ItemStack>> modifiers = new ArrayList<>(4);
|
||||
private final List<ItemDataModifier<ItemStack>> clientBoundModifiers = new ArrayList<>(4);
|
||||
@@ -151,7 +155,7 @@ public class BukkitCustomItem extends AbstractCustomItem<ItemStack> {
|
||||
}
|
||||
|
||||
@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);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -22,8 +22,10 @@ import net.momirealms.craftengine.core.item.recipe.UniqueIdItem;
|
||||
import net.momirealms.craftengine.core.pack.AbstractPackManager;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
|
||||
import net.momirealms.craftengine.core.plugin.logger.Debugger;
|
||||
import net.momirealms.craftengine.core.util.*;
|
||||
import net.momirealms.craftengine.core.util.GsonHelper;
|
||||
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.event.HandlerList;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
@@ -103,17 +105,6 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
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
|
||||
public NetworkItemHandler<ItemStack> networkItemHandler() {
|
||||
return this.networkItemHandler;
|
||||
@@ -124,37 +115,25 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item<ItemStack> s2c(Item<ItemStack> item, Player player) {
|
||||
if (item.isEmpty()) return item;
|
||||
return this.networkItemHandler.s2c(item, player).orElse(item);
|
||||
public Optional<Item<ItemStack>> s2c(Item<ItemStack> item, Player player) {
|
||||
if (item.isEmpty()) return Optional.empty();
|
||||
return this.networkItemHandler.s2c(item, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item<ItemStack> c2s(Item<ItemStack> item) {
|
||||
if (item.isEmpty()) return item;
|
||||
return this.networkItemHandler.c2s(item).orElse(item);
|
||||
public Optional<Item<ItemStack>> c2s(Item<ItemStack> item) {
|
||||
if (item.isEmpty()) return Optional.empty();
|
||||
return this.networkItemHandler.c2s(item);
|
||||
}
|
||||
|
||||
public Optional<ItemStack> s2c(ItemStack itemStack, Player player) {
|
||||
try {
|
||||
Item<ItemStack> wrapped = wrap(itemStack);
|
||||
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> s2c(ItemStack item, Player player) {
|
||||
if (item.isEmpty()) return Optional.empty();
|
||||
return this.networkItemHandler.s2c(wrap(item), player).map(Item::getItem);
|
||||
}
|
||||
|
||||
public Optional<ItemStack> c2s(ItemStack itemStack) {
|
||||
try {
|
||||
Item<ItemStack> wrapped = wrap(itemStack);
|
||||
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();
|
||||
}
|
||||
public Optional<ItemStack> c2s(ItemStack item) {
|
||||
if (item.isEmpty()) return Optional.empty();
|
||||
return this.networkItemHandler.c2s(wrap(item)).map(Item::getItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -169,7 +148,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
jsonObject.addProperty("count", result.count());
|
||||
jsonObject.add("components", result.components());
|
||||
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) {
|
||||
return this.emptyItem;
|
||||
}
|
||||
@@ -429,7 +408,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
Object armorTrim = FastNMS.INSTANCE.constructor$ArmorTrim(optionalMaterial.get(), optionalPattern.get());
|
||||
Object previousTrim;
|
||||
if (VersionHelper.isOrAbove1_20_5()) {
|
||||
previousTrim = base.getExactComponent(ComponentKeys.TRIM);
|
||||
previousTrim = base.getExactComponent(DataComponentKeys.TRIM);
|
||||
} else {
|
||||
try {
|
||||
previousTrim = VersionHelper.isOrAbove1_20_2() ?
|
||||
@@ -445,7 +424,7 @@ public class BukkitItemManager extends AbstractItemManager<ItemStack> {
|
||||
}
|
||||
Item<ItemStack> newItem = base.copyWithCount(1);
|
||||
if (VersionHelper.isOrAbove1_20_5()) {
|
||||
newItem.setExactComponent(ComponentKeys.TRIM, armorTrim);
|
||||
newItem.setExactComponent(DataComponentKeys.TRIM, armorTrim);
|
||||
} else {
|
||||
try {
|
||||
CoreReflections.method$ArmorTrim$setTrim.invoke(null, FastNMS.INSTANCE.registryAccess(), newItem.getLiteralObject(), armorTrim);
|
||||
|
||||
@@ -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.MBuiltInRegistries;
|
||||
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.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.util.Key;
|
||||
import net.momirealms.craftengine.core.util.RandomUtils;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import net.momirealms.sparrow.nbt.Tag;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
@@ -23,6 +29,11 @@ public class ComponentItemWrapper implements ItemWrapper<ItemStack> {
|
||||
private final ItemStack item;
|
||||
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) {
|
||||
this.item = ItemStackUtils.ensureCraftItemStack(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) {
|
||||
setComponentInternal(type, MRegistryOps.NBT, value);
|
||||
setComponentInternal(type, MRegistryOps.NBT, value);
|
||||
}
|
||||
|
||||
public void setSparrowNBTComponent(Object type, Tag value) {
|
||||
@@ -196,4 +207,39 @@ public class ComponentItemWrapper implements ItemWrapper<ItemStack> {
|
||||
public void shrink(int 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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,16 @@ package net.momirealms.craftengine.bukkit.item;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||
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.core.entity.EquipmentSlot;
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
import net.momirealms.craftengine.core.item.ItemWrapper;
|
||||
import net.momirealms.craftengine.core.util.RandomUtils;
|
||||
import net.momirealms.sparrow.nbt.Tag;
|
||||
import org.bukkit.enchantments.Enchantment;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class LegacyItemWrapper implements ItemWrapper<ItemStack> {
|
||||
private final Object nmsStack;
|
||||
@@ -21,11 +27,12 @@ public class LegacyItemWrapper implements ItemWrapper<ItemStack> {
|
||||
Object finalNMSTag;
|
||||
if (value instanceof Tag tag) {
|
||||
finalNMSTag = MRegistryOps.SPARROW_NBT.convertTo(MRegistryOps.NBT, tag);
|
||||
} else if (CoreReflections.clazz$Tag.isInstance(value)) {
|
||||
finalNMSTag = value;
|
||||
} else {
|
||||
finalNMSTag = MRegistryOps.JAVA.convertTo(MRegistryOps.NBT, value);
|
||||
}
|
||||
|
||||
Object currentTag = FastNMS.INSTANCE.field$ItemStack$getOrCreateTag(this.nmsStack);
|
||||
if (path == null || path.length == 0) {
|
||||
if (CoreReflections.clazz$CompoundTag.isInstance(finalNMSTag)) {
|
||||
FastNMS.INSTANCE.method$ItemStack$setTag(this.nmsStack, finalNMSTag);
|
||||
@@ -34,6 +41,8 @@ public class LegacyItemWrapper implements ItemWrapper<ItemStack> {
|
||||
return false;
|
||||
}
|
||||
|
||||
Object currentTag = FastNMS.INSTANCE.field$ItemStack$getOrCreateTag(this.nmsStack);
|
||||
|
||||
for (int i = 0; i < path.length - 1; i++) {
|
||||
Object pathSegment = path[i];
|
||||
if (pathSegment == null) return false;
|
||||
@@ -147,4 +156,38 @@ public class LegacyItemWrapper implements ItemWrapper<ItemStack> {
|
||||
public void shrink(int 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();
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
import net.momirealms.craftengine.core.item.CustomItem;
|
||||
import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
import net.momirealms.craftengine.core.item.NetworkItemBuildContext;
|
||||
import net.momirealms.craftengine.core.item.NetworkItemHandler;
|
||||
import net.momirealms.craftengine.core.item.modifier.ArgumentsModifier;
|
||||
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.text.component.ComponentProvider;
|
||||
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.ListTag;
|
||||
import net.momirealms.sparrow.nbt.StringTag;
|
||||
@@ -33,84 +34,214 @@ public final class LegacyNetworkItemHandler implements NetworkItemHandler<ItemSt
|
||||
|
||||
@Override
|
||||
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();
|
||||
boolean hasDifferentMaterial = false;
|
||||
if (optionalCustomItem.isPresent()) {
|
||||
BukkitCustomItem customItem = (BukkitCustomItem) optionalCustomItem.get();
|
||||
if (customItem.item() != FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject())) {
|
||||
wrapped = wrapped.unsafeTransmuteCopy(customItem.item(), wrapped.count());
|
||||
hasDifferentMaterial = true;
|
||||
}
|
||||
}
|
||||
if (!wrapped.hasTag(NETWORK_ITEM_TAG)) {
|
||||
if (hasDifferentMaterial) {
|
||||
return Optional.of(wrapped);
|
||||
forceReturn = true;
|
||||
}
|
||||
}
|
||||
|
||||
CompoundTag networkData = (CompoundTag) wrapped.getTag(NETWORK_ITEM_TAG);
|
||||
if (networkData == null) return Optional.empty();
|
||||
wrapped.removeTag(NETWORK_ITEM_TAG);
|
||||
for (Map.Entry<String, Tag> entry : networkData.entrySet()) {
|
||||
if (entry.getValue() instanceof CompoundTag tag) {
|
||||
NetworkItemHandler.apply(entry.getKey(), tag, wrapped);
|
||||
if (networkData != null) {
|
||||
forceReturn = true;
|
||||
// 移除tag
|
||||
wrapped.removeTag(NETWORK_ITEM_TAG);
|
||||
// 恢复物品
|
||||
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
|
||||
public Optional<Item<ItemStack>> s2c(Item<ItemStack> wrapped, Player player) {
|
||||
Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
|
||||
if (optionalCustomItem.isEmpty()) {
|
||||
if (!Config.interceptItem()) return Optional.empty();
|
||||
return new OtherItem(wrapped, false).process(NetworkTextReplaceContext.of(player));
|
||||
} else {
|
||||
BukkitCustomItem customItem = (BukkitCustomItem) optionalCustomItem.get();
|
||||
Object serverItem = FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject());
|
||||
boolean hasDifferentMaterial = serverItem == customItem.item() && serverItem != customItem.clientItem();
|
||||
if (hasDifferentMaterial) {
|
||||
wrapped = wrapped.unsafeTransmuteCopy(customItem.clientItem(), wrapped.count());
|
||||
}
|
||||
if (!customItem.hasClientBoundDataModifier()) {
|
||||
if (!Config.interceptItem() && !hasDifferentMaterial) return Optional.empty();
|
||||
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);
|
||||
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().s2c(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(previousItem), player);
|
||||
if (itemStack.isPresent()) {
|
||||
newItems.add(FastNMS.INSTANCE.field$CraftItemStack$handle(itemStack.get()));
|
||||
changed = true;
|
||||
} 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()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (tag.isEmpty()) {
|
||||
if (hasDifferentMaterial) {
|
||||
return Optional.of(wrapped);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
wrapped.setTag(tag, NETWORK_ITEM_TAG);
|
||||
return Optional.of(wrapped);
|
||||
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().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) {
|
||||
|
||||
@@ -31,95 +31,201 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
|
||||
|
||||
@Override
|
||||
public Optional<Item<ItemStack>> c2s(Item<ItemStack> wrapped) {
|
||||
Tag customData = wrapped.getSparrowNBTComponent(ComponentTypes.CUSTOM_DATA);
|
||||
if (!(customData instanceof CompoundTag compoundTag)) return Optional.empty();
|
||||
boolean forceReturn = false;
|
||||
|
||||
// 处理收纳袋
|
||||
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();
|
||||
boolean hasDifferentMaterial = false;
|
||||
if (optionalCustomItem.isPresent()) {
|
||||
BukkitCustomItem customItem = (BukkitCustomItem) optionalCustomItem.get();
|
||||
if (customItem.item() != FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject())) {
|
||||
wrapped = wrapped.unsafeTransmuteCopy(customItem.item(), wrapped.count());
|
||||
hasDifferentMaterial = true;
|
||||
forceReturn = true;
|
||||
}
|
||||
}
|
||||
CompoundTag networkData = compoundTag.getCompound(NETWORK_ITEM_TAG);
|
||||
if (networkData == null) {
|
||||
if (hasDifferentMaterial) {
|
||||
return Optional.of(wrapped);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
compoundTag.remove(NETWORK_ITEM_TAG);
|
||||
for (Map.Entry<String, Tag> entry : networkData.entrySet()) {
|
||||
if (entry.getValue() instanceof CompoundTag tag) {
|
||||
NetworkItemHandler.apply(entry.getKey(), tag, wrapped);
|
||||
|
||||
// 获取custom data
|
||||
Tag customData = wrapped.getSparrowNBTComponent(DataComponentTypes.CUSTOM_DATA);
|
||||
if (customData instanceof CompoundTag compoundTag) {
|
||||
CompoundTag networkData = compoundTag.getCompound(NETWORK_ITEM_TAG);
|
||||
if (networkData != null) {
|
||||
forceReturn = true;
|
||||
// 移除此tag
|
||||
compoundTag.remove(NETWORK_ITEM_TAG);
|
||||
|
||||
// 恢复物品
|
||||
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 Optional.of(wrapped);
|
||||
|
||||
return forceReturn ? Optional.of(wrapped) : Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Item<ItemStack>> s2c(Item<ItemStack> wrapped, Player player) {
|
||||
Item<ItemStack> original = wrapped;
|
||||
Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
|
||||
if (optionalCustomItem.isEmpty()) {
|
||||
if (!Config.interceptItem()) return Optional.empty();
|
||||
return new OtherItem(wrapped, false).process(NetworkTextReplaceContext.of(player));
|
||||
} else {
|
||||
BukkitCustomItem customItem = (BukkitCustomItem) optionalCustomItem.get();
|
||||
Object serverItem = FastNMS.INSTANCE.method$ItemStack$getItem(wrapped.getLiteralObject());
|
||||
boolean hasDifferentMaterial = serverItem == customItem.item() && serverItem != customItem.clientItem();
|
||||
if (hasDifferentMaterial) {
|
||||
wrapped = wrapped.unsafeTransmuteCopy(customItem.clientItem(), wrapped.count());
|
||||
}
|
||||
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);
|
||||
boolean forceReturn = false;
|
||||
|
||||
// 处理收纳袋
|
||||
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().s2c(FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(previousItem), player);
|
||||
if (itemStack.isPresent()) {
|
||||
newItems.add(FastNMS.INSTANCE.field$CraftItemStack$handle(itemStack.get()));
|
||||
changed = true;
|
||||
} else {
|
||||
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);
|
||||
newItems.add(previousItem);
|
||||
}
|
||||
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(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 (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<>();
|
||||
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) {
|
||||
@@ -143,7 +249,7 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
|
||||
for (String line : lore) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -157,7 +263,7 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
|
||||
Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(line);
|
||||
if (!tokens.isEmpty()) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -171,7 +277,7 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
|
||||
Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(line);
|
||||
if (!tokens.isEmpty()) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -179,41 +285,38 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
|
||||
}
|
||||
|
||||
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;
|
||||
String tagStr = nameTag.getAsString();
|
||||
Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(tagStr);
|
||||
Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(nameTag);
|
||||
if (!tokens.isEmpty()) {
|
||||
item.setNBTComponent(ComponentKeys.ITEM_NAME, AdventureHelper.componentToNbt(AdventureHelper.replaceText(AdventureHelper.nbtToComponent(nameTag), tokens, context)));
|
||||
tag.get().put(ComponentIds.ITEM_NAME, NetworkItemHandler.pack(Operation.ADD, nameTag));
|
||||
item.setNBTComponent(DataComponentKeys.ITEM_NAME, AdventureHelper.componentToNbt(AdventureHelper.replaceText(AdventureHelper.nbtToComponent(nameTag), tokens, context)));
|
||||
tag.get().put(DataComponentIds.ITEM_NAME, NetworkItemHandler.pack(Operation.ADD, nameTag));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
String tagStr = nameTag.getAsString();
|
||||
Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(tagStr);
|
||||
Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(nameTag);
|
||||
if (!tokens.isEmpty()) {
|
||||
item.setNBTComponent(ComponentKeys.CUSTOM_NAME, AdventureHelper.componentToNbt(AdventureHelper.replaceText(AdventureHelper.nbtToComponent(nameTag), tokens, context)));
|
||||
tag.get().put(ComponentIds.CUSTOM_NAME, NetworkItemHandler.pack(Operation.ADD, nameTag));
|
||||
item.setNBTComponent(DataComponentKeys.CUSTOM_NAME, AdventureHelper.componentToNbt(AdventureHelper.replaceText(AdventureHelper.nbtToComponent(nameTag), tokens, context)));
|
||||
tag.get().put(DataComponentIds.CUSTOM_NAME, NetworkItemHandler.pack(Operation.ADD, nameTag));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
if (!(loreTag instanceof ListTag listTag)) {
|
||||
return false;
|
||||
}
|
||||
ListTag newLore = new ListTag();
|
||||
for (Tag tag : listTag) {
|
||||
String tagStr = tag.getAsString();
|
||||
Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(tagStr);
|
||||
Map<String, ComponentProvider> tokens = CraftEngine.instance().fontManager().matchTags(tag);
|
||||
if (tokens.isEmpty()) {
|
||||
newLore.add(tag);
|
||||
} else {
|
||||
@@ -222,8 +325,8 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
item.setNBTComponent(ComponentKeys.LORE, newLore);
|
||||
tagSupplier.get().put(ComponentIds.LORE, NetworkItemHandler.pack(Operation.ADD, listTag));
|
||||
item.setNBTComponent(DataComponentKeys.LORE, newLore);
|
||||
tagSupplier.get().put(DataComponentIds.LORE, NetworkItemHandler.pack(Operation.ADD, listTag));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -257,9 +360,11 @@ public final class ModernNetworkItemHandler implements NetworkItemHandler<ItemSt
|
||||
this.globalChanged = true;
|
||||
}
|
||||
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());
|
||||
this.item.setNBTComponent(ComponentKeys.CUSTOM_DATA, customData);
|
||||
this.item.setNBTComponent(DataComponentKeys.CUSTOM_DATA, customData);
|
||||
return Optional.of(this.item);
|
||||
} else if (this.forceReturn) {
|
||||
return Optional.of(this.item);
|
||||
|
||||
@@ -3,12 +3,12 @@ package net.momirealms.craftengine.bukkit.item.behavior;
|
||||
import net.momirealms.craftengine.bukkit.block.behavior.StrippableBlockBehavior;
|
||||
import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
|
||||
import net.momirealms.craftengine.bukkit.nms.FastNMS;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||
import net.momirealms.craftengine.bukkit.util.*;
|
||||
import net.momirealms.craftengine.bukkit.world.BukkitExistingBlock;
|
||||
import net.momirealms.craftengine.core.block.BlockStateWrapper;
|
||||
import net.momirealms.craftengine.core.block.ImmutableBlockState;
|
||||
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.InteractionResult;
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
@@ -107,19 +107,7 @@ public class AxeItemBehavior extends ItemBehavior {
|
||||
player.swingHand(context.getHand());
|
||||
}
|
||||
// shrink item amount
|
||||
if (VersionHelper.isOrAbove1_20_5()) {
|
||||
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);
|
||||
}
|
||||
item.hurtAndBreak(1, player, context.getHand() == InteractionHand.MAIN_HAND ? EquipmentSlot.MAIN_HAND : EquipmentSlot.OFF_HAND);
|
||||
}
|
||||
return InteractionResult.SUCCESS_AND_CANCEL;
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ public class FlintAndSteelItemBehavior extends ItemBehavior {
|
||||
context.getHitResult(), (Item<ItemStack>) context.getItem())) {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
// 且没有shift
|
||||
// 且没有shift或者忽略潜行的可交互方块
|
||||
if (!player.isSecondaryUseActive()) {
|
||||
player.playSound(firePos, FLINT_SOUND, SoundSource.BLOCK, 1f, RandomUtils.generateRandomFloat(0.8f, 1.2f));
|
||||
}
|
||||
|
||||
@@ -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.CustomFurniture;
|
||||
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.Player;
|
||||
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);
|
||||
|
||||
List<AABB> aabbs = new ArrayList<>();
|
||||
for (HitBox hitBox : placement.hitBoxes()) {
|
||||
hitBox.initShapeForPlacement(finalPlacePosition.x(), finalPlacePosition.y(), finalPlacePosition.z(), (float) furnitureYaw, QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - furnitureYaw), 0).conjugate(), aabbs::add);
|
||||
for (HitBoxConfig hitBoxConfig : placement.hitBoxConfigs()) {
|
||||
hitBoxConfig.initShapeForPlacement(finalPlacePosition.x(), finalPlacePosition.y(), finalPlacePosition.z(), (float) furnitureYaw, QuaternionUtils.toQuaternionf(0, Math.toRadians(180 - furnitureYaw), 0).conjugate(), aabbs::add);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
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.plugin.reflection.minecraft.CoreReflections;
|
||||
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.context.UseOnContext;
|
||||
import net.momirealms.craftengine.core.pack.Pack;
|
||||
import net.momirealms.craftengine.core.pack.PendingConfigSection;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.locale.LocalizedResourceConfigException;
|
||||
import net.momirealms.craftengine.core.util.Direction;
|
||||
import net.momirealms.craftengine.core.util.Key;
|
||||
import net.momirealms.craftengine.core.util.MiscUtils;
|
||||
import net.momirealms.craftengine.core.util.ResourceConfigUtils;
|
||||
import net.momirealms.craftengine.core.world.BlockHitResult;
|
||||
import net.momirealms.craftengine.core.world.BlockPos;
|
||||
|
||||
@@ -3,7 +3,7 @@ package net.momirealms.craftengine.bukkit.item.factory;
|
||||
import com.google.gson.JsonElement;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
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.plugin.reflection.minecraft.CoreReflections;
|
||||
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.KeyUtils;
|
||||
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.FireworkExplosion;
|
||||
import net.momirealms.craftengine.core.item.data.Trim;
|
||||
@@ -49,7 +49,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
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;
|
||||
Object currentObj = rootMap;
|
||||
for (int i = 0; i < path.length; i++) {
|
||||
@@ -70,7 +70,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
@Override
|
||||
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;
|
||||
Object currentTag = FastNMS.INSTANCE.method$CustomData$getUnsafe(customData);
|
||||
for (int i = 0; i < path.length; i++) {
|
||||
@@ -90,7 +90,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
|
||||
|
||||
@Override
|
||||
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;
|
||||
Tag currentTag = rootTag;
|
||||
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);
|
||||
}
|
||||
|
||||
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 (valueTag instanceof CompoundTag) {
|
||||
@@ -151,7 +151,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
|
||||
currentTag.put(finalKey, valueTag);
|
||||
}
|
||||
|
||||
item.setSparrowNBTComponent(ComponentTypes.CUSTOM_DATA, rootTag);
|
||||
item.setSparrowNBTComponent(DataComponentTypes.CUSTOM_DATA, rootTag);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -161,14 +161,14 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
|
||||
|
||||
@Override
|
||||
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 (path.length == 1) {
|
||||
String key = path[0].toString();
|
||||
if (rootTag.containsKey(key)) {
|
||||
rootTag.remove(key);
|
||||
item.setSparrowNBTComponent(ComponentTypes.CUSTOM_DATA, rootTag);
|
||||
item.setSparrowNBTComponent(DataComponentTypes.CUSTOM_DATA, rootTag);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -191,7 +191,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
|
||||
String finalKey = path[path.length - 1].toString();
|
||||
if (parentTag.containsKey(finalKey)) {
|
||||
parentTag.remove(finalKey);
|
||||
item.setSparrowNBTComponent(ComponentTypes.CUSTOM_DATA, rootTag);
|
||||
item.setSparrowNBTComponent(DataComponentTypes.CUSTOM_DATA, rootTag);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -280,115 +280,115 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
|
||||
@Override
|
||||
protected void customModelData(ComponentItemWrapper item, Integer data) {
|
||||
if (data == null) {
|
||||
item.resetComponent(ComponentTypes.CUSTOM_MODEL_DATA);
|
||||
item.resetComponent(DataComponentTypes.CUSTOM_MODEL_DATA);
|
||||
} else {
|
||||
item.setJavaComponent(ComponentTypes.CUSTOM_MODEL_DATA, data);
|
||||
item.setJavaComponent(DataComponentTypes.CUSTOM_MODEL_DATA, data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<Integer> customModelData(ComponentItemWrapper item) {
|
||||
return item.getJavaComponent(ComponentTypes.CUSTOM_MODEL_DATA);
|
||||
return item.getJavaComponent(DataComponentTypes.CUSTOM_MODEL_DATA);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void customNameJson(ComponentItemWrapper item, String json) {
|
||||
if (json == null) {
|
||||
item.resetComponent(ComponentTypes.CUSTOM_NAME);
|
||||
item.resetComponent(DataComponentTypes.CUSTOM_NAME);
|
||||
} else {
|
||||
item.setJavaComponent(ComponentTypes.CUSTOM_NAME, json);
|
||||
item.setJavaComponent(DataComponentTypes.CUSTOM_NAME, json);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<String> customNameJson(ComponentItemWrapper item) {
|
||||
return item.getJavaComponent(ComponentTypes.CUSTOM_NAME);
|
||||
return item.getJavaComponent(DataComponentTypes.CUSTOM_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void itemNameJson(ComponentItemWrapper item, String json) {
|
||||
if (json == null) {
|
||||
item.resetComponent(ComponentTypes.ITEM_NAME);
|
||||
item.resetComponent(DataComponentTypes.ITEM_NAME);
|
||||
} else {
|
||||
item.setJavaComponent(ComponentTypes.ITEM_NAME, json);
|
||||
item.setJavaComponent(DataComponentTypes.ITEM_NAME, json);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<String> itemNameJson(ComponentItemWrapper item) {
|
||||
return item.getJavaComponent(ComponentTypes.ITEM_NAME);
|
||||
return item.getJavaComponent(DataComponentTypes.ITEM_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void skull(ComponentItemWrapper item, String skullData) {
|
||||
if (skullData == null) {
|
||||
item.resetComponent(ComponentTypes.PROFILE);
|
||||
item.resetComponent(DataComponentTypes.PROFILE);
|
||||
} else {
|
||||
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
|
||||
protected Optional<List<String>> loreJson(ComponentItemWrapper item) {
|
||||
return item.getJavaComponent(ComponentTypes.LORE);
|
||||
return item.getJavaComponent(DataComponentTypes.LORE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loreJson(ComponentItemWrapper item, List<String> lore) {
|
||||
if (lore == null || lore.isEmpty()) {
|
||||
item.resetComponent(ComponentTypes.LORE);
|
||||
item.resetComponent(DataComponentTypes.LORE);
|
||||
} else {
|
||||
item.setJavaComponent(ComponentTypes.LORE, lore);
|
||||
item.setJavaComponent(DataComponentTypes.LORE, lore);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean unbreakable(ComponentItemWrapper item) {
|
||||
return item.hasComponent(ComponentTypes.UNBREAKABLE);
|
||||
return item.hasComponent(DataComponentTypes.UNBREAKABLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unbreakable(ComponentItemWrapper item, boolean unbreakable) {
|
||||
if (unbreakable) {
|
||||
item.setJavaComponent(ComponentTypes.UNBREAKABLE, Map.of());
|
||||
item.setJavaComponent(DataComponentTypes.UNBREAKABLE, Map.of());
|
||||
} else {
|
||||
item.resetComponent(ComponentTypes.UNBREAKABLE);
|
||||
item.resetComponent(DataComponentTypes.UNBREAKABLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
protected void glint(ComponentItemWrapper item, Boolean glint) {
|
||||
if (glint == null) {
|
||||
item.resetComponent(ComponentTypes.ENCHANTMENT_GLINT_OVERRIDE);
|
||||
item.resetComponent(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE);
|
||||
} else {
|
||||
item.setJavaComponent(ComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, glint);
|
||||
item.setJavaComponent(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, glint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<Integer> damage(ComponentItemWrapper item) {
|
||||
return item.getJavaComponent(ComponentTypes.DAMAGE);
|
||||
return item.getJavaComponent(DataComponentTypes.DAMAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void damage(ComponentItemWrapper item, Integer damage) {
|
||||
if (damage == null) {
|
||||
item.resetComponent(ComponentTypes.DAMAGE);
|
||||
item.resetComponent(DataComponentTypes.DAMAGE);
|
||||
} else {
|
||||
item.setJavaComponent(ComponentTypes.DAMAGE, damage);
|
||||
item.setJavaComponent(DataComponentTypes.DAMAGE, damage);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<Color> dyedColor(ComponentItemWrapper item) {
|
||||
if (!item.hasComponent(ComponentTypes.DYED_COLOR)) return Optional.empty();
|
||||
Object javaObj = getJavaComponent(item, ComponentTypes.DYED_COLOR);
|
||||
if (!item.hasComponent(DataComponentTypes.DYED_COLOR)) return Optional.empty();
|
||||
Object javaObj = getJavaComponent(item, DataComponentTypes.DYED_COLOR);
|
||||
if (javaObj instanceof Integer integer) {
|
||||
return Optional.of(Color.fromDecimal(integer));
|
||||
} else if (javaObj instanceof Map<?, ?> map) {
|
||||
@@ -400,30 +400,30 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
|
||||
@Override
|
||||
protected void dyedColor(ComponentItemWrapper item, Color color) {
|
||||
if (color == null) {
|
||||
item.resetComponent(ComponentTypes.DYED_COLOR);
|
||||
item.resetComponent(DataComponentTypes.DYED_COLOR);
|
||||
} else {
|
||||
item.setJavaComponent(ComponentTypes.DYED_COLOR, color.color());
|
||||
item.setJavaComponent(DataComponentTypes.DYED_COLOR, color.color());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
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());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void maxDamage(ComponentItemWrapper item, Integer damage) {
|
||||
if (damage == null) {
|
||||
item.resetComponent(ComponentTypes.MAX_DAMAGE);
|
||||
item.resetComponent(DataComponentTypes.MAX_DAMAGE);
|
||||
} else {
|
||||
item.setJavaComponent(ComponentTypes.MAX_DAMAGE, damage);
|
||||
item.setJavaComponent(DataComponentTypes.MAX_DAMAGE, damage);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
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();
|
||||
try {
|
||||
Map<String, Integer> map = EnchantmentUtils.toMap(enchant);
|
||||
@@ -439,26 +439,26 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
|
||||
@Override
|
||||
protected void enchantments(ComponentItemWrapper item, List<Enchantment> enchantments) {
|
||||
if (enchantments == null || enchantments.isEmpty()) {
|
||||
item.resetComponent(ComponentTypes.ENCHANTMENTS);
|
||||
item.resetComponent(DataComponentTypes.ENCHANTMENTS);
|
||||
} else {
|
||||
Map<String, Integer> enchants = new HashMap<>();
|
||||
for (Enchantment enchantment : enchantments) {
|
||||
enchants.put(enchantment.id().toString(), enchantment.level());
|
||||
}
|
||||
item.setJavaComponent(ComponentTypes.ENCHANTMENTS, enchants);
|
||||
item.setJavaComponent(DataComponentTypes.ENCHANTMENTS, enchants);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void storedEnchantments(ComponentItemWrapper item, List<Enchantment> enchantments) {
|
||||
if (enchantments == null || enchantments.isEmpty()) {
|
||||
item.resetComponent(ComponentTypes.STORED_ENCHANTMENTS);
|
||||
item.resetComponent(DataComponentTypes.STORED_ENCHANTMENTS);
|
||||
} else {
|
||||
Map<String, Integer> enchants = new HashMap<>();
|
||||
for (Enchantment enchantment : enchantments) {
|
||||
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
|
||||
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());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void maxStackSize(ComponentItemWrapper item, Integer maxStackSize) {
|
||||
if (maxStackSize == null) {
|
||||
item.resetComponent(ComponentTypes.MAX_STACK_SIZE);
|
||||
item.resetComponent(DataComponentTypes.MAX_STACK_SIZE);
|
||||
} else {
|
||||
item.setJavaComponent(ComponentTypes.MAX_STACK_SIZE, maxStackSize);
|
||||
item.setJavaComponent(DataComponentTypes.MAX_STACK_SIZE, maxStackSize);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void repairCost(ComponentItemWrapper item, Integer data) {
|
||||
if (data == null) {
|
||||
item.resetComponent(ComponentTypes.REPAIR_COST);
|
||||
item.resetComponent(DataComponentTypes.REPAIR_COST);
|
||||
} else {
|
||||
item.setJavaComponent(ComponentTypes.REPAIR_COST, data);
|
||||
item.setJavaComponent(DataComponentTypes.REPAIR_COST, data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<Integer> repairCost(ComponentItemWrapper item) {
|
||||
return item.getJavaComponent(ComponentTypes.REPAIR_COST);
|
||||
return item.getJavaComponent(DataComponentTypes.REPAIR_COST);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void trim(ComponentItemWrapper item, Trim trim) {
|
||||
if (trim == null) {
|
||||
item.resetComponent(ComponentTypes.TRIM);
|
||||
item.resetComponent(DataComponentTypes.TRIM);
|
||||
} else {
|
||||
item.setJavaComponent(ComponentTypes.TRIM, Map.of(
|
||||
item.setJavaComponent(DataComponentTypes.TRIM, Map.of(
|
||||
"pattern", trim.pattern().asString(),
|
||||
"material", trim.material().asString()
|
||||
));
|
||||
@@ -510,7 +510,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
|
||||
|
||||
@Override
|
||||
protected Optional<Trim> trim(ComponentItemWrapper item) {
|
||||
Optional<Object> trim = item.getJavaComponent(ComponentTypes.TRIM);
|
||||
Optional<Object> trim = item.getJavaComponent(DataComponentTypes.TRIM);
|
||||
if (trim.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -522,7 +522,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
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();
|
||||
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);
|
||||
@@ -542,9 +542,9 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
|
||||
@Override
|
||||
protected void fireworkExplosion(ComponentItemWrapper item, FireworkExplosion explosion) {
|
||||
if (explosion == null) {
|
||||
item.resetComponent(ComponentTypes.FIREWORK_EXPLOSION);
|
||||
item.resetComponent(DataComponentTypes.FIREWORK_EXPLOSION);
|
||||
} else {
|
||||
item.setJavaComponent(ComponentTypes.FIREWORK_EXPLOSION, Map.of(
|
||||
item.setJavaComponent(DataComponentTypes.FIREWORK_EXPLOSION, Map.of(
|
||||
"shape", explosion.shape().getName(),
|
||||
"has_trail", explosion.hasTrail(),
|
||||
"has_twinkle", explosion.hasTwinkle(),
|
||||
@@ -590,7 +590,7 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
|
||||
|
||||
@Override
|
||||
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();
|
||||
compoundTag.put("modifiers", modifiers);
|
||||
for (AttributeModifier modifier : modifierList) {
|
||||
@@ -607,6 +607,16 @@ public class ComponentItemFactory1_20_5 extends BukkitItemFactory<ComponentItemW
|
||||
modifierTag.putString("operation", modifier.operation().id());
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package net.momirealms.craftengine.bukkit.item.factory;
|
||||
|
||||
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.plugin.CraftEngine;
|
||||
|
||||
@@ -16,7 +16,7 @@ public class ComponentItemFactory1_21 extends ComponentItemFactory1_20_5 {
|
||||
|
||||
@Override
|
||||
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(
|
||||
(String) song.get("song"),
|
||||
(boolean) song.getOrDefault("show_in_tooltip", true))
|
||||
@@ -25,7 +25,7 @@ public class ComponentItemFactory1_21 extends ComponentItemFactory1_20_5 {
|
||||
|
||||
@Override
|
||||
protected void jukeboxSong(ComponentItemWrapper item, JukeboxPlayable data) {
|
||||
item.setJavaComponent(ComponentTypes.JUKEBOX_PLAYABLE, Map.of(
|
||||
item.setJavaComponent(DataComponentTypes.JUKEBOX_PLAYABLE, Map.of(
|
||||
"song", data.song(),
|
||||
"show_in_tooltip", true
|
||||
));
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package net.momirealms.craftengine.bukkit.item.factory;
|
||||
|
||||
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.item.setting.EquipmentData;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
@@ -21,43 +21,43 @@ public class ComponentItemFactory1_21_2 extends ComponentItemFactory1_21 {
|
||||
@Override
|
||||
protected void tooltipStyle(ComponentItemWrapper item, String data) {
|
||||
if (data == null) {
|
||||
item.resetComponent(ComponentTypes.TOOLTIP_STYLE);
|
||||
item.resetComponent(DataComponentTypes.TOOLTIP_STYLE);
|
||||
} else {
|
||||
item.setJavaComponent(ComponentTypes.TOOLTIP_STYLE, data);
|
||||
item.setJavaComponent(DataComponentTypes.TOOLTIP_STYLE, data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<String> tooltipStyle(ComponentItemWrapper item) {
|
||||
return item.getJavaComponent(ComponentTypes.TOOLTIP_STYLE);
|
||||
return item.getJavaComponent(DataComponentTypes.TOOLTIP_STYLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void itemModel(ComponentItemWrapper item, String data) {
|
||||
if (data == null) {
|
||||
item.resetComponent(ComponentTypes.ITEM_MODEL);
|
||||
item.resetComponent(DataComponentTypes.ITEM_MODEL);
|
||||
} else {
|
||||
item.setJavaComponent(ComponentTypes.ITEM_MODEL, data);
|
||||
item.setJavaComponent(DataComponentTypes.ITEM_MODEL, data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<String> itemModel(ComponentItemWrapper item) {
|
||||
return item.getJavaComponent(ComponentTypes.ITEM_MODEL);
|
||||
return item.getJavaComponent(DataComponentTypes.ITEM_MODEL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void equippable(ComponentItemWrapper item, EquipmentData data) {
|
||||
if (data == null) {
|
||||
item.resetComponent(ComponentTypes.EQUIPPABLE);
|
||||
item.resetComponent(DataComponentTypes.EQUIPPABLE);
|
||||
} else {
|
||||
item.setSparrowNBTComponent(ComponentTypes.EQUIPPABLE, data.toNBT());
|
||||
item.setSparrowNBTComponent(DataComponentTypes.EQUIPPABLE, data.toNBT());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
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();
|
||||
Map<String, Object> data = MiscUtils.castToMap(optionalData.get(), false);
|
||||
String slot = data.get("slot").toString();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package net.momirealms.craftengine.bukkit.item.factory;
|
||||
|
||||
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 java.util.List;
|
||||
@@ -16,7 +16,7 @@ public class ComponentItemFactory1_21_4 extends ComponentItemFactory1_21_2 {
|
||||
|
||||
@Override
|
||||
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();
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> data = (Map<String, Object>) optional.get();
|
||||
@@ -29,9 +29,9 @@ public class ComponentItemFactory1_21_4 extends ComponentItemFactory1_21_2 {
|
||||
@Override
|
||||
protected void customModelData(ComponentItemWrapper item, Integer data) {
|
||||
if (data == null) {
|
||||
item.resetComponent(ComponentTypes.CUSTOM_MODEL_DATA);
|
||||
item.resetComponent(DataComponentTypes.CUSTOM_MODEL_DATA);
|
||||
} 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())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import net.kyori.adventure.text.Component;
|
||||
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.item.ComponentKeys;
|
||||
import net.momirealms.craftengine.core.item.DataComponentKeys;
|
||||
import net.momirealms.craftengine.core.item.data.JukeboxPlayable;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.util.AdventureHelper;
|
||||
@@ -30,23 +30,23 @@ public class ComponentItemFactory1_21_5 extends ComponentItemFactory1_21_4 {
|
||||
@Override
|
||||
protected void customNameJson(ComponentItemWrapper item, String json) {
|
||||
if (json == null) {
|
||||
item.resetComponent(ComponentTypes.CUSTOM_NAME);
|
||||
item.resetComponent(DataComponentTypes.CUSTOM_NAME);
|
||||
} else {
|
||||
item.setSparrowNBTComponent(ComponentTypes.CUSTOM_NAME, AdventureHelper.componentToNbt(AdventureHelper.jsonToComponent(json)));
|
||||
item.setSparrowNBTComponent(DataComponentTypes.CUSTOM_NAME, AdventureHelper.componentToNbt(AdventureHelper.jsonToComponent(json)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
protected void customNameComponent(ComponentItemWrapper item, Component component) {
|
||||
if (component == null) {
|
||||
item.resetComponent(ComponentTypes.CUSTOM_NAME);
|
||||
item.resetComponent(DataComponentTypes.CUSTOM_NAME);
|
||||
} 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
|
||||
protected void itemNameJson(ComponentItemWrapper item, String json) {
|
||||
if (json == null) {
|
||||
item.resetComponent(ComponentTypes.ITEM_NAME);
|
||||
item.resetComponent(DataComponentTypes.ITEM_NAME);
|
||||
} else {
|
||||
item.setSparrowNBTComponent(ComponentTypes.ITEM_NAME, AdventureHelper.componentToNbt(AdventureHelper.jsonToComponent(json)));
|
||||
item.setSparrowNBTComponent(DataComponentTypes.ITEM_NAME, AdventureHelper.componentToNbt(AdventureHelper.jsonToComponent(json)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void itemNameComponent(ComponentItemWrapper item, Component component) {
|
||||
if (component == null) {
|
||||
item.resetComponent(ComponentTypes.ITEM_NAME);
|
||||
item.resetComponent(DataComponentTypes.ITEM_NAME);
|
||||
} else {
|
||||
item.setSparrowNBTComponent(ComponentTypes.ITEM_NAME, AdventureHelper.componentToNbt(component));
|
||||
item.setSparrowNBTComponent(DataComponentTypes.ITEM_NAME, AdventureHelper.componentToNbt(component));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
protected Optional<List<String>> loreJson(ComponentItemWrapper item) {
|
||||
if (!item.hasComponent(ComponentTypes.LORE)) return Optional.empty();
|
||||
Optional<JsonElement> json = item.getJsonComponent(ComponentTypes.LORE);
|
||||
if (!item.hasComponent(DataComponentTypes.LORE)) return Optional.empty();
|
||||
Optional<JsonElement> json = item.getJsonComponent(DataComponentTypes.LORE);
|
||||
if (json.isEmpty()) return Optional.empty();
|
||||
List<String> lore = new ArrayList<>();
|
||||
for (JsonElement jsonElement : (JsonArray) json.get()) {
|
||||
@@ -93,40 +93,40 @@ public class ComponentItemFactory1_21_5 extends ComponentItemFactory1_21_4 {
|
||||
@Override
|
||||
protected void loreComponent(ComponentItemWrapper item, List<Component> lore) {
|
||||
if (lore == null || lore.isEmpty()) {
|
||||
item.resetComponent(ComponentTypes.LORE);
|
||||
item.resetComponent(DataComponentTypes.LORE);
|
||||
} else {
|
||||
List<Tag> loreTags = new ArrayList<>();
|
||||
for (Component component : lore) {
|
||||
loreTags.add(AdventureHelper.componentToTag(component));
|
||||
}
|
||||
item.setSparrowNBTComponent(ComponentTypes.LORE, new ListTag(loreTags));
|
||||
item.setSparrowNBTComponent(DataComponentTypes.LORE, new ListTag(loreTags));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loreJson(ComponentItemWrapper item, List<String> lore) {
|
||||
if (lore == null || lore.isEmpty()) {
|
||||
item.resetComponent(ComponentTypes.LORE);
|
||||
item.resetComponent(DataComponentTypes.LORE);
|
||||
} else {
|
||||
List<Tag> loreTags = new ArrayList<>();
|
||||
for (String json : lore) {
|
||||
loreTags.add(AdventureHelper.componentToTag(AdventureHelper.jsonToComponent(json)));
|
||||
}
|
||||
item.setSparrowNBTComponent(ComponentTypes.LORE, new ListTag(loreTags));
|
||||
item.setSparrowNBTComponent(DataComponentTypes.LORE, new ListTag(loreTags));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<JukeboxPlayable> jukeboxSong(ComponentItemWrapper item) {
|
||||
if (!item.hasComponent(ComponentTypes.JUKEBOX_PLAYABLE)) return Optional.empty();
|
||||
String song = (String) item.getJavaComponent(ComponentTypes.JUKEBOX_PLAYABLE).orElse(null);
|
||||
if (!item.hasComponent(DataComponentTypes.JUKEBOX_PLAYABLE)) return Optional.empty();
|
||||
String song = (String) item.getJavaComponent(DataComponentTypes.JUKEBOX_PLAYABLE).orElse(null);
|
||||
if (song == null) return Optional.empty();
|
||||
return Optional.of(new JukeboxPlayable(song, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void jukeboxSong(ComponentItemWrapper item, JukeboxPlayable data) {
|
||||
item.setJavaComponent(ComponentTypes.JUKEBOX_PLAYABLE, data.song());
|
||||
item.setJavaComponent(DataComponentTypes.JUKEBOX_PLAYABLE, data.song());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -151,6 +151,6 @@ public class ComponentItemFactory1_21_5 extends ComponentItemFactory1_21_4 {
|
||||
}
|
||||
modifiers.add(modifierTag);
|
||||
}
|
||||
item.setSparrowNBTComponent(ComponentKeys.ATTRIBUTE_MODIFIERS, modifiers);
|
||||
item.setSparrowNBTComponent(DataComponentKeys.ATTRIBUTE_MODIFIERS, modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
protected Optional<Trim> trim(LegacyItemWrapper item) {
|
||||
String material = item.getJavaTag("Trim", "material");
|
||||
|
||||
@@ -4,6 +4,7 @@ import io.papermc.paper.event.block.CompostItemEvent;
|
||||
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
|
||||
import net.momirealms.craftengine.bukkit.api.event.CustomBlockInteractEvent;
|
||||
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.BukkitItemManager;
|
||||
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.ItemBuildContext;
|
||||
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.setting.FoodData;
|
||||
import net.momirealms.craftengine.core.item.updater.ItemUpdateResult;
|
||||
@@ -141,7 +143,7 @@ public class ItemEventListener implements Listener {
|
||||
Direction direction = DirectionUtils.toDirection(event.getBlockFace());
|
||||
BlockPos pos = LocationUtils.toBlockPos(block.getLocation());
|
||||
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
|
||||
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();
|
||||
//PlayerUtils.resendItemInHand(player);
|
||||
}
|
||||
|
||||
Cancellable dummy = Cancellable.dummy();
|
||||
@@ -285,15 +286,13 @@ public class ItemEventListener implements Listener {
|
||||
}
|
||||
// custom item
|
||||
else {
|
||||
if (optionalCustomItem.get().settings().canPlaceRelatedVanillaBlock()) {
|
||||
// 如果用户设置了允许放置对应的原版方块,那么直接返回。
|
||||
// 这种情况下最好是return,以避免同时触发多个behavior发生冲突
|
||||
// 当用户选择其作为原版方块放下时,自定义行为可能已经不重要了?
|
||||
return;
|
||||
} else {
|
||||
// todo 实际上这里的处理并不正确,因为判断玩家是否能够放置那个方块需要更加细节的判断。比如玩家无法对着树叶放置火把,但是交互事件依然触发,此情况下不可丢弃自定义行为。
|
||||
if (optionalCustomItem.get().settings().disableVanillaBehavior()) {
|
||||
// 不能在BlockPlaceEvent里检测,是因为种农作物不触发相关事件
|
||||
// 允许尝试放置方块
|
||||
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) {
|
||||
if (!Config.triggerUpdatePickUp()) return;
|
||||
if (!(event.getEntity() instanceof Player player)) return;
|
||||
org.bukkit.entity.Item itemDrop = event.getItem();
|
||||
ItemStack itemStack = itemDrop.getItemStack();
|
||||
Item<ItemStack> wrapped = this.itemManager.wrap(itemStack);
|
||||
ItemUpdateResult result = this.itemManager.updateItem(wrapped, () -> ItemBuildContext.of(BukkitAdaptors.adapt(player)));
|
||||
if (result.updated()) {
|
||||
itemDrop.setItemStack((ItemStack) result.finalItem().getItem());
|
||||
Optional<CustomItem<ItemStack>> optionalCustomItem = wrapped.getCustomItem();
|
||||
if (optionalCustomItem.isEmpty()) return;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ import net.momirealms.craftengine.core.item.Item;
|
||||
import net.momirealms.craftengine.core.item.ItemBuildContext;
|
||||
import net.momirealms.craftengine.core.item.ItemKeys;
|
||||
import net.momirealms.craftengine.core.item.recipe.*;
|
||||
import net.momirealms.craftengine.core.plugin.CraftEngine;
|
||||
import net.momirealms.craftengine.core.plugin.config.Config;
|
||||
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
|
||||
import net.momirealms.craftengine.core.util.*;
|
||||
@@ -29,7 +28,6 @@ import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
@@ -113,88 +111,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
|
||||
}
|
||||
);
|
||||
|
||||
private static void modifyShapedRecipeIngredients(CustomShapedRecipe<ItemStack> recipe, Object shapedRecipe) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// nms 模块需要使用此方法
|
||||
public static List<Object> getIngredientLooks(List<UniqueKey> holders) {
|
||||
List<Object> itemStacks = new ArrayList<>();
|
||||
for (UniqueKey holder : holders) {
|
||||
@@ -213,28 +130,6 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
|
||||
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) {
|
||||
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 RecipeEventListener recipeEventListener;
|
||||
private final CrafterEventListener crafterEventListener;
|
||||
// 欺骗服务端使其以为自己处于启动阶段
|
||||
private Object stolenFeatureFlagSet;
|
||||
// 需要在主线程卸载的配方
|
||||
@@ -265,6 +161,7 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
|
||||
instance = this;
|
||||
this.plugin = plugin;
|
||||
this.recipeEventListener = new RecipeEventListener(plugin, this, plugin.itemManager());
|
||||
this.crafterEventListener = VersionHelper.isOrAbove1_21() ? new CrafterEventListener(plugin, this, plugin.itemManager()) : null;
|
||||
}
|
||||
|
||||
public static Object minecraftRecipeManager() {
|
||||
@@ -278,6 +175,9 @@ public class BukkitRecipeManager extends AbstractRecipeManager<ItemStack> {
|
||||
@Override
|
||||
public void delayedInit() {
|
||||
Bukkit.getPluginManager().registerEvents(this.recipeEventListener, this.plugin.javaPlugin());
|
||||
if (this.crafterEventListener != null) {
|
||||
Bukkit.getPluginManager().registerEvents(this.crafterEventListener, this.plugin.javaPlugin());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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了,估计没什么用
|
||||
}
|
||||
}
|
||||
@@ -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))));
|
||||
}
|
||||
}
|
||||
@@ -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))));
|
||||
}
|
||||
}
|
||||
@@ -4,15 +4,14 @@ import com.destroystokyo.paper.event.inventory.PrepareResultEvent;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
|
||||
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.reflection.bukkit.CraftBukkitReflections;
|
||||
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
|
||||
import net.momirealms.craftengine.bukkit.plugin.user.BukkitServerPlayer;
|
||||
import net.momirealms.craftengine.bukkit.util.ComponentUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.InventoryUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.ItemStackUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.LegacyInventoryUtils;
|
||||
import net.momirealms.craftengine.bukkit.util.*;
|
||||
import net.momirealms.craftengine.core.entity.player.InteractionHand;
|
||||
import net.momirealms.craftengine.core.item.*;
|
||||
import net.momirealms.craftengine.core.item.equipment.TrimBasedEquipment;
|
||||
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.ItemEquipment;
|
||||
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.function.Function;
|
||||
import net.momirealms.craftengine.core.util.*;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
@@ -35,6 +38,7 @@ import org.bukkit.event.inventory.*;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import org.bukkit.inventory.*;
|
||||
import org.bukkit.inventory.view.AnvilView;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -328,11 +332,11 @@ public class RecipeEventListener implements Listener {
|
||||
|
||||
Item<ItemStack> wrappedResult = BukkitItemManager.instance().wrap(event.getResult());
|
||||
if (!firstCustomItem.settings().canEnchant()) {
|
||||
Object previousEnchantment = wrappedFirst.getExactComponent(ComponentTypes.ENCHANTMENTS);
|
||||
Object previousEnchantment = wrappedFirst.getExactComponent(DataComponentTypes.ENCHANTMENTS);
|
||||
if (previousEnchantment != null) {
|
||||
wrappedResult.setExactComponent(ComponentTypes.ENCHANTMENTS, previousEnchantment);
|
||||
wrappedResult.setExactComponent(DataComponentTypes.ENCHANTMENTS, previousEnchantment);
|
||||
} else {
|
||||
wrappedResult.resetComponent(ComponentTypes.ENCHANTMENTS);
|
||||
wrappedResult.resetComponent(DataComponentTypes.ENCHANTMENTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -439,7 +443,7 @@ public class RecipeEventListener implements Listener {
|
||||
} catch (ReflectiveOperationException 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;
|
||||
wrappedFirst.customNameJson(null);
|
||||
} else if (!VersionHelper.isOrAbove1_20_5() && wrappedFirst.hasTag("display", "Name")) {
|
||||
@@ -594,66 +598,189 @@ public class RecipeEventListener implements Listener {
|
||||
return new Pair<>(first, second);
|
||||
}
|
||||
|
||||
// 准备结果阶段
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onCraftingRecipe(PrepareItemCraftEvent event) {
|
||||
public void onPrepareCraftingRecipe(PrepareItemCraftEvent event) {
|
||||
if (!Config.enableRecipeSystem()) return;
|
||||
org.bukkit.inventory.Recipe recipe = event.getRecipe();
|
||||
if (!(recipe instanceof CraftingRecipe craftingRecipe)) return;
|
||||
Key recipeId = Key.of(craftingRecipe.getKey().namespace(), craftingRecipe.getKey().value());
|
||||
CraftingInventory inventory = event.getInventory();
|
||||
Key recipeId = getCurrentCraftingRecipeId(inventory);
|
||||
if (recipeId == null) return;
|
||||
Optional<Recipe<ItemStack>> optionalRecipe = this.recipeManager.recipeById(recipeId);
|
||||
// 也许是其他插件注册的配方,直接无视
|
||||
if (optionalRecipe.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
CraftingInventory inventory = event.getInventory();
|
||||
if (!(optionalRecipe.get() instanceof CustomCraftingTableRecipe<ItemStack> craftingTableRecipe)) {
|
||||
inventory.setResult(null);
|
||||
return;
|
||||
}
|
||||
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
|
||||
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
|
||||
ItemBuildContext itemBuildContext = ItemBuildContext.of(serverPlayer);
|
||||
if (!craftingTableRecipe.canUse(itemBuildContext)) {
|
||||
inventory.setResult(null);
|
||||
return;
|
||||
if (craftingTableRecipe.hasCondition()) {
|
||||
if (!craftingTableRecipe.canUse(PlayerOptionalContext.of(serverPlayer))) {
|
||||
inventory.setResult(null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
CraftingInput<ItemStack> input = getCraftingInput(inventory);
|
||||
if (input == null) return;
|
||||
if (craftingTableRecipe.hasVisualResult() && VersionHelper.PREMIUM) {
|
||||
inventory.setResult(craftingTableRecipe.assembleVisual(input, itemBuildContext));
|
||||
ItemBuildContext itemBuildContext = ItemBuildContext.of(serverPlayer);
|
||||
inventory.setResult(craftingTableRecipe.assembleVisual(null, itemBuildContext));
|
||||
} 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)
|
||||
public void onCraftingFinish(CraftItemEvent event) {
|
||||
if (!Config.enableRecipeSystem() || !VersionHelper.PREMIUM) return;
|
||||
org.bukkit.inventory.Recipe recipe = event.getRecipe();
|
||||
if (!(recipe instanceof CraftingRecipe craftingRecipe)) return;
|
||||
Key recipeId = Key.of(craftingRecipe.getKey().namespace(), craftingRecipe.getKey().value());
|
||||
CraftingInventory inventory = event.getInventory();
|
||||
ItemStack visualResultOrReal = inventory.getResult();
|
||||
// 可惜我们没有结果
|
||||
if (ItemStackUtils.isEmpty(visualResultOrReal)) return;
|
||||
Key recipeId = getCurrentCraftingRecipeId(inventory);
|
||||
if (recipeId == null) return;
|
||||
Optional<Recipe<ItemStack>> optionalRecipe = this.recipeManager.recipeById(recipeId);
|
||||
// 也许是其他插件注册的配方,直接无视
|
||||
if (optionalRecipe.isEmpty()) {
|
||||
if (optionalRecipe.isEmpty() || !(optionalRecipe.get() instanceof CustomCraftingTableRecipe<ItemStack> ceRecipe)) {
|
||||
return;
|
||||
}
|
||||
CraftingInventory inventory = event.getInventory();
|
||||
if (!(optionalRecipe.get() instanceof CustomCraftingTableRecipe<ItemStack> craftingTableRecipe)) {
|
||||
// 没有视觉结果和函数你凑什么热闹
|
||||
if (!ceRecipe.hasVisualResult() && !ceRecipe.hasFunctions()) {
|
||||
return;
|
||||
}
|
||||
InventoryAction action = event.getAction();
|
||||
// 无事发生,不要更新
|
||||
if (action == InventoryAction.NOTHING) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
|
||||
BukkitServerPlayer serverPlayer = BukkitAdaptors.adapt(player);
|
||||
if (craftingTableRecipe.hasVisualResult()) {
|
||||
CraftingInput<ItemStack> input = getCraftingInput(inventory);
|
||||
inventory.setResult(craftingTableRecipe.assemble(input, ItemBuildContext.of(serverPlayer)));
|
||||
}
|
||||
Function<PlayerOptionalContext>[] functions = craftingTableRecipe.craftingFunctions();
|
||||
if (functions != null) {
|
||||
PlayerOptionalContext context = PlayerOptionalContext.of(serverPlayer);
|
||||
for (Function<PlayerOptionalContext> function : functions) {
|
||||
function.run(context);
|
||||
|
||||
// 对低版本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 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) {
|
||||
@@ -673,72 +800,294 @@ public class RecipeEventListener implements Listener {
|
||||
return input;
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onSmithingTrim(PrepareSmithingEvent event) {
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGH)
|
||||
public void onPrepareSmithingRecipe(PrepareSmithingEvent event) {
|
||||
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();
|
||||
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);
|
||||
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 CustomSmithingTrimRecipe<ItemStack> smithingTrimRecipe)) {
|
||||
event.setResult(null);
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
// 有函数的情况下,执行函数
|
||||
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());
|
||||
Optional<Recipe<ItemStack>> optionalRecipe = this.recipeManager.recipeById(recipeId);
|
||||
if (optionalRecipe.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (!(optionalRecipe.get() instanceof CustomSmithingTrimRecipe<ItemStack> smithingTrimRecipe)) {
|
||||
event.setResult(null);
|
||||
return;
|
||||
}
|
||||
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
|
||||
ItemBuildContext itemBuildContext = ItemBuildContext.of(BukkitAdaptors.adapt(player));
|
||||
if (!smithingTrimRecipe.canUse(itemBuildContext)) {
|
||||
event.setResult(null);
|
||||
return;
|
||||
}
|
||||
// trim 配方只能执行函数
|
||||
else if (recipe instanceof SmithingTrimRecipe trimRecipe) {
|
||||
Key recipeId = KeyUtils.namespacedKey2Key(trimRecipe.getKey());
|
||||
Optional<Recipe<ItemStack>> optionalRecipe = this.recipeManager.recipeById(recipeId);
|
||||
if (optionalRecipe.isEmpty() || !(optionalRecipe.get() instanceof CustomSmithingTrimRecipe<ItemStack> ceRecipe)) {
|
||||
return;
|
||||
}
|
||||
// 没有函数你凑什么热闹
|
||||
if (!ceRecipe.hasFunctions()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SmithingInput<ItemStack> input = getSmithingInput(inventory);
|
||||
if (smithingTrimRecipe.matches(input)) {
|
||||
ItemStack result = smithingTrimRecipe.assemble(getSmithingInput(inventory), itemBuildContext);
|
||||
event.setResult(result);
|
||||
} else {
|
||||
event.setResult(null);
|
||||
}
|
||||
}
|
||||
InventoryAction action = event.getAction();
|
||||
// 啥也没干
|
||||
if (action == InventoryAction.NOTHING) {
|
||||
return;
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onSmithingTransform(PrepareSmithingEvent event) {
|
||||
if (!Config.enableRecipeSystem()) return;
|
||||
SmithingInventory inventory = event.getInventory();
|
||||
if (!(inventory.getRecipe() instanceof SmithingTransformRecipe recipe)) return;
|
||||
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;
|
||||
}
|
||||
SmithingInput<ItemStack> input = getSmithingInput(inventory);
|
||||
if (smithingTransformRecipe.matches(input)) {
|
||||
Player player = InventoryUtils.getPlayerFromInventoryEvent(event);
|
||||
ItemStack processed = smithingTransformRecipe.assemble(input, ItemBuildContext.of(BukkitAdaptors.adapt(player)));
|
||||
event.setResult(processed);
|
||||
} else {
|
||||
event.setResult(null);
|
||||
// 对低版本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);
|
||||
|
||||
// 先取一次
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,8 +11,10 @@ import net.momirealms.craftengine.bukkit.util.ResourcePackUtils;
|
||||
import net.momirealms.craftengine.core.entity.player.Player;
|
||||
import net.momirealms.craftengine.core.pack.AbstractPackManager;
|
||||
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.config.Config;
|
||||
import net.momirealms.craftengine.core.util.Base64Utils;
|
||||
import net.momirealms.craftengine.core.util.VersionHelper;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.EventHandler;
|
||||
@@ -21,6 +23,7 @@ import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@@ -124,4 +127,9 @@ public class BukkitPackManager extends AbstractPackManager implements Listener {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.hitbox.BukkitHitBoxTypes;
|
||||
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.item.BukkitItemManager;
|
||||
import net.momirealms.craftengine.bukkit.item.behavior.BukkitItemBehaviors;
|
||||
@@ -87,7 +88,7 @@ public class BukkitCraftEngine extends CraftEngine {
|
||||
super.sharedClassPathAppender = sharedClassPathAppender;
|
||||
super.privateClassPathAppender = privateClassPathAppender;
|
||||
super.logger = logger;
|
||||
super.platform = new BukkitPlatform();
|
||||
super.platform = new BukkitPlatform(this);
|
||||
super.scheduler = new BukkitSchedulerAdapter(this);
|
||||
Class<?> compatibilityClass = ReflectionUtils.getClazz(COMPATIBILITY_CLASS);
|
||||
if (compatibilityClass != null) {
|
||||
@@ -209,6 +210,7 @@ public class BukkitCraftEngine extends CraftEngine {
|
||||
super.advancementManager = new BukkitAdvancementManager(this);
|
||||
super.projectileManager = new BukkitProjectileManager(this);
|
||||
super.furnitureManager = new BukkitFurnitureManager(this);
|
||||
super.seatManager = new BukkitSeatManager(this);
|
||||
super.onPluginEnable();
|
||||
super.compatibilityManager().onEnable();
|
||||
|
||||
|
||||
@@ -20,6 +20,11 @@ import org.bukkit.Particle;
|
||||
import java.util.Map;
|
||||
|
||||
public class BukkitPlatform implements Platform {
|
||||
private final BukkitCraftEngine plugin;
|
||||
|
||||
public BukkitPlatform(BukkitCraftEngine plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchCommand(String command) {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package net.momirealms.craftengine.bukkit.plugin.command;
|
||||
|
||||
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.command.feature.*;
|
||||
import net.momirealms.craftengine.core.plugin.command.AbstractCommandManager;
|
||||
import net.momirealms.craftengine.core.plugin.command.CommandFeature;
|
||||
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.entity.Player;
|
||||
import org.incendo.cloud.SenderMapper;
|
||||
import org.incendo.cloud.bukkit.CloudBukkitCapabilities;
|
||||
import org.incendo.cloud.execution.ExecutionCoordinator;
|
||||
@@ -16,6 +16,7 @@ import org.incendo.cloud.paper.LegacyPaperCommandManager;
|
||||
import org.incendo.cloud.setting.ManagerSetting;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class BukkitCommandManager extends AbstractCommandManager<CommandSender> {
|
||||
private final BukkitCraftEngine plugin;
|
||||
@@ -26,11 +27,7 @@ public class BukkitCommandManager extends AbstractCommandManager<CommandSender>
|
||||
plugin.javaPlugin(),
|
||||
ExecutionCoordinator.simpleCoordinator(),
|
||||
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.index = Index.create(CommandFeature::getFeatureID, List.of(
|
||||
new ReloadCommand(this, plugin),
|
||||
@@ -43,6 +40,8 @@ public class BukkitCommandManager extends AbstractCommandManager<CommandSender>
|
||||
new SearchRecipeAdminCommand(this, plugin),
|
||||
new SearchUsageAdminCommand(this, plugin),
|
||||
new TestCommand(this, plugin),
|
||||
new SetLocaleCommand(this, plugin),
|
||||
new UnsetLocaleCommand(this, plugin),
|
||||
new DebugGetBlockStateRegistryIdCommand(this, plugin),
|
||||
new DebugGetBlockInternalIdCommand(this, plugin),
|
||||
new DebugAppearanceStateUsageCommand(this, plugin),
|
||||
@@ -63,7 +62,8 @@ public class BukkitCommandManager extends AbstractCommandManager<CommandSender>
|
||||
new UploadPackCommand(this, plugin),
|
||||
new SendResourcePackCommand(this, plugin),
|
||||
new DebugSaveDefaultResourcesCommand(this, plugin),
|
||||
new DebugCleanCacheCommand(this, plugin)
|
||||
new DebugCleanCacheCommand(this, plugin),
|
||||
new DebugGenerateInternalAssetsCommand(this, plugin)
|
||||
// new OverrideGiveCommand(this, plugin)
|
||||
));
|
||||
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
|
||||
protected Sender wrapSender(CommandSender sender) {
|
||||
return this.plugin.senderFactory().wrap(sender);
|
||||
|
||||
@@ -20,7 +20,9 @@ import org.incendo.cloud.suggestion.Suggestion;
|
||||
import org.incendo.cloud.suggestion.SuggestionProvider;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class DebugAppearanceStateUsageCommand extends BukkitCommandFeature<CommandSender> {
|
||||
@@ -48,18 +50,30 @@ public class DebugAppearanceStateUsageCommand extends BukkitCommandFeature<Comma
|
||||
Component block = Component.text(baseBlockId + ": ");
|
||||
plugin().senderFactory().wrap(context.sender()).sendMessage(block);
|
||||
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<>();
|
||||
for (BlockStateWrapper appearance : appearances) {
|
||||
Component text = Component.text("|");
|
||||
List<Integer> reals = blockManager.appearanceToRealStates(appearance.registryId());
|
||||
if (reals.isEmpty()) {
|
||||
Component hover = Component.text(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));
|
||||
String cached = reversed.get(appearance);
|
||||
if (cached != null) {
|
||||
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 {
|
||||
boolean isFixed = allocator.isForcedState(appearance);
|
||||
NamedTextColor namedTextColor = isFixed ? NamedTextColor.RED : NamedTextColor.YELLOW;
|
||||
Component hover = Component.text(baseBlockId.value() + ":" + i).color(namedTextColor);
|
||||
boolean forced = allocator.isForcedState(appearance);
|
||||
NamedTextColor namedTextColor = forced ? NamedTextColor.RED : NamedTextColor.YELLOW;
|
||||
Component hover = Component.text((forced ? "[Forced] " : "[Auto] ") + baseBlockId.value() + ":" + i).color(namedTextColor);
|
||||
List<Component> hoverChildren = new ArrayList<>();
|
||||
hoverChildren.add(Component.newline());
|
||||
hoverChildren.add(Component.text(appearance.getAsString()).color(namedTextColor));
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user