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

Merge pull request #434 from Xiao-MoMi/dev

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

View File

@@ -29,18 +29,18 @@ subprojects {
filesMatching(arrayListOf("commands.yml", "config.yml")) {
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")

View File

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

View File

@@ -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")

View File

@@ -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;
}
}

View File

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

View File

@@ -1,7 +1,6 @@
package net.momirealms.craftengine.bukkit.compatibility.quickshop;
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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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();

View File

@@ -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);

View File

@@ -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;
}
}

View File

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

View File

@@ -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 {

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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<>();

View File

@@ -2,6 +2,7 @@ package net.momirealms.craftengine.bukkit.api;
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurniture;
import net.momirealms.craftengine.bukkit.entity.furniture.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;
}
/**

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -6,6 +6,9 @@ import net.momirealms.craftengine.bukkit.block.behavior.UnsafeCompositeBlockBeha
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.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);
}

View File

@@ -4,7 +4,7 @@ import net.momirealms.craftengine.core.block.AbstractCustomBlock;
import net.momirealms.craftengine.core.block.BlockStateVariantProvider;
import net.momirealms.craftengine.core.block.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);

View File

@@ -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());

View File

@@ -8,6 +8,8 @@ import net.momirealms.craftengine.core.block.BlockStateWrapper;
import net.momirealms.craftengine.core.block.StatePropertyAccessor;
import net.momirealms.craftengine.core.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();

View File

@@ -43,6 +43,7 @@ public class BukkitBlockBehaviors extends BlockBehaviors {
public static final Key ATTACHED_STEM_BLOCK = Key.from("craftengine:attached_stem_block");
public static final Key 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);
}
}

View File

@@ -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)

View File

@@ -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);

View File

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

View File

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

View File

@@ -3,6 +3,7 @@ package net.momirealms.craftengine.bukkit.block.behavior;
import net.momirealms.craftengine.bukkit.block.entity.BukkitBlockEntityTypes;
import net.momirealms.craftengine.bukkit.block.entity.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);
}

View File

@@ -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

View File

@@ -51,7 +51,7 @@ public class VerticalCropBlockBehavior extends BukkitBlockBehavior {
if (FastNMS.INSTANCE.method$BlockGetter$getBlockState(level, (this.direction ? LocationUtils.above(blockPos) : LocationUtils.below(blockPos))) == MBlocks.AIR$defaultState) {
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);

View File

@@ -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);
}

View File

@@ -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() {}
}

View File

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

View File

@@ -85,7 +85,7 @@ public class SimpleStorageBlockEntity extends BlockEntity {
}
if (VersionHelper.isOrAbove1_20_5()) {
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);

View File

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

View File

@@ -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")
);
}
}

View File

@@ -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();
}
}

View File

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

View File

@@ -7,13 +7,13 @@ import net.momirealms.craftengine.core.util.VersionHelper;
import java.util.Optional;
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;

View File

@@ -5,7 +5,7 @@ import net.momirealms.craftengine.core.entity.furniture.AnchorType;
import net.momirealms.craftengine.core.entity.furniture.CustomFurniture;
import net.momirealms.craftengine.core.entity.furniture.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;
}

View File

@@ -5,20 +5,16 @@ import it.unimi.dsi.fastutil.ints.IntArrayList;
import net.momirealms.craftengine.bukkit.entity.BukkitEntity;
import net.momirealms.craftengine.bukkit.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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -0,0 +1,70 @@
package net.momirealms.craftengine.bukkit.entity.furniture;
import net.momirealms.craftengine.bukkit.entity.seat.BukkitSeat;
import net.momirealms.craftengine.core.entity.furniture.Furniture;
import net.momirealms.craftengine.core.entity.furniture.HitBox;
import net.momirealms.craftengine.core.entity.furniture.HitBoxConfig;
import net.momirealms.craftengine.core.entity.furniture.HitBoxPart;
import net.momirealms.craftengine.core.entity.seat.Seat;
import net.momirealms.craftengine.core.entity.seat.SeatConfig;
import net.momirealms.craftengine.core.world.EntityHitResult;
import net.momirealms.craftengine.core.world.Vec3d;
import net.momirealms.sparrow.nbt.CompoundTag;
import java.util.Optional;
public class BukkitHitBox implements HitBox {
private final Furniture furniture;
private final HitBoxConfig config;
private final HitBoxPart[] parts;
private final Seat<HitBox>[] seats;
public BukkitHitBox(Furniture furniture, HitBoxConfig config, HitBoxPart[] parts) {
this.parts = parts;
this.config = config;
this.furniture = furniture;
this.seats = createSeats(config);
}
@SuppressWarnings("unchecked")
private Seat<HitBox>[] createSeats(HitBoxConfig config) {
SeatConfig[] seatConfigs = config.seats();
Seat<HitBox>[] seats = new Seat[seatConfigs.length];
for (int i = 0; i < seatConfigs.length; i++) {
seats[i] = new BukkitSeat<>(this, seatConfigs[i]);
}
return seats;
}
@Override
public HitBoxPart[] parts() {
return this.parts;
}
@Override
public HitBoxConfig config() {
return this.config;
}
@Override
public Seat<HitBox>[] seats() {
return this.seats;
}
@Override
public Optional<EntityHitResult> clip(Vec3d min, Vec3d max) {
for (HitBoxPart hbe : this.parts) {
Optional<EntityHitResult> result = hbe.aabb().clip(min, max);
if (result.isPresent()) {
return result;
}
}
return Optional.empty();
}
@Override
public void saveCustomData(CompoundTag data) {
data.putString("type", "furniture");
data.putInt("entity_id", this.furniture.baseEntityId());
}
}

View File

@@ -1,21 +1,24 @@
package net.momirealms.craftengine.bukkit.entity.furniture;
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());
}
}
}

View File

@@ -2,21 +2,15 @@ package net.momirealms.craftengine.bukkit.entity.furniture;
import com.destroystokyo.paper.event.entity.EntityAddToWorldEvent;
import com.destroystokyo.paper.event.entity.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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -6,6 +6,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflect
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeHolders;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.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);
}
}
}

View File

@@ -8,9 +8,11 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MAttributeH
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityTypes;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.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
);

View File

@@ -6,6 +6,7 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.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

View File

@@ -10,6 +10,7 @@ import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.MEntityType
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.NetworkReflections;
import net.momirealms.craftengine.bukkit.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
);

View File

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

View File

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

View File

@@ -6,7 +6,7 @@ import net.momirealms.craftengine.core.item.*;
import net.momirealms.craftengine.core.item.behavior.ItemBehavior;
import net.momirealms.craftengine.core.item.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;
}

View File

@@ -22,8 +22,10 @@ import net.momirealms.craftengine.core.item.recipe.UniqueIdItem;
import net.momirealms.craftengine.core.pack.AbstractPackManager;
import net.momirealms.craftengine.core.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);

View File

@@ -8,13 +8,19 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.CoreReflections;
import net.momirealms.craftengine.bukkit.plugin.reflection.minecraft.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());
}
}

View File

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

View File

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

View File

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

View File

@@ -3,10 +3,16 @@ package net.momirealms.craftengine.bukkit.item;
import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.bukkit.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();
}
}

View File

@@ -4,7 +4,7 @@ import net.momirealms.craftengine.bukkit.nms.FastNMS;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.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) {

View File

@@ -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);

View File

@@ -3,12 +3,12 @@ package net.momirealms.craftengine.bukkit.item.behavior;
import net.momirealms.craftengine.bukkit.block.behavior.StrippableBlockBehavior;
import net.momirealms.craftengine.bukkit.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;
}

View File

@@ -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));
}

View File

@@ -12,7 +12,7 @@ import net.momirealms.craftengine.bukkit.util.LocationUtils;
import net.momirealms.craftengine.core.entity.furniture.AnchorType;
import net.momirealms.craftengine.core.entity.furniture.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;
}
}

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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
));

View File

@@ -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();

View File

@@ -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())));
}
}
}

View File

@@ -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);
}
}

View File

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

View File

@@ -4,6 +4,7 @@ import io.papermc.paper.event.block.CompostItemEvent;
import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
import net.momirealms.craftengine.bukkit.api.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;
}
}

View File

@@ -19,7 +19,6 @@ import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import net.momirealms.craftengine.core.item.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

View File

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

View File

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

View File

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

View File

@@ -4,15 +4,14 @@ import com.destroystokyo.paper.event.inventory.PrepareResultEvent;
import net.kyori.adventure.text.Component;
import net.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);
}
}
}
}
}

View File

@@ -11,8 +11,10 @@ import net.momirealms.craftengine.bukkit.util.ResourcePackUtils;
import net.momirealms.craftengine.core.entity.player.Player;
import net.momirealms.craftengine.core.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);
}
}

View File

@@ -9,6 +9,7 @@ import net.momirealms.craftengine.bukkit.block.entity.renderer.element.BukkitBlo
import net.momirealms.craftengine.bukkit.entity.furniture.BukkitFurnitureManager;
import net.momirealms.craftengine.bukkit.entity.furniture.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();

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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));

View File

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

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