9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-27 10:59:07 +00:00

添加村民交易物品映射

This commit is contained in:
XiaoMoMi
2025-10-04 22:17:31 +08:00
parent 22ca008a9d
commit 3906a55b2d
9 changed files with 255 additions and 97 deletions

View File

@@ -1,91 +0,0 @@
//package net.momirealms.craftengine.bukkit.plugin.command.feature;
//
//import net.kyori.adventure.text.Component;
//import net.momirealms.craftengine.bukkit.api.BukkitAdaptors;
//import net.momirealms.craftengine.bukkit.item.BukkitItemManager;
//import net.momirealms.craftengine.bukkit.plugin.command.BukkitCommandFeature;
//import net.momirealms.craftengine.bukkit.util.PlayerUtils;
//import net.momirealms.craftengine.core.item.Item;
//import net.momirealms.craftengine.core.plugin.CraftEngine;
//import net.momirealms.craftengine.core.plugin.command.CraftEngineCommandManager;
//import net.momirealms.craftengine.core.plugin.locale.MessageConstants;
//import net.momirealms.craftengine.core.util.Key;
//import org.bukkit.NamespacedKey;
//import org.bukkit.command.CommandSender;
//import org.bukkit.entity.Player;
//import org.bukkit.inventory.ItemStack;
//import org.checkerframework.checker.nullness.qual.NonNull;
//import org.incendo.cloud.Command;
//import org.incendo.cloud.bukkit.data.MultiplePlayerSelector;
//import org.incendo.cloud.bukkit.parser.NamespacedKeyParser;
//import org.incendo.cloud.bukkit.parser.selector.MultiplePlayerSelectorParser;
//import org.incendo.cloud.context.CommandContext;
//import org.incendo.cloud.context.CommandInput;
//import org.incendo.cloud.parser.standard.IntegerParser;
//import org.incendo.cloud.suggestion.Suggestion;
//import org.incendo.cloud.suggestion.SuggestionProvider;
//
//import java.util.Collection;
//import java.util.concurrent.CompletableFuture;
//
//public class OverrideGiveCommand extends BukkitCommandFeature<CommandSender> {
//
// public OverrideGiveCommand(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("player", MultiplePlayerSelectorParser.multiplePlayerSelectorParser(true))
// .required("id", NamespacedKeyParser.namespacedKeyComponent().suggestionProvider(new SuggestionProvider<>() {
// @Override
// public @NonNull CompletableFuture<? extends @NonNull Iterable<? extends @NonNull Suggestion>> suggestionsFuture(@NonNull CommandContext<Object> context, @NonNull CommandInput input) {
// return CompletableFuture.completedFuture(plugin().itemManager().cachedAllItemSuggestions());
// }
// }))
// .optional("amount", IntegerParser.integerParser(1, 6400))
// .handler(context -> {
// MultiplePlayerSelector selector = context.get("player");
// int amount = context.getOrDefault("amount", 1);
// NamespacedKey namespacedKey = context.get("id");
// Key itemId = Key.of(namespacedKey.namespace(), namespacedKey.value());
// Collection<Player> players = selector.values();
//
// Component anyItemDisplayName = null;
//
// for (Player player : players) {
// Item<ItemStack> builtItem = BukkitItemManager.instance().createWrappedItem(itemId, BukkitAdaptors.adapt(player));
// if (builtItem == null) {
// return;
// }
// int maxStack = builtItem.maxStackSize();
// anyItemDisplayName = builtItem.hoverNameComponent()
// .orElseGet(() -> {
// if (builtItem.isCustomItem()) {
// return Component.text(itemId.asString());
// } else {
// return Component.translatable("item.minecraft." + itemId.value());
// }
// });
//
// if (amount > maxStack * 100) {
// plugin().senderFactory().wrap(context.sender()).sendMessage(Component.translatable("commands.give.failed.toomanyitems", Component.text(maxStack * 100), anyItemDisplayName);
// return;
// }
//
// PlayerUtils.giveItem(player, amount, builtItem);
// }
// if (players.size() == 1) {
// plugin().senderFactory().wrap(context.sender()).sendMessage(Component.translatable("commands.give.success.single", Component.text(amount), anyItemDisplayName, ));
// } else if (players.size() > 1) {
// handleFeedback(context, MessageConstants.COMMAND_ITEM_GIVE_SUCCESS_MULTIPLE, Component.text(amount), Component.text(itemId.toString()), Component.text(players.size()));
// }
// });
// }
//
// @Override
// public String getFeatureID() {
// return "override_minecraft_give";
// }
//}

View File

@@ -58,6 +58,7 @@ import net.momirealms.craftengine.bukkit.world.BukkitWorldManager;
import net.momirealms.craftengine.core.advancement.network.AdvancementHolder;
import net.momirealms.craftengine.core.advancement.network.AdvancementProgress;
import net.momirealms.craftengine.core.block.ImmutableBlockState;
import net.momirealms.craftengine.core.block.entity.render.DynamicBlockEntityRenderer;
import net.momirealms.craftengine.core.entity.player.InteractionHand;
import net.momirealms.craftengine.core.font.FontManager;
import net.momirealms.craftengine.core.font.IllegalCharacterProcessResult;
@@ -68,6 +69,7 @@ import net.momirealms.craftengine.core.item.context.UseOnContext;
import net.momirealms.craftengine.core.item.recipe.network.legacy.LegacyRecipeHolder;
import net.momirealms.craftengine.core.item.recipe.network.modern.RecipeBookEntry;
import net.momirealms.craftengine.core.item.recipe.network.modern.display.RecipeDisplay;
import net.momirealms.craftengine.core.item.trade.MerchantOffer;
import net.momirealms.craftengine.core.pack.host.ResourcePackDownloadData;
import net.momirealms.craftengine.core.pack.host.ResourcePackHost;
import net.momirealms.craftengine.core.plugin.CraftEngine;
@@ -354,6 +356,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
registerC2SGamePacketListener(new ContainerClick1_20(), VersionHelper.isOrAbove1_21_5() ? -1 : this.packetIds.serverboundContainerClickPacket(), "ServerboundContainerClickPacket");
registerC2SGamePacketListener(new InteractEntityListener(), this.packetIds.serverboundInteractPacket(), "ServerboundInteractPacket");
registerC2SGamePacketListener(new CustomPayloadListener1_20(), VersionHelper.isOrAbove1_20_2() ? -1 : this.packetIds.serverboundCustomPayloadPacket(), "ServerboundCustomPayloadPacket");
registerS2CGamePacketListener(VersionHelper.isOrAbove1_20_5() ? new MerchantOffersListener1_20_5() : new MerchantOffersListener1_20(), this.packetIds.clientBoundMerchantOffersPacket(), "ClientboundMerchantOffersPacket");
registerS2CGamePacketListener(new AddEntityListener(RegistryUtils.currentEntityTypeRegistrySize()), this.packetIds.clientboundAddEntityPacket(), "ClientboundAddEntityPacket");
registerS2CGamePacketListener(
VersionHelper.isOrAbove1_20_3() ?
@@ -2167,7 +2170,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
float zDist = buf.readFloat();
float maxSpeed = buf.readFloat();
int count = buf.readInt();
Object option = FastNMS.INSTANCE.method$StreamCodec$decode(NetworkReflections.instance$ParticleTypes$STREAM_CODEC, buf);
Object option = FastNMS.INSTANCE.method$StreamDecoder$decode(NetworkReflections.instance$ParticleTypes$STREAM_CODEC, FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf));
if (option == null) return;
if (!CoreReflections.clazz$BlockParticleOption.isInstance(option)) return;
Object blockState = FastNMS.INSTANCE.field$BlockParticleOption$blockState(option);
@@ -2189,7 +2192,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
buf.writeFloat(zDist);
buf.writeFloat(maxSpeed);
buf.writeInt(count);
FastNMS.INSTANCE.method$StreamCodec$encode(NetworkReflections.instance$ParticleTypes$STREAM_CODEC, buf, remappedOption);
FastNMS.INSTANCE.method$StreamEncoder$encode(NetworkReflections.instance$ParticleTypes$STREAM_CODEC, FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf), remappedOption);
}
}
@@ -2214,7 +2217,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
float zDist = buf.readFloat();
float maxSpeed = buf.readFloat();
int count = buf.readInt();
Object option = FastNMS.INSTANCE.method$StreamCodec$decode(NetworkReflections.instance$ParticleTypes$STREAM_CODEC, buf);
Object option = FastNMS.INSTANCE.method$StreamDecoder$decode(NetworkReflections.instance$ParticleTypes$STREAM_CODEC, FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf));
if (option == null) return;
if (!CoreReflections.clazz$BlockParticleOption.isInstance(option)) return;
Object blockState = FastNMS.INSTANCE.field$BlockParticleOption$blockState(option);
@@ -2235,7 +2238,7 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
buf.writeFloat(zDist);
buf.writeFloat(maxSpeed);
buf.writeInt(count);
FastNMS.INSTANCE.method$StreamCodec$encode(NetworkReflections.instance$ParticleTypes$STREAM_CODEC, buf, remappedOption);
FastNMS.INSTANCE.method$StreamEncoder$encode(NetworkReflections.instance$ParticleTypes$STREAM_CODEC, FastNMS.INSTANCE.constructor$FriendlyByteBuf(buf), remappedOption);
}
}
@@ -3803,4 +3806,124 @@ public class BukkitNetworkManager implements NetworkManager, Listener, PluginMes
}
}
}
public static class MerchantOffersListener1_20 implements ByteBufferPacketListener {
@Override
public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) {
if (!(user instanceof BukkitServerPlayer serverPlayer)) return;
FriendlyByteBuf buf = event.getBuffer();
int containerId = buf.readContainerId();
BukkitItemManager manager = BukkitItemManager.instance();
List<MerchantOffer<ItemStack>> merchantOffers = buf.readCollection(ArrayList::new, byteBuf -> {
Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(byteBuf);
ItemStack cost1 = FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf);
ItemStack result = FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf);
ItemStack cost2 = FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf);
boolean outOfStock = byteBuf.readBoolean();
int uses = byteBuf.readInt();
int maxUses = byteBuf.readInt();
int xp = byteBuf.readInt();
int specialPrice = byteBuf.readInt();
float priceMultiplier = byteBuf.readFloat();
int demand = byteBuf.readInt();
return new MerchantOffer<>(manager.wrap(cost1), Optional.of(manager.wrap(cost2)), manager.wrap(result), outOfStock, uses, maxUses, xp, specialPrice, priceMultiplier, demand);
});
for (MerchantOffer<ItemStack> offer : merchantOffers) {
offer.applyClientboundData(item -> manager.s2c(item, serverPlayer));
}
int villagerLevel = buf.readVarInt();
int villagerXp = buf.readVarInt();
boolean showProgress = buf.readBoolean();
boolean canRestock = buf.readBoolean();
event.setChanged(true);
buf.clear();
buf.writeVarInt(event.packetID());
buf.writeContainerId(containerId);
buf.writeCollection(merchantOffers, (byteBuf, offer) -> {
Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(byteBuf);
FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(friendlyBuf, offer.cost1().getItem());
FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(friendlyBuf, offer.result().getItem());
FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(friendlyBuf, offer.cost2().get().getItem());
byteBuf.writeBoolean(offer.outOfStock());
byteBuf.writeInt(offer.uses());
byteBuf.writeInt(offer.maxUses());
byteBuf.writeInt(offer.xp());
byteBuf.writeInt(offer.specialPrice());
byteBuf.writeFloat(offer.priceMultiplier());
byteBuf.writeInt(offer.demand());
});
buf.writeVarInt(villagerLevel);
buf.writeVarInt(villagerXp);
buf.writeBoolean(showProgress);
buf.writeBoolean(canRestock);
}
}
public static class MerchantOffersListener1_20_5 implements ByteBufferPacketListener {
@SuppressWarnings("unchecked")
@Override
public void onPacketSend(NetWorkUser user, ByteBufPacketEvent event) {
if (!(user instanceof BukkitServerPlayer serverPlayer)) return;
FriendlyByteBuf buf = event.getBuffer();
int containerId = buf.readContainerId();
BukkitItemManager manager = BukkitItemManager.instance();
List<MerchantOffer<ItemStack>> merchantOffers = buf.readCollection(ArrayList::new, byteBuf -> {
Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(byteBuf);
ItemStack cost1 = FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(FastNMS.INSTANCE.field$ItemCost$itemStack(FastNMS.INSTANCE.method$StreamDecoder$decode(NetworkReflections.instance$ItemCost$STREAM_CODEC, friendlyBuf)));
ItemStack result = FastNMS.INSTANCE.method$FriendlyByteBuf$readItem(friendlyBuf);
Optional<ItemStack> cost2 = ((Optional<Object>) FastNMS.INSTANCE.method$StreamDecoder$decode(NetworkReflections.instance$ItemCost$OPTIONAL_STREAM_CODEC, friendlyBuf))
.map(cost -> FastNMS.INSTANCE.method$CraftItemStack$asCraftMirror(FastNMS.INSTANCE.field$ItemCost$itemStack(cost)));
boolean outOfStock = byteBuf.readBoolean();
int uses = byteBuf.readInt();
int maxUses = byteBuf.readInt();
int xp = byteBuf.readInt();
int specialPrice = byteBuf.readInt();
float priceMultiplier = byteBuf.readFloat();
int demand = byteBuf.readInt();
return new MerchantOffer<>(manager.wrap(cost1), cost2.map(manager::wrap), manager.wrap(result), outOfStock, uses, maxUses, xp, specialPrice, priceMultiplier, demand);
});
for (MerchantOffer<ItemStack> offer : merchantOffers) {
offer.applyClientboundData(item -> manager.s2c(item, serverPlayer));
}
int villagerLevel = buf.readVarInt();
int villagerXp = buf.readVarInt();
boolean showProgress = buf.readBoolean();
boolean canRestock = buf.readBoolean();
event.setChanged(true);
buf.clear();
buf.writeVarInt(event.packetID());
buf.writeContainerId(containerId);
buf.writeCollection(merchantOffers, (byteBuf, offer) -> {
Object friendlyBuf = FastNMS.INSTANCE.constructor$FriendlyByteBuf(byteBuf);
FastNMS.INSTANCE.method$StreamEncoder$encode(NetworkReflections.instance$ItemCost$STREAM_CODEC, friendlyBuf, itemStackToItemCost(offer.cost1().getLiteralObject(), offer.cost1().count()));
FastNMS.INSTANCE.method$FriendlyByteBuf$writeItem(friendlyBuf, offer.result().getItem());
FastNMS.INSTANCE.method$StreamEncoder$encode(NetworkReflections.instance$ItemCost$OPTIONAL_STREAM_CODEC, friendlyBuf, offer.cost2().map(it -> itemStackToItemCost(it.getLiteralObject(), it.count())));
byteBuf.writeBoolean(offer.outOfStock());
byteBuf.writeInt(offer.uses());
byteBuf.writeInt(offer.maxUses());
byteBuf.writeInt(offer.xp());
byteBuf.writeInt(offer.specialPrice());
byteBuf.writeFloat(offer.priceMultiplier());
byteBuf.writeInt(offer.demand());
});
buf.writeVarInt(villagerLevel);
buf.writeVarInt(villagerXp);
buf.writeBoolean(showProgress);
buf.writeBoolean(canRestock);
}
private Object itemStackToItemCost(Object itemStack, int count) {
return FastNMS.INSTANCE.constructor$ItemCost(
FastNMS.INSTANCE.method$Item$builtInRegistryHolder(FastNMS.INSTANCE.method$ItemStack$getItem(itemStack)),
count,
FastNMS.INSTANCE.method$DataComponentExactPredicate$allOf(FastNMS.INSTANCE.method$ItemStack$getComponents(itemStack))
);
}
}
}

View File

@@ -60,6 +60,8 @@ public interface PacketIds {
int clientboundUpdateAdvancementsPacket();
int clientBoundMerchantOffersPacket();
int serverboundContainerClickPacket();
int serverboundSetCreativeModeSlotPacket();

View File

@@ -161,6 +161,11 @@ public class PacketIds1_20 implements PacketIds {
return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundBlockEventPacket, PacketFlow.CLIENTBOUND);
}
@Override
public int clientBoundMerchantOffersPacket() {
return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ClientboundMerchantOffersPacket, PacketFlow.CLIENTBOUND);
}
@Override
public int serverboundContainerClickPacket() {
return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ServerboundContainerClickPacket, PacketFlow.SERVERBOUND);
@@ -171,7 +176,6 @@ public class PacketIds1_20 implements PacketIds {
return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ServerboundSetCreativeModeSlotPacket, PacketFlow.SERVERBOUND);
}
@Override
public int serverboundInteractPacket() {
return PlayPacketIdHelper.byClazz(NetworkReflections.clazz$ServerboundInteractPacket, PacketFlow.SERVERBOUND);

View File

@@ -160,6 +160,11 @@ public class PacketIds1_20_5 implements PacketIds {
return PlayPacketIdHelper.byName("minecraft:forget_level_chunk", PacketFlow.CLIENTBOUND);
}
@Override
public int clientBoundMerchantOffersPacket() {
return PlayPacketIdHelper.byName("minecraft:merchant_offers", PacketFlow.CLIENTBOUND);
}
@Override
public int serverboundContainerClickPacket() {
return PlayPacketIdHelper.byName("minecraft:container_click", PacketFlow.SERVERBOUND);

View File

@@ -4463,4 +4463,11 @@ public final class CoreReflections {
public static final Field field$EnumProperty$values = requireNonNull(
ReflectionUtils.getDeclaredField(clazz$EnumProperty, VersionHelper.isOrAbove1_21_2() ? List.class : ImmutableSet.class, 0)
);
public static final Class<?> clazz$ItemCost = MiscUtils.requireNonNullIf(
BukkitReflectionUtils.findReobfOrMojmapClass(
"world.item.trading.ItemCost",
"world.item.trading.ItemCost"
), VersionHelper.isOrAbove1_20_5()
);
}

View File

@@ -1692,4 +1692,25 @@ public final class NetworkReflections {
CoreReflections.clazz$SoundSource
)
);
public static final Class<?> clazz$ClientboundMerchantOffersPacket = requireNonNull(
BukkitReflectionUtils.findReobfOrMojmapClass(
"network.protocol.game.PacketPlayOutOpenWindowMerchant",
"network.protocol.game.ClientboundMerchantOffersPacket"
)
);
public static final Object instance$ItemCost$STREAM_CODEC;
public static final Object instance$ItemCost$OPTIONAL_STREAM_CODEC;
static {
try {
instance$ItemCost$STREAM_CODEC = !VersionHelper.isOrAbove1_20_5() ? null :
ReflectionUtils.getDeclaredField(CoreReflections.clazz$ItemCost, clazz$StreamCodec, 0).get(null);
instance$ItemCost$OPTIONAL_STREAM_CODEC = !VersionHelper.isOrAbove1_20_5() ? null :
ReflectionUtils.getDeclaredField(CoreReflections.clazz$ItemCost, clazz$StreamCodec, 1).get(null);
} catch (ReflectiveOperationException e) {
throw new ReflectionInitException("Failed to initialize ItemCost$STREAM_CODEC", e);
}
}
}

View File

@@ -0,0 +1,87 @@
package net.momirealms.craftengine.core.item.trade;
import net.momirealms.craftengine.core.item.Item;
import java.util.Optional;
import java.util.function.Function;
public class MerchantOffer<I> {
private Item<I> cost1;
private Optional<Item<I>> cost2;
private Item<I> result;
private final int uses;
private final int maxUses;
private final int specialPrice;
private final int xp;
private final float priceMultiplier;
private final int demand;
private final boolean outOfStock;
public MerchantOffer(Item<I> cost1,
Optional<Item<I>> cost2,
Item<I> result,
boolean outOfStock,
int uses,
int maxUses,
int xp,
int specialPrice,
float priceMultiplier,
int demand) {
this.cost1 = cost1;
this.cost2 = cost2;
this.result = result;
this.outOfStock = outOfStock;
this.uses = uses;
this.maxUses = maxUses;
this.specialPrice = specialPrice;
this.xp = xp;
this.priceMultiplier = priceMultiplier;
this.demand = demand;
}
public void applyClientboundData(Function<Item<I>, Item<I>> function) {
this.cost1 = function.apply(this.cost1);
this.cost2 = this.cost2.map(function);
this.result = function.apply(this.result);
}
public Item<I> cost1() {
return cost1;
}
public Optional<Item<I>> cost2() {
return cost2;
}
public Item<I> result() {
return result;
}
public int uses() {
return uses;
}
public int maxUses() {
return maxUses;
}
public int specialPrice() {
return specialPrice;
}
public int xp() {
return xp;
}
public float priceMultiplier() {
return priceMultiplier;
}
public int demand() {
return demand;
}
public boolean outOfStock() {
return outOfStock;
}
}

View File

@@ -50,7 +50,7 @@ byte_buddy_version=1.17.5
ahocorasick_version=0.6.3
snake_yaml_version=2.5
anti_grief_version=1.0.2
nms_helper_version=1.0.102
nms_helper_version=1.0.103
evalex_version=3.5.0
reactive_streams_version=1.0.4
amazon_awssdk_version=2.34.5