mirror of
https://github.com/LeavesMC/Leaves.git
synced 2025-12-19 14:59:32 +00:00
Support REI protocol (#391)
This commit is contained in:
@@ -1,38 +0,0 @@
|
|||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
||||||
From: violetc <58360096+s-yh-china@users.noreply.github.com>
|
|
||||||
Date: Thu, 27 Mar 2025 13:04:35 +0800
|
|
||||||
Subject: [PATCH] Recipe send all
|
|
||||||
|
|
||||||
|
|
||||||
diff --git a/net/minecraft/stats/ServerRecipeBook.java b/net/minecraft/stats/ServerRecipeBook.java
|
|
||||||
index e3985b70cee7f7d56f179aeef8c2a6a6b312d83a..a476f6b6554b4c7ba1625ab4b9da3bcf3d40955b 100644
|
|
||||||
--- a/net/minecraft/stats/ServerRecipeBook.java
|
|
||||||
+++ b/net/minecraft/stats/ServerRecipeBook.java
|
|
||||||
@@ -150,12 +150,23 @@ public class ServerRecipeBook extends RecipeBook {
|
|
||||||
|
|
||||||
public void sendInitialRecipeBook(ServerPlayer player) {
|
|
||||||
player.connection.send(new ClientboundRecipeBookSettingsPacket(this.getBookSettings()));
|
|
||||||
- List<ClientboundRecipeBookAddPacket.Entry> list = new ArrayList<>(this.known.size());
|
|
||||||
+ // Leaves start - recipe-send-all
|
|
||||||
+ List<ClientboundRecipeBookAddPacket.Entry> list;
|
|
||||||
|
|
||||||
- for (ResourceKey<Recipe<?>> resourceKey : this.known) {
|
|
||||||
- this.displayResolver
|
|
||||||
- .displaysForRecipe(resourceKey, entry -> list.add(new ClientboundRecipeBookAddPacket.Entry(entry, false, this.highlight.contains(resourceKey))));
|
|
||||||
+ if (org.leavesmc.leaves.LeavesConfig.protocol.recipeSendAll) {
|
|
||||||
+ list = new ArrayList<>(player.server.getRecipeManager().getRecipes().size());
|
|
||||||
+ player.server.getRecipeManager().getRecipes().stream().map(RecipeHolder::id).forEach( key -> {
|
|
||||||
+ this.displayResolver
|
|
||||||
+ .displaysForRecipe(key, entry -> list.add(new ClientboundRecipeBookAddPacket.Entry(entry, false, this.highlight.contains(key))));
|
|
||||||
+ });
|
|
||||||
+ } else {
|
|
||||||
+ list = new ArrayList<>(this.known.size());
|
|
||||||
+ for (ResourceKey<Recipe<?>> resourceKey : this.known) {
|
|
||||||
+ this.displayResolver
|
|
||||||
+ .displaysForRecipe(resourceKey, entry -> list.add(new ClientboundRecipeBookAddPacket.Entry(entry, false, this.highlight.contains(resourceKey))));
|
|
||||||
+ }
|
|
||||||
}
|
|
||||||
+ // Leaves end - recipe-send-all
|
|
||||||
|
|
||||||
player.connection.send(new ClientboundRecipeBookAddPacket(list, true));
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: violetc <58360096+s-yh-china@users.noreply.github.com>
|
||||||
|
Date: Thu, 27 Mar 2025 13:04:35 +0800
|
||||||
|
Subject: [PATCH] Support REI protocol
|
||||||
|
|
||||||
|
|
||||||
|
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
|
||||||
|
index d89a1aa9355205883412eaaf535dad30f945a4dc..33e05636164144b3d2bdbd091c72583728cc294e 100644
|
||||||
|
--- a/net/minecraft/server/players/PlayerList.java
|
||||||
|
+++ b/net/minecraft/server/players/PlayerList.java
|
||||||
|
@@ -1624,6 +1624,7 @@ public abstract class PlayerList {
|
||||||
|
serverPlayer.getRecipeBook().sendInitialRecipeBook(serverPlayer);
|
||||||
|
}
|
||||||
|
org.leavesmc.leaves.protocol.BBORProtocol.onDataPackReload(); // Leaves - bbor
|
||||||
|
+ org.leavesmc.leaves.protocol.rei.REIServerProtocol.onRecipeReload(); // Leaves - rei
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAllowCommandsForAllPlayers() {
|
||||||
|
diff --git a/net/minecraft/world/item/crafting/SmithingTransformRecipe.java b/net/minecraft/world/item/crafting/SmithingTransformRecipe.java
|
||||||
|
index 143601053a6aeea4396f8e0ee0746ff7d5bbb323..0af83ec1fe3aa85c6bfc814a1339a54e9d3725d6 100644
|
||||||
|
--- a/net/minecraft/world/item/crafting/SmithingTransformRecipe.java
|
||||||
|
+++ b/net/minecraft/world/item/crafting/SmithingTransformRecipe.java
|
||||||
|
@@ -87,6 +87,12 @@ public class SmithingTransformRecipe implements SmithingRecipe {
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
+ // Leaves start
|
||||||
|
+ public ItemStack getResult() {
|
||||||
|
+ return this.result.copy();
|
||||||
|
+ }
|
||||||
|
+ // Leaves end
|
||||||
|
+
|
||||||
|
// CraftBukkit start
|
||||||
|
@Override
|
||||||
|
public org.bukkit.inventory.Recipe toBukkitRecipe(org.bukkit.NamespacedKey id) {
|
||||||
@@ -841,13 +841,22 @@ public final class LeavesConfig {
|
|||||||
@GlobalConfig("lms-paster-protocol")
|
@GlobalConfig("lms-paster-protocol")
|
||||||
public boolean lmsPasterProtocol = false;
|
public boolean lmsPasterProtocol = false;
|
||||||
|
|
||||||
@GlobalConfig("rei-server-protocol")
|
@GlobalConfig(value = "rei-server-protocol", validator = ReiValidator.class)
|
||||||
public boolean reiServerProtocol = false;
|
public boolean reiServerProtocol = false;
|
||||||
|
|
||||||
|
public static class ReiValidator extends BooleanConfigValidator {
|
||||||
|
@Override
|
||||||
|
public void verify(Boolean old, Boolean value) throws IllegalArgumentException {
|
||||||
|
if (old != value && value != null) {
|
||||||
|
org.leavesmc.leaves.protocol.rei.REIServerProtocol.onConfigModify(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@GlobalConfig("chat-image-protocol")
|
@GlobalConfig("chat-image-protocol")
|
||||||
public boolean chatImageProtocol = false;
|
public boolean chatImageProtocol = false;
|
||||||
|
|
||||||
@GlobalConfig("recipe-send-all")
|
@RemovedConfig(name = "recipe-send-all", category = {"protocol"})
|
||||||
public boolean recipeSendAll = false;
|
public boolean recipeSendAll = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,138 @@
|
|||||||
|
package org.leavesmc.leaves.protocol.rei;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import org.leavesmc.leaves.LeavesLogger;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
|
public class PacketTransformer {
|
||||||
|
|
||||||
|
private static final byte START = 0x0;
|
||||||
|
private static final byte PART = 0x1;
|
||||||
|
private static final byte END = 0x2;
|
||||||
|
private static final byte ONLY = 0x3;
|
||||||
|
|
||||||
|
private final Map<UUID, PartData> cache = Collections.synchronizedMap(new HashMap<>());
|
||||||
|
|
||||||
|
private static class PartData {
|
||||||
|
private final ResourceLocation id;
|
||||||
|
private final int partsNum;
|
||||||
|
private final List<RegistryFriendlyByteBuf> parts;
|
||||||
|
|
||||||
|
public PartData(ResourceLocation id, int partsNum) {
|
||||||
|
this.id = id;
|
||||||
|
this.partsNum = partsNum;
|
||||||
|
this.parts = new ArrayList<>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void inbound(ResourceLocation id, RegistryFriendlyByteBuf buf, ServerPlayer player, BiConsumer<ResourceLocation, RegistryFriendlyByteBuf> consumer) {
|
||||||
|
UUID key = player.getUUID();
|
||||||
|
PartData data;
|
||||||
|
switch (buf.readByte()) {
|
||||||
|
case START -> {
|
||||||
|
int partsNum = buf.readInt();
|
||||||
|
data = new PartData(id, partsNum);
|
||||||
|
if (cache.put(key, data) != null) {
|
||||||
|
LeavesLogger.LOGGER.warning("Received invalid START packet for SplitPacketTransformer with packet id " + id);
|
||||||
|
}
|
||||||
|
buf.retain();
|
||||||
|
data.parts.add(buf);
|
||||||
|
}
|
||||||
|
case PART -> {
|
||||||
|
if ((data = cache.get(key)) == null) {
|
||||||
|
LeavesLogger.LOGGER.warning("Received invalid PART packet for SplitPacketTransformer with packet id " + id);
|
||||||
|
buf.release();
|
||||||
|
} else if (!data.id.equals(id)) {
|
||||||
|
LeavesLogger.LOGGER.warning("Received invalid PART packet for SplitPacketTransformer with packet id " + id + ", id in cache is {}" + data.id);
|
||||||
|
buf.release();
|
||||||
|
for (RegistryFriendlyByteBuf part : data.parts) {
|
||||||
|
if (part != buf) {
|
||||||
|
part.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cache.remove(key);
|
||||||
|
} else {
|
||||||
|
buf.retain();
|
||||||
|
data.parts.add(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case END -> {
|
||||||
|
if ((data = cache.get(key)) == null) {
|
||||||
|
LeavesLogger.LOGGER.warning("Received invalid END packet for SplitPacketTransformer with packet id {}" + id);
|
||||||
|
buf.release();
|
||||||
|
} else if (!data.id.equals(id)) {
|
||||||
|
LeavesLogger.LOGGER.warning("Received invalid END packet for SplitPacketTransformer with packet id " + id + ", id in cache is {}" + data.id);
|
||||||
|
buf.release();
|
||||||
|
for (RegistryFriendlyByteBuf part : data.parts) {
|
||||||
|
if (part != buf) {
|
||||||
|
part.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cache.remove(key);
|
||||||
|
} else {
|
||||||
|
buf.retain();
|
||||||
|
data.parts.add(buf);
|
||||||
|
}
|
||||||
|
if (data == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (data.parts.size() != data.partsNum) {
|
||||||
|
LeavesLogger.LOGGER.warning("Received invalid END packet for SplitPacketTransformer with packet id " + id + " with size " + data.parts + ", parts expected is {}" + data.partsNum);
|
||||||
|
for (RegistryFriendlyByteBuf part : data.parts) {
|
||||||
|
if (part != buf) {
|
||||||
|
part.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
RegistryFriendlyByteBuf byteBuf = new RegistryFriendlyByteBuf(Unpooled.wrappedBuffer(data.parts.toArray(new ByteBuf[0])), buf.registryAccess());
|
||||||
|
consumer.accept(data.id, byteBuf);
|
||||||
|
byteBuf.release();
|
||||||
|
}
|
||||||
|
cache.remove(key);
|
||||||
|
}
|
||||||
|
case ONLY -> consumer.accept(id, buf);
|
||||||
|
default -> throw new IllegalStateException("Illegal split packet header!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void outbound(ResourceLocation id, RegistryFriendlyByteBuf buf, BiConsumer<ResourceLocation, RegistryFriendlyByteBuf> consumer) {
|
||||||
|
int maxSize = 1048576 - 1 - 20 - id.toString().getBytes(StandardCharsets.UTF_8).length;
|
||||||
|
if (buf.readableBytes() <= maxSize) {
|
||||||
|
ByteBuf stateBuf = Unpooled.buffer(1);
|
||||||
|
stateBuf.writeByte(ONLY);
|
||||||
|
RegistryFriendlyByteBuf packetBuffer = new RegistryFriendlyByteBuf(Unpooled.wrappedBuffer(stateBuf, buf), buf.registryAccess());
|
||||||
|
consumer.accept(id, packetBuffer);
|
||||||
|
} else {
|
||||||
|
int partSize = maxSize - 4;
|
||||||
|
int parts = (int) Math.ceil(buf.readableBytes() / (float) partSize);
|
||||||
|
for (int i = 0; i < parts; i++) {
|
||||||
|
RegistryFriendlyByteBuf packetBuffer = new RegistryFriendlyByteBuf(Unpooled.buffer(), buf.registryAccess());
|
||||||
|
if (i == 0) {
|
||||||
|
packetBuffer.writeByte(START);
|
||||||
|
packetBuffer.writeInt(parts);
|
||||||
|
} else if (i == parts - 1) {
|
||||||
|
packetBuffer.writeByte(END);
|
||||||
|
} else {
|
||||||
|
packetBuffer.writeByte(PART);
|
||||||
|
}
|
||||||
|
int next = Math.min(buf.readableBytes(), partSize);
|
||||||
|
packetBuffer.writeBytes(buf.retainedSlice(buf.readerIndex(), next));
|
||||||
|
buf.skipBytes(next);
|
||||||
|
consumer.accept(id, packetBuffer);
|
||||||
|
}
|
||||||
|
buf.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,70 +1,262 @@
|
|||||||
package org.leavesmc.leaves.protocol.rei;
|
package org.leavesmc.leaves.protocol.rei;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import io.netty.buffer.ByteBufUtil;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
|
import net.minecraft.Util;
|
||||||
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.minecraft.util.Mth;
|
import net.minecraft.util.Mth;
|
||||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.SmithingTemplateItem;
|
||||||
|
import net.minecraft.world.item.crafting.FireworkRocketRecipe;
|
||||||
|
import net.minecraft.world.item.crafting.MapCloningRecipe;
|
||||||
|
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||||
|
import net.minecraft.world.item.crafting.RecipeMap;
|
||||||
|
import net.minecraft.world.item.crafting.RecipeType;
|
||||||
|
import net.minecraft.world.item.crafting.ShapedRecipe;
|
||||||
|
import net.minecraft.world.item.crafting.ShapelessRecipe;
|
||||||
|
import net.minecraft.world.item.crafting.SmithingTrimRecipe;
|
||||||
|
import net.minecraft.world.item.crafting.TippedArrowRecipe;
|
||||||
|
import net.minecraft.world.item.crafting.TransmuteRecipe;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.permissions.Permission;
|
||||||
|
import org.bukkit.permissions.PermissionDefault;
|
||||||
|
import org.bukkit.plugin.PluginManager;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.leavesmc.leaves.LeavesConfig;
|
import org.leavesmc.leaves.LeavesConfig;
|
||||||
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
|
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
|
||||||
import org.leavesmc.leaves.protocol.core.LeavesProtocolManager.EmptyPayload;
|
|
||||||
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
|
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
|
||||||
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
|
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
|
||||||
import org.leavesmc.leaves.protocol.rei.payload.CreateItemGrabPayload;
|
import org.leavesmc.leaves.protocol.rei.display.BlastingDisplay;
|
||||||
import org.leavesmc.leaves.protocol.rei.payload.CreateItemHotbarPayload;
|
import org.leavesmc.leaves.protocol.rei.display.CampfireDisplay;
|
||||||
import org.leavesmc.leaves.protocol.rei.payload.CreateItemMessagePayload;
|
import org.leavesmc.leaves.protocol.rei.display.Display;
|
||||||
import org.leavesmc.leaves.protocol.rei.payload.CreateItemPayload;
|
import org.leavesmc.leaves.protocol.rei.display.ShapedDisplay;
|
||||||
|
import org.leavesmc.leaves.protocol.rei.display.ShapelessDisplay;
|
||||||
|
import org.leavesmc.leaves.protocol.rei.display.SmeltingDisplay;
|
||||||
|
import org.leavesmc.leaves.protocol.rei.display.SmokingDisplay;
|
||||||
|
import org.leavesmc.leaves.protocol.rei.display.StoneCuttingDisplay;
|
||||||
|
import org.leavesmc.leaves.protocol.rei.payload.BufCustomPacketPayload;
|
||||||
|
import org.leavesmc.leaves.protocol.rei.payload.DisplaySyncPayload;
|
||||||
|
|
||||||
// @LeavesProtocol(namespace = "roughlyenoughitems") TODO will fix
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
|
@LeavesProtocol(namespace = REIServerProtocol.PROTOCOL_ID)
|
||||||
public class REIServerProtocol {
|
public class REIServerProtocol {
|
||||||
|
|
||||||
public static final String PROTOCOL_ID = "roughlyenoughitems";
|
public static final String PROTOCOL_ID = "roughlyenoughitems";
|
||||||
|
public static final String CHEAT_PERMISSION = "leaves.protocol.rei.cheat";
|
||||||
|
public static final ResourceLocation DELETE_ITEMS_PACKET = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "delete_item");
|
||||||
|
public static final ResourceLocation CREATE_ITEMS_PACKET = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "create_item");
|
||||||
|
public static final ResourceLocation CREATE_ITEMS_GRAB_PACKET = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "create_item_grab");
|
||||||
|
public static final ResourceLocation CREATE_ITEMS_HOTBAR_PACKET = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "create_item_hotbar");
|
||||||
|
public static final ResourceLocation CREATE_ITEMS_MESSAGE_PACKET = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "ci_msg");
|
||||||
|
public static final ResourceLocation SYNC_DISPLAYS_PACKET = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "sync_displays");
|
||||||
|
|
||||||
|
public static final Map<ResourceLocation, PacketTransformer> TRANSFORMERS = Util.make(() -> {
|
||||||
|
ImmutableMap.Builder<ResourceLocation, PacketTransformer> builder = ImmutableMap.builder();
|
||||||
|
builder.put(SYNC_DISPLAYS_PACKET, new PacketTransformer());
|
||||||
|
builder.put(DELETE_ITEMS_PACKET, new PacketTransformer());
|
||||||
|
builder.put(CREATE_ITEMS_PACKET, new PacketTransformer());
|
||||||
|
builder.put(CREATE_ITEMS_GRAB_PACKET, new PacketTransformer());
|
||||||
|
builder.put(CREATE_ITEMS_HOTBAR_PACKET, new PacketTransformer());
|
||||||
|
return builder.build();
|
||||||
|
});
|
||||||
|
private static final Set<ServerPlayer> enabledPlayers = new HashSet<>();
|
||||||
|
private static int minecraftRecipeVer = 0;
|
||||||
|
private static int nextReiRecipeVer = -1;
|
||||||
|
private static ImmutableList<CustomPacketPayload> cachedPayloads;
|
||||||
|
private static final Executor executor = new ThreadPoolExecutor(
|
||||||
|
1, 1, 0L, TimeUnit.MILLISECONDS,
|
||||||
|
new ArrayBlockingQueue<>(1),
|
||||||
|
new ThreadPoolExecutor.DiscardOldestPolicy()
|
||||||
|
);
|
||||||
|
|
||||||
|
public static void onRecipeReload() {
|
||||||
|
minecraftRecipeVer = MinecraftServer.getServer().getTickCount();
|
||||||
|
}
|
||||||
|
|
||||||
@Contract("_ -> new")
|
@Contract("_ -> new")
|
||||||
public static ResourceLocation id(String path) {
|
public static ResourceLocation id(String path) {
|
||||||
return ResourceLocation.tryBuild(PROTOCOL_ID, path);
|
return ResourceLocation.tryBuild(PROTOCOL_ID, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ProtocolHandler.PayloadReceiver(payload = EmptyPayload.class, payloadId = "delete_item")
|
public static void onConfigModify(boolean enabled) {
|
||||||
public static void handleDeleteItem(ServerPlayer player, EmptyPayload payload) {
|
PluginManager pluginManager = Bukkit.getServer().getPluginManager();
|
||||||
if (!check(player, true)) {
|
if (enabled) {
|
||||||
return;
|
if (pluginManager.getPermission(CHEAT_PERMISSION) == null) {
|
||||||
|
pluginManager.addPermission(new Permission(CHEAT_PERMISSION, PermissionDefault.OP));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pluginManager.removePermission(CHEAT_PERMISSION);
|
||||||
|
enabledPlayers.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ProtocolHandler.PlayerLeave
|
||||||
|
public static void onPlayerLoggedOut(@NotNull ServerPlayer player) {
|
||||||
|
if (LeavesConfig.protocol.reiServerProtocol) {
|
||||||
|
enabledPlayers.remove(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ProtocolHandler.Ticker
|
||||||
|
public static void tick() {
|
||||||
|
if (!LeavesConfig.protocol.reiServerProtocol) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (MinecraftServer.getServer().getTickCount() % 200 == 1 && minecraftRecipeVer != nextReiRecipeVer) {
|
||||||
|
nextReiRecipeVer = minecraftRecipeVer;
|
||||||
|
executor.execute(() -> reloadRecipe(nextReiRecipeVer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
|
private static void reloadRecipe(int reiRecipeVer) {
|
||||||
|
ImmutableList.Builder<Display> builder = ImmutableList.builder();
|
||||||
|
MinecraftServer server = MinecraftServer.getServer();
|
||||||
|
RecipeMap recipeMap = server.getRecipeManager().recipes;
|
||||||
|
recipeMap.byType(RecipeType.CRAFTING).forEach(holder -> {
|
||||||
|
switch (holder.value()) {
|
||||||
|
case ShapedRecipe ignored -> builder.add(new ShapedDisplay((RecipeHolder) holder));
|
||||||
|
case ShapelessRecipe ignored -> builder.add(new ShapelessDisplay((RecipeHolder) holder));
|
||||||
|
case TransmuteRecipe ignored -> builder.addAll(Display.ofTransmuteRecipe((RecipeHolder) holder));
|
||||||
|
case TippedArrowRecipe ignored -> builder.addAll(Display.ofTippedArrowRecipe((RecipeHolder) holder));
|
||||||
|
case FireworkRocketRecipe ignored -> builder.addAll(Display.ofFireworkRocketRecipe((RecipeHolder) holder));
|
||||||
|
case MapCloningRecipe ignored -> builder.addAll(Display.ofMapCloningRecipe((RecipeHolder) holder));
|
||||||
|
// ignore ArmorDyeRecipe, BannerDuplicateRecipe, BookCloningRecipe, ShieldDecorationRecipe
|
||||||
|
default -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
recipeMap.byType(RecipeType.STONECUTTING).forEach(holder -> builder.add(new StoneCuttingDisplay(holder)));
|
||||||
|
recipeMap.byType(RecipeType.SMELTING).forEach(holder -> builder.add(new SmeltingDisplay(holder)));
|
||||||
|
recipeMap.byType(RecipeType.BLASTING).forEach(holder -> builder.add(new BlastingDisplay(holder)));
|
||||||
|
recipeMap.byType(RecipeType.SMOKING).forEach(holder -> builder.add(new SmokingDisplay(holder)));
|
||||||
|
recipeMap.byType(RecipeType.CAMPFIRE_COOKING).forEach(holder -> builder.add(new CampfireDisplay(holder)));
|
||||||
|
recipeMap.byType(RecipeType.SMITHING).forEach(holder -> {
|
||||||
|
switch (holder.value()) {
|
||||||
|
case SmithingTrimRecipe ignored -> builder.addAll(Display.ofSmithingTrimRecipe((RecipeHolder) holder));
|
||||||
|
case SmithingTemplateItem ignored -> builder.add(Display.ofTransforming((RecipeHolder) holder));
|
||||||
|
default -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
DisplaySyncPayload displaySyncPayload = new DisplaySyncPayload(
|
||||||
|
DisplaySyncPayload.SyncType.SET,
|
||||||
|
builder.build(),
|
||||||
|
reiRecipeVer
|
||||||
|
);
|
||||||
|
|
||||||
|
RegistryFriendlyByteBuf s2cBuf = ProtocolUtils.decorate(Unpooled.buffer());
|
||||||
|
DisplaySyncPayload.STREAM_CODEC.encode(s2cBuf, displaySyncPayload);
|
||||||
|
ImmutableList.Builder<CustomPacketPayload> listBuilder = ImmutableList.builder();
|
||||||
|
outboundTransform(SYNC_DISPLAYS_PACKET, s2cBuf, (id, splitBuf) ->
|
||||||
|
listBuilder.add(new BufCustomPacketPayload(new CustomPacketPayload.Type<>(id), ByteBufUtil.getBytes(splitBuf)))
|
||||||
|
);
|
||||||
|
|
||||||
|
cachedPayloads = listBuilder.build();
|
||||||
|
MinecraftServer.getServer().execute(() -> {
|
||||||
|
for (ServerPlayer player : enabledPlayers) {
|
||||||
|
for (CustomPacketPayload payload : cachedPayloads) {
|
||||||
|
ProtocolUtils.sendPayloadPacket(player, payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@ProtocolHandler.MinecraftRegister(ignoreId = true)
|
||||||
|
public static void onPlayerSubscribed(@NotNull ServerPlayer player, String channel) {
|
||||||
|
if (!LeavesConfig.protocol.reiServerProtocol) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
enabledPlayers.add(player);
|
||||||
|
if (channel.equals("sync_displays")) {
|
||||||
|
if (cachedPayloads != null) {
|
||||||
|
cachedPayloads.forEach(payload -> ProtocolUtils.sendPayloadPacket(player, payload));
|
||||||
|
}
|
||||||
|
} else if (channel.equals("ci_msg")) {
|
||||||
|
// cheat rei-client into using "delete_item" packet
|
||||||
|
if (player.getServer().getProfilePermissions(player.getGameProfile()) < 1) {
|
||||||
|
player.getBukkitEntity().sendOpLevel((byte) 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ProtocolHandler.PayloadReceiver(payload = BufCustomPacketPayload.class, payloadId = "delete_item")
|
||||||
|
public static void handleDeleteItem(ServerPlayer player, BufCustomPacketPayload payload) {
|
||||||
|
if (!LeavesConfig.protocol.reiServerProtocol || !hasCheatPermission(player)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RegistryFriendlyByteBuf c2sBuf = ProtocolUtils.decorate(Unpooled.buffer());
|
||||||
|
c2sBuf.writeBytes(payload.payload());
|
||||||
|
|
||||||
|
inboundTransform(player, payload.id(), c2sBuf, (id, wholeBuf) -> {
|
||||||
AbstractContainerMenu menu = player.containerMenu;
|
AbstractContainerMenu menu = player.containerMenu;
|
||||||
if (!menu.getCarried().isEmpty()) {
|
if (!menu.getCarried().isEmpty()) {
|
||||||
menu.setCarried(ItemStack.EMPTY);
|
menu.setCarried(ItemStack.EMPTY);
|
||||||
menu.broadcastChanges();
|
menu.broadcastChanges();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ProtocolHandler.PayloadReceiver(payload = CreateItemPayload.class, payloadId = "create_item")
|
@ProtocolHandler.PayloadReceiver(payload = BufCustomPacketPayload.class, payloadId = "create_item")
|
||||||
public static void handleCreateItem(ServerPlayer player, CreateItemPayload payload) {
|
public static void handleCreateItem(ServerPlayer player, BufCustomPacketPayload payload) {
|
||||||
if (!check(player, true)) {
|
if (!LeavesConfig.protocol.reiServerProtocol || !hasCheatPermission(player)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
RegistryFriendlyByteBuf c2sBuf = ProtocolUtils.decorate(Unpooled.buffer());
|
||||||
ItemStack stack = payload.item();
|
c2sBuf.writeBytes(payload.payload());
|
||||||
if (player.getInventory().add(stack.copy())) {
|
BiConsumer<ResourceLocation, RegistryFriendlyByteBuf> consumer = (ignored, c2sWholeBuf) -> {
|
||||||
ProtocolUtils.sendPayloadPacket(player, new CreateItemMessagePayload(stack.copy(), player.getScoreboardName()));
|
FriendlyByteBuf tmpBuf = new FriendlyByteBuf(Unpooled.buffer()).writeBytes(c2sWholeBuf.readByteArray());
|
||||||
|
ItemStack itemStack = tmpBuf.readJsonWithCodec(ItemStack.OPTIONAL_CODEC);
|
||||||
|
if (player.getInventory().add(itemStack.copy())) {
|
||||||
|
RegistryFriendlyByteBuf s2cWholeBuf = ProtocolUtils.decorate(Unpooled.buffer());
|
||||||
|
s2cWholeBuf.writeJsonWithCodec(ItemStack.OPTIONAL_CODEC, itemStack.copy());
|
||||||
|
s2cWholeBuf.writeUtf(player.getScoreboardName(), 32767);
|
||||||
|
// Due to the bug in REI, no packets are actually sent here.
|
||||||
|
/*
|
||||||
|
outboundTransform(CREATE_ITEMS_MESSAGE_PACKET, s2cWholeBuf, (id, s2cSplitBuf) -> {
|
||||||
|
ProtocolUtils.sendPayloadPacket(player, new BufCustomPacketPayload(new CustomPacketPayload.Type<>(id), ByteBufUtil.getBytes(s2cSplitBuf)));
|
||||||
|
});
|
||||||
|
*/
|
||||||
} else {
|
} else {
|
||||||
player.displayClientMessage(Component.translatable("text.rei.failed_cheat_items"), false);
|
player.displayClientMessage(Component.translatable("text.rei.failed_cheat_items"), false);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
inboundTransform(player, payload.id(), c2sBuf, consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ProtocolHandler.PayloadReceiver(payload = CreateItemGrabPayload.class, payloadId = "create_item_grab")
|
@ProtocolHandler.PayloadReceiver(payload = BufCustomPacketPayload.class, payloadId = "create_item_grab")
|
||||||
public static void handleCreateItemGrab(ServerPlayer player, CreateItemGrabPayload payload) {
|
public static void handleCreateItemGrab(ServerPlayer player, BufCustomPacketPayload payload) {
|
||||||
if (!check(player, true)) {
|
if (!LeavesConfig.protocol.reiServerProtocol || !hasCheatPermission(player)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
RegistryFriendlyByteBuf c2sBuf = ProtocolUtils.decorate(Unpooled.buffer());
|
||||||
|
c2sBuf.writeBytes(payload.payload());
|
||||||
|
|
||||||
AbstractContainerMenu menu = player.containerMenu;
|
BiConsumer<ResourceLocation, RegistryFriendlyByteBuf> consumer = (ignored, c2sWholeBuf) -> {
|
||||||
ItemStack itemStack = payload.item();
|
FriendlyByteBuf tmpBuf = new FriendlyByteBuf(Unpooled.buffer()).writeBytes(c2sWholeBuf.readByteArray());
|
||||||
|
ItemStack itemStack = tmpBuf.readJsonWithCodec(ItemStack.OPTIONAL_CODEC);
|
||||||
ItemStack stack = itemStack.copy();
|
ItemStack stack = itemStack.copy();
|
||||||
|
AbstractContainerMenu menu = player.containerMenu;
|
||||||
if (!menu.getCarried().isEmpty() && ItemStack.isSameItemSameComponents(menu.getCarried(), stack)) {
|
if (!menu.getCarried().isEmpty() && ItemStack.isSameItemSameComponents(menu.getCarried(), stack)) {
|
||||||
stack.setCount(Mth.clamp(stack.getCount() + menu.getCarried().getCount(), 1, stack.getMaxStackSize()));
|
stack.setCount(Mth.clamp(stack.getCount() + menu.getCarried().getCount(), 1, stack.getMaxStackSize()));
|
||||||
} else if (!menu.getCarried().isEmpty()) {
|
} else if (!menu.getCarried().isEmpty()) {
|
||||||
@@ -72,36 +264,78 @@ public class REIServerProtocol {
|
|||||||
}
|
}
|
||||||
menu.setCarried(stack.copy());
|
menu.setCarried(stack.copy());
|
||||||
menu.broadcastChanges();
|
menu.broadcastChanges();
|
||||||
ProtocolUtils.sendPayloadPacket(player, new CreateItemMessagePayload(stack, player.getScoreboardName()));
|
RegistryFriendlyByteBuf s2cWholeBuf = ProtocolUtils.decorate(Unpooled.buffer());
|
||||||
|
s2cWholeBuf.writeJsonWithCodec(ItemStack.OPTIONAL_CODEC, stack.copy());
|
||||||
|
s2cWholeBuf.writeUtf(player.getScoreboardName(), 32767);
|
||||||
|
// Due to the bug in REI, no packets are actually sent here.
|
||||||
|
/*
|
||||||
|
outboundTransform(CREATE_ITEMS_MESSAGE_PACKET, s2cWholeBuf, (id, s2cSplitBuf) -> {
|
||||||
|
ProtocolUtils.sendPayloadPacket(player, new BufCustomPacketPayload(new CustomPacketPayload.Type<>(id), ByteBufUtil.getBytes(s2cSplitBuf)));
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
};
|
||||||
|
inboundTransform(player, payload.id(), c2sBuf, consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ProtocolHandler.PayloadReceiver(payload = CreateItemHotbarPayload.class, payloadId = "create_item_hotbar")
|
@ProtocolHandler.PayloadReceiver(payload = BufCustomPacketPayload.class, payloadId = "create_item_hotbar")
|
||||||
public static void handleCreateItemHotbar(ServerPlayer player, CreateItemHotbarPayload payload) {
|
public static void handleCreateItemHotbar(ServerPlayer player, BufCustomPacketPayload payload) {
|
||||||
if (!check(player, true)) {
|
if (!LeavesConfig.protocol.reiServerProtocol || !hasCheatPermission(player)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
RegistryFriendlyByteBuf c2sBuf = ProtocolUtils.decorate(Unpooled.buffer());
|
||||||
ItemStack stack = payload.item();
|
c2sBuf.writeBytes(payload.payload());
|
||||||
int hotbarSlotId = payload.hotbarSlot();
|
BiConsumer<ResourceLocation, RegistryFriendlyByteBuf> consumer = (ignored, c2sWholeBuf) -> {
|
||||||
|
FriendlyByteBuf tmpBuf = new FriendlyByteBuf(Unpooled.buffer()).writeBytes(c2sWholeBuf.readByteArray());
|
||||||
|
ItemStack stack = tmpBuf.readJsonWithCodec(ItemStack.OPTIONAL_CODEC);
|
||||||
|
int hotbarSlotId = tmpBuf.readVarInt();
|
||||||
if (hotbarSlotId >= 0 && hotbarSlotId < 9) {
|
if (hotbarSlotId >= 0 && hotbarSlotId < 9) {
|
||||||
AbstractContainerMenu menu = player.containerMenu;
|
AbstractContainerMenu menu = player.containerMenu;
|
||||||
player.getInventory().items.set(hotbarSlotId, stack.copy());
|
player.getInventory().items.set(hotbarSlotId, stack.copy());
|
||||||
menu.broadcastChanges();
|
menu.broadcastChanges();
|
||||||
ProtocolUtils.sendPayloadPacket(player, new CreateItemMessagePayload(stack, player.getScoreboardName()));
|
RegistryFriendlyByteBuf s2cWholeBuf = ProtocolUtils.decorate(Unpooled.buffer());
|
||||||
|
s2cWholeBuf.writeJsonWithCodec(ItemStack.OPTIONAL_CODEC, stack.copy());
|
||||||
|
s2cWholeBuf.writeUtf(player.getScoreboardName(), 32767);
|
||||||
|
// Due to the bug in REI, no packets are actually sent here.
|
||||||
|
/*
|
||||||
|
outboundTransform(CREATE_ITEMS_MESSAGE_PACKET, s2cWholeBuf, (id, s2cSplitBuf) -> {
|
||||||
|
ProtocolUtils.sendPayloadPacket(player, new BufCustomPacketPayload(new CustomPacketPayload.Type<>(id), ByteBufUtil.getBytes(s2cSplitBuf)));
|
||||||
|
});
|
||||||
|
*/
|
||||||
} else {
|
} else {
|
||||||
player.displayClientMessage(Component.translatable("text.rei.failed_cheat_items"), false);
|
player.displayClientMessage(Component.translatable("text.rei.failed_cheat_items"), false);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
inboundTransform(player, payload.id(), c2sBuf, consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean check(ServerPlayer player, boolean needOP) {
|
private static void inboundTransform(ServerPlayer player,
|
||||||
if (!LeavesConfig.protocol.reiServerProtocol) {
|
ResourceLocation id,
|
||||||
return false;
|
RegistryFriendlyByteBuf buf,
|
||||||
|
BiConsumer<ResourceLocation, RegistryFriendlyByteBuf> consumer) {
|
||||||
|
PacketTransformer transformer = TRANSFORMERS.get(id);
|
||||||
|
if (transformer != null) {
|
||||||
|
transformer.inbound(id, buf, player, consumer);
|
||||||
|
} else {
|
||||||
|
consumer.accept(id, buf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needOP && MinecraftServer.getServer().getPlayerList().isOp(player.gameProfile)) { // TODO check permission node
|
private static void outboundTransform(ResourceLocation id,
|
||||||
|
RegistryFriendlyByteBuf buf,
|
||||||
|
BiConsumer<ResourceLocation, RegistryFriendlyByteBuf> consumer) {
|
||||||
|
PacketTransformer transformer = TRANSFORMERS.get(id);
|
||||||
|
if (transformer != null) {
|
||||||
|
transformer.outbound(id, buf, consumer);
|
||||||
|
} else {
|
||||||
|
consumer.accept(id, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean hasCheatPermission(ServerPlayer player) {
|
||||||
|
if (player.getBukkitEntity().hasPermission(CHEAT_PERMISSION)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
player.displayClientMessage(Component.translatable("text.rei.no_permission_cheat").withStyle(ChatFormatting.RED), false);
|
player.displayClientMessage(Component.translatable("text.rei.no_permission_cheat").withStyle(ChatFormatting.RED), false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package org.leavesmc.leaves.protocol.rei.display;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.item.crafting.AbstractCookingRecipe;
|
||||||
|
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||||
|
|
||||||
|
public class BlastingDisplay extends CookingDisplay {
|
||||||
|
public BlastingDisplay(RecipeHolder<? extends AbstractCookingRecipe> recipe) {
|
||||||
|
super(recipe);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final ResourceLocation SERIALIZER_ID = ResourceLocation.tryBuild("minecraft", "default/blasting");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getSerializerId() {
|
||||||
|
return SERIALIZER_ID;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package org.leavesmc.leaves.protocol.rei.display;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.item.crafting.CampfireCookingRecipe;
|
||||||
|
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||||
|
|
||||||
|
public class CampfireDisplay extends CookingDisplay {
|
||||||
|
public CampfireDisplay(RecipeHolder<CampfireCookingRecipe> recipe) {
|
||||||
|
super(recipe);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final ResourceLocation SERIALIZER_ID = ResourceLocation.tryBuild("minecraft", "default/campfire");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getSerializerId() {
|
||||||
|
return SERIALIZER_ID;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package org.leavesmc.leaves.protocol.rei.display;
|
||||||
|
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.crafting.AbstractCookingRecipe;
|
||||||
|
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||||
|
import net.minecraft.world.item.crafting.SingleRecipeInput;
|
||||||
|
import org.bukkit.craftbukkit.CraftRegistry;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.leavesmc.leaves.protocol.rei.ingredient.EntryIngredient;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public abstract class CookingDisplay extends Display {
|
||||||
|
protected float xp;
|
||||||
|
protected double cookTime;
|
||||||
|
|
||||||
|
private static final StreamCodec<RegistryFriendlyByteBuf, CookingDisplay> CODEC = StreamCodec.composite(
|
||||||
|
EntryIngredient.CODEC.apply(ByteBufCodecs.list()),
|
||||||
|
CookingDisplay::getInputEntries,
|
||||||
|
EntryIngredient.CODEC.apply(ByteBufCodecs.list()),
|
||||||
|
CookingDisplay::getOutputEntries,
|
||||||
|
ByteBufCodecs.optional(ResourceLocation.STREAM_CODEC),
|
||||||
|
CookingDisplay::getOptionalLocation,
|
||||||
|
ByteBufCodecs.FLOAT,
|
||||||
|
CookingDisplay::getXp,
|
||||||
|
ByteBufCodecs.DOUBLE,
|
||||||
|
CookingDisplay::getCookTime,
|
||||||
|
CookingDisplay::of
|
||||||
|
);
|
||||||
|
|
||||||
|
private CookingDisplay(@NotNull List<EntryIngredient> inputs, @NotNull List<EntryIngredient> outputs, @NotNull ResourceLocation id, float xp, double cookTime) {
|
||||||
|
super(inputs, outputs, id);
|
||||||
|
this.xp = xp;
|
||||||
|
this.cookTime = cookTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CookingDisplay(RecipeHolder<? extends AbstractCookingRecipe> recipe) {
|
||||||
|
this(
|
||||||
|
List.of(EntryIngredient.ofIngredient(recipe.value().input())),
|
||||||
|
List.of(EntryIngredient.of(recipe.value().assemble(new SingleRecipeInput(ItemStack.EMPTY), CraftRegistry.getMinecraftRegistry()))),
|
||||||
|
recipe.id().location(),
|
||||||
|
recipe.value().experience(),
|
||||||
|
recipe.value().cookingTime()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getXp() {
|
||||||
|
return xp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getCookTime() {
|
||||||
|
return cookTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StreamCodec<RegistryFriendlyByteBuf, CookingDisplay> streamCodec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||||
|
private static CookingDisplay of(@NotNull List<EntryIngredient> inputs, @NotNull List<EntryIngredient> outputs, @NotNull Optional<ResourceLocation> id, float xp, double cookTime) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package org.leavesmc.leaves.protocol.rei.display;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.leavesmc.leaves.protocol.rei.ingredient.EntryIngredient;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public abstract class CraftingDisplay extends Display {
|
||||||
|
|
||||||
|
public CraftingDisplay(@NotNull List<EntryIngredient> inputs,
|
||||||
|
@NotNull List<EntryIngredient> outputs,
|
||||||
|
@NotNull ResourceLocation location) {
|
||||||
|
super(inputs, outputs, location);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract int getWidth();
|
||||||
|
|
||||||
|
public abstract int getHeight();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package org.leavesmc.leaves.protocol.rei.display;
|
||||||
|
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.leavesmc.leaves.protocol.rei.ingredient.EntryIngredient;
|
||||||
|
|
||||||
|
import java.util.BitSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class CustomDisplay extends CraftingDisplay {
|
||||||
|
private final int width;
|
||||||
|
private final int height;
|
||||||
|
|
||||||
|
private static final StreamCodec<RegistryFriendlyByteBuf, CustomDisplay> CODEC = StreamCodec.composite(
|
||||||
|
EntryIngredient.CODEC.apply(ByteBufCodecs.list()),
|
||||||
|
CustomDisplay::getInputEntries,
|
||||||
|
EntryIngredient.CODEC.apply(ByteBufCodecs.list()),
|
||||||
|
CustomDisplay::getOutputEntries,
|
||||||
|
ByteBufCodecs.optional(ResourceLocation.STREAM_CODEC),
|
||||||
|
CustomDisplay::getOptionalLocation,
|
||||||
|
CustomDisplay::of
|
||||||
|
);
|
||||||
|
|
||||||
|
private static final ResourceLocation SERIALIZER_ID = ResourceLocation.tryBuild("minecraft", "default/crafting/custom");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* see me.shedaniel.rei.plugin.common.displays.crafting.DefaultCustomDisplay#DefaultCustomDisplay
|
||||||
|
*/
|
||||||
|
public CustomDisplay(@NotNull List<EntryIngredient> inputs, @NotNull List<EntryIngredient> outputs, @NotNull ResourceLocation location) {
|
||||||
|
super(inputs, outputs, location);
|
||||||
|
BitSet row = new BitSet(3);
|
||||||
|
BitSet column = new BitSet(3);
|
||||||
|
for (int i = 0; i < 9; i++)
|
||||||
|
if (i < inputs.size()) {
|
||||||
|
EntryIngredient stacks = inputs.get(i);
|
||||||
|
if (stacks.stream().anyMatch(stack -> !stack.isEmpty())) {
|
||||||
|
row.set((i - (i % 3)) / 3);
|
||||||
|
column.set(i % 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.width = column.cardinality();
|
||||||
|
this.height = row.cardinality();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getWidth() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getHeight() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getSerializerId() {
|
||||||
|
return SERIALIZER_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StreamCodec<RegistryFriendlyByteBuf, CustomDisplay> streamCodec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||||
|
private static CustomDisplay of(@NotNull List<EntryIngredient> inputs, @NotNull List<EntryIngredient> outputs, @NotNull Optional<ResourceLocation> id) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,327 @@
|
|||||||
|
package org.leavesmc.leaves.protocol.rei.display;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import net.minecraft.core.Holder;
|
||||||
|
import net.minecraft.core.HolderGetter;
|
||||||
|
import net.minecraft.core.HolderLookup;
|
||||||
|
import net.minecraft.core.HolderSet;
|
||||||
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.core.RegistryAccess;
|
||||||
|
import net.minecraft.core.component.DataComponents;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.tags.TagKey;
|
||||||
|
import net.minecraft.util.context.ContextMap;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.Items;
|
||||||
|
import net.minecraft.world.item.alchemy.PotionContents;
|
||||||
|
import net.minecraft.world.item.component.Fireworks;
|
||||||
|
import net.minecraft.world.item.crafting.FireworkRocketRecipe;
|
||||||
|
import net.minecraft.world.item.crafting.Ingredient;
|
||||||
|
import net.minecraft.world.item.crafting.MapCloningRecipe;
|
||||||
|
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||||
|
import net.minecraft.world.item.crafting.SmithingTransformRecipe;
|
||||||
|
import net.minecraft.world.item.crafting.SmithingTrimRecipe;
|
||||||
|
import net.minecraft.world.item.crafting.TippedArrowRecipe;
|
||||||
|
import net.minecraft.world.item.crafting.TransmuteRecipe;
|
||||||
|
import net.minecraft.world.item.crafting.display.RecipeDisplay;
|
||||||
|
import net.minecraft.world.item.crafting.display.ShapedCraftingRecipeDisplay;
|
||||||
|
import net.minecraft.world.item.crafting.display.ShapelessCraftingRecipeDisplay;
|
||||||
|
import net.minecraft.world.item.crafting.display.SlotDisplay;
|
||||||
|
import net.minecraft.world.item.crafting.display.SlotDisplayContext;
|
||||||
|
import net.minecraft.world.item.equipment.trim.ArmorTrim;
|
||||||
|
import net.minecraft.world.item.equipment.trim.TrimMaterial;
|
||||||
|
import net.minecraft.world.item.equipment.trim.TrimMaterials;
|
||||||
|
import net.minecraft.world.item.equipment.trim.TrimPattern;
|
||||||
|
import net.minecraft.world.item.equipment.trim.TrimPatterns;
|
||||||
|
import net.minecraft.world.level.ItemLike;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.leavesmc.leaves.protocol.rei.ingredient.EntryIngredient;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A display to be used alongside Roughly Enough Items.
|
||||||
|
* <p>
|
||||||
|
* see me.shedaniel.rei.api.common.display.Display
|
||||||
|
*/
|
||||||
|
public abstract class Display {
|
||||||
|
|
||||||
|
protected ResourceLocation id;
|
||||||
|
|
||||||
|
protected List<EntryIngredient> inputs;
|
||||||
|
|
||||||
|
protected List<EntryIngredient> outputs;
|
||||||
|
|
||||||
|
public Display(@NotNull List<EntryIngredient> inputs,
|
||||||
|
@NotNull List<EntryIngredient> outputs,
|
||||||
|
@NotNull ResourceLocation id) {
|
||||||
|
this.inputs = inputs;
|
||||||
|
this.outputs = outputs;
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<EntryIngredient> getInputEntries() {
|
||||||
|
return inputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<EntryIngredient> getOutputEntries() {
|
||||||
|
return outputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResourceLocation getDisplayLocation() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<ResourceLocation> getOptionalLocation() {
|
||||||
|
return Optional.ofNullable(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static StreamCodec<RegistryFriendlyByteBuf, Display> dispatchCodec() {
|
||||||
|
return new StreamCodec<>() {
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Display decode(@NotNull RegistryFriendlyByteBuf buffer) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(@NotNull RegistryFriendlyByteBuf buffer, @NotNull Display display) {
|
||||||
|
new FriendlyByteBuf(buffer).writeResourceLocation(display.getSerializerId());
|
||||||
|
((StreamCodec<RegistryFriendlyByteBuf, Display>) display.streamCodec()).encode(buffer, display);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract ResourceLocation getSerializerId();
|
||||||
|
|
||||||
|
public abstract StreamCodec<RegistryFriendlyByteBuf, ? extends Display> streamCodec();
|
||||||
|
|
||||||
|
public static Collection<Display> ofTransmuteRecipe(@NotNull RecipeHolder<TransmuteRecipe> recipeHolder) {
|
||||||
|
TransmuteRecipe recipe = recipeHolder.value();
|
||||||
|
List<RecipeDisplay> displays = recipe.display();
|
||||||
|
List<Display> displayList = new ArrayList<>();
|
||||||
|
if (!displays.isEmpty()) {
|
||||||
|
RecipeDisplay recipeDisplay = displays.getFirst();
|
||||||
|
if (recipeDisplay instanceof ShapelessCraftingRecipeDisplay shapelessRecipeDisplay) {
|
||||||
|
displayList.add(new ShapelessDisplay(shapelessRecipeDisplay, recipeHolder.id().location()));
|
||||||
|
} else if (recipeDisplay instanceof ShapedCraftingRecipeDisplay shapelessRecipe) {
|
||||||
|
displayList.add(new ShapedDisplay(shapelessRecipe, recipeHolder.id().location()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return displayList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* see me.shedaniel.rei.plugin.client.categories.crafting.filler.TippedArrowRecipeFiller#apply
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public static Collection<Display> ofTippedArrowRecipe(@NotNull RecipeHolder<TippedArrowRecipe> recipeHolder) {
|
||||||
|
EntryIngredient arrowIngredient = EntryIngredient.of(Items.ARROW);
|
||||||
|
Set<ResourceLocation> registeredPotions = new HashSet<>();
|
||||||
|
List<Display> displays = new ArrayList<>();
|
||||||
|
MinecraftServer.getServer().registryAccess().lookup(Registries.POTION).stream()
|
||||||
|
.flatMap(Registry::listElements)
|
||||||
|
.map(reference -> PotionContents.createItemStack(Items.LINGERING_POTION, reference))
|
||||||
|
.forEach(itemStack -> {
|
||||||
|
PotionContents potion = itemStack.get(DataComponents.POTION_CONTENTS);
|
||||||
|
if (potion == null || potion.potion().isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (potion.potion().get().unwrapKey().isPresent() && registeredPotions.add(potion.potion().get().unwrapKey().get().location())) {
|
||||||
|
List<EntryIngredient> input = new ArrayList<>();
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
input.add(arrowIngredient);
|
||||||
|
input.add(EntryIngredient.of(itemStack));
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
input.add(arrowIngredient);
|
||||||
|
ItemStack outputStack = new ItemStack(Items.TIPPED_ARROW, 8);
|
||||||
|
outputStack.set(DataComponents.POTION_CONTENTS, potion);
|
||||||
|
displays.add(new CustomDisplay(input, List.of(EntryIngredient.of(outputStack)), recipeHolder.id().location()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return displays;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* see me.shedaniel.rei.plugin.client.categories.crafting.filler.TippedArrowRecipeFiller#apply
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public static Collection<Display> ofFireworkRocketRecipe(@NotNull RecipeHolder<FireworkRocketRecipe> recipeHolder) {
|
||||||
|
EntryIngredient[] inputs = new EntryIngredient[4];
|
||||||
|
inputs[0] = EntryIngredient.of(Items.GUNPOWDER);
|
||||||
|
inputs[1] = EntryIngredient.of(Items.PAPER);
|
||||||
|
inputs[2] = EntryIngredient.of(new ItemStack(Items.AIR), new ItemStack(Items.GUNPOWDER), new ItemStack(Items.GUNPOWDER));
|
||||||
|
inputs[3] = EntryIngredient.of(new ItemStack(Items.AIR), new ItemStack(Items.AIR), new ItemStack(Items.GUNPOWDER));
|
||||||
|
ItemStack[] outputs = new ItemStack[3];
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
outputs[i] = new ItemStack(Items.FIREWORK_ROCKET, 3);
|
||||||
|
outputs[i].set(DataComponents.FIREWORKS, new Fireworks(i + 1, List.of()));
|
||||||
|
}
|
||||||
|
return Collections.singleton(new ShapelessDisplay(List.of(inputs), List.of(EntryIngredient.of(outputs)), recipeHolder.id().location()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* see me.shedaniel.rei.plugin.client.categories.crafting.filler.MapCloningRecipeFiller#apply
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public static Collection<Display> ofMapCloningRecipe(@NotNull RecipeHolder<MapCloningRecipe> recipeHolder) {
|
||||||
|
return Collections.singleton(
|
||||||
|
new ShapelessDisplay(
|
||||||
|
List.of(EntryIngredient.of(Items.FILLED_MAP), EntryIngredient.of(Items.MAP)),
|
||||||
|
List.of(EntryIngredient.of(new ItemStack(Items.FILLED_MAP, 2))),
|
||||||
|
recipeHolder.id().location())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* see me.shedaniel.rei.plugin.common.displays.DefaultSmithingDisplay#ofTransforming
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public static SmithingDisplay ofTransforming(RecipeHolder<SmithingTransformRecipe> recipeHolder) {
|
||||||
|
|
||||||
|
|
||||||
|
return new SmithingDisplay(
|
||||||
|
List.of(
|
||||||
|
recipeHolder.value().templateIngredient().map(EntryIngredient::ofIngredient).orElse(EntryIngredient.empty()),
|
||||||
|
recipeHolder.value().baseIngredient().map(EntryIngredient::ofIngredient).orElse(EntryIngredient.empty()),
|
||||||
|
recipeHolder.value().additionIngredient().map(EntryIngredient::ofIngredient).orElse(EntryIngredient.empty())
|
||||||
|
),
|
||||||
|
List.of(EntryIngredient.of(recipeHolder.value().getResult())),
|
||||||
|
SmithingDisplay.SmithingRecipeType.TRANSFORM,
|
||||||
|
recipeHolder.id().location()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* see me.shedaniel.rei.plugin.common.displays.DefaultSmithingDisplay#fromTrimming
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
public static Collection<Display> ofSmithingTrimRecipe(@NotNull RecipeHolder<SmithingTrimRecipe> recipeHolder) {
|
||||||
|
RegistryAccess registryAccess = MinecraftServer.getServer().registryAccess();
|
||||||
|
SmithingTrimRecipe recipe = recipeHolder.value();
|
||||||
|
List<Display> displays = new ArrayList<>();
|
||||||
|
for (Holder<Item> templateItem : (Iterable<Holder<Item>>) recipe.templateIngredient().map(Ingredient::items).orElse(Stream.of())::iterator) {
|
||||||
|
Holder.Reference<TrimPattern> trimPattern = getPatternFromTemplate(registryAccess, templateItem)
|
||||||
|
.orElse(null);
|
||||||
|
if (trimPattern == null) continue;
|
||||||
|
|
||||||
|
for (Holder<Item> additionStack : (Iterable<Holder<Item>>) recipe.additionIngredient().map(Ingredient::items).orElse(Stream.of())::iterator) {
|
||||||
|
Holder.Reference<TrimMaterial> trimMaterial = getMaterialFromIngredient(registryAccess, additionStack)
|
||||||
|
.orElse(null);
|
||||||
|
if (trimMaterial == null) continue;
|
||||||
|
|
||||||
|
EntryIngredient baseIngredient = recipe.baseIngredient().map(EntryIngredient::ofIngredient).orElse(EntryIngredient.empty());
|
||||||
|
EntryIngredient templateOutput = baseIngredient.isEmpty() ? EntryIngredient.empty()
|
||||||
|
: getTrimmingOutput(registryAccess, templateItem.value().getDefaultInstance(), baseIngredient.get(0), additionStack.value().getDefaultInstance());
|
||||||
|
|
||||||
|
displays.add(new SmithingDisplay(List.of(
|
||||||
|
EntryIngredient.ofItemHolder(templateItem),
|
||||||
|
baseIngredient,
|
||||||
|
EntryIngredient.ofItemHolder(additionStack)
|
||||||
|
), List.of(templateOutput), SmithingDisplay.SmithingRecipeType.TRIM, recipeHolder.id().location()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return displays;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EntryIngredient getTrimmingOutput(RegistryAccess registryAccess, ItemStack templateItem, ItemStack baseItem, ItemStack additionItem) {
|
||||||
|
Holder.Reference<TrimPattern> trimPattern = TrimPatterns.getFromTemplate(registryAccess, templateItem)
|
||||||
|
.orElse(null);
|
||||||
|
if (trimPattern == null) return EntryIngredient.empty();
|
||||||
|
Holder.Reference<TrimMaterial> trimMaterial = TrimMaterials.getFromIngredient(registryAccess, additionItem)
|
||||||
|
.orElse(null);
|
||||||
|
if (trimMaterial == null) return EntryIngredient.empty();
|
||||||
|
ArmorTrim armorTrim = new ArmorTrim(trimMaterial, trimPattern);
|
||||||
|
ArmorTrim trim = baseItem.get(DataComponents.TRIM);
|
||||||
|
if (trim != null && trim.hasPatternAndMaterial(trimPattern, trimMaterial)) return EntryIngredient.empty();
|
||||||
|
ItemStack newItem = baseItem.copyWithCount(1);
|
||||||
|
newItem.set(DataComponents.TRIM, armorTrim);
|
||||||
|
return EntryIngredient.of(newItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Optional<Holder.Reference<TrimPattern>> getPatternFromTemplate(HolderLookup.Provider provider, Holder<Item> item) {
|
||||||
|
return provider.lookupOrThrow(Registries.TRIM_PATTERN)
|
||||||
|
.listElements()
|
||||||
|
.filter(reference -> item == reference.value().templateItem())
|
||||||
|
.findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Optional<Holder.Reference<TrimMaterial>> getMaterialFromIngredient(HolderLookup.Provider provider, Holder<Item> item) {
|
||||||
|
return provider.lookupOrThrow(Registries.TRIM_MATERIAL)
|
||||||
|
.listElements()
|
||||||
|
.filter(reference -> item == reference.value().ingredient())
|
||||||
|
.findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EntryIngredient ofSlotDisplay(SlotDisplay slot) {
|
||||||
|
return switch (slot) {
|
||||||
|
case SlotDisplay.Empty ignored -> EntryIngredient.empty();
|
||||||
|
case SlotDisplay.ItemSlotDisplay s -> EntryIngredient.of(s.item().value());
|
||||||
|
case SlotDisplay.ItemStackSlotDisplay s -> EntryIngredient.of(s.stack());
|
||||||
|
case SlotDisplay.TagSlotDisplay s -> ofItemTag(s.tag());
|
||||||
|
case SlotDisplay.Composite s -> {
|
||||||
|
ArrayList<ItemStack> list = new ArrayList<>();
|
||||||
|
for (SlotDisplay slotDisplay : s.contents()) {
|
||||||
|
ofSlotDisplay(slotDisplay).stream().forEach(list::add);
|
||||||
|
}
|
||||||
|
yield EntryIngredient.of(list.toArray(new ItemStack[0]));
|
||||||
|
}
|
||||||
|
// REI Bad idea
|
||||||
|
case SlotDisplay.AnyFuel ignored -> EntryIngredient.empty();
|
||||||
|
default -> {
|
||||||
|
RegistryAccess access = MinecraftServer.getServer().registryAccess();
|
||||||
|
try {
|
||||||
|
List<ItemStack> stacks = slot.resolveForStacks(new ContextMap.Builder()
|
||||||
|
.withParameter(SlotDisplayContext.REGISTRIES, access)
|
||||||
|
.create(SlotDisplayContext.CONTEXT));
|
||||||
|
yield EntryIngredient.of(stacks.toArray(new ItemStack[0]));
|
||||||
|
} catch (Exception e) {
|
||||||
|
MinecraftServer.LOGGER.warn("Failed to resolve slot display: {}", slot, e);
|
||||||
|
yield EntryIngredient.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<EntryIngredient> ofSlotDisplays(Collection<SlotDisplay> slots) {
|
||||||
|
if (slots instanceof Collection<?> collection && collection.isEmpty()) return Collections.emptyList();
|
||||||
|
ImmutableList.Builder<EntryIngredient> ingredients = ImmutableList.builder();
|
||||||
|
for (SlotDisplay slot : slots) {
|
||||||
|
ingredients.add(ofSlotDisplay(slot));
|
||||||
|
}
|
||||||
|
return ingredients.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends ItemLike> EntryIngredient ofItemTag(TagKey<T> tagKey) {
|
||||||
|
HolderGetter<T> getter = MinecraftServer.getServer().registryAccess().lookupOrThrow(tagKey.registry());
|
||||||
|
HolderSet.Named<T> holders = getter.get(tagKey).orElse(null);
|
||||||
|
if (holders == null) return EntryIngredient.empty();
|
||||||
|
|
||||||
|
int size = holders.size();
|
||||||
|
if (size == 0) return EntryIngredient.empty();
|
||||||
|
if (size == 1) return EntryIngredient.of(new ItemStack(holders.get(0).value()));
|
||||||
|
|
||||||
|
List<ItemStack> stackList = new ArrayList<>();
|
||||||
|
for (Holder<T> t : holders) {
|
||||||
|
ItemStack stack = new ItemStack(t.value());
|
||||||
|
if (!stack.isEmpty()) {
|
||||||
|
stackList.add(stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return EntryIngredient.of(stackList.toArray(new ItemStack[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package org.leavesmc.leaves.protocol.rei.display;
|
||||||
|
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.crafting.CraftingInput;
|
||||||
|
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||||
|
import net.minecraft.world.item.crafting.ShapedRecipe;
|
||||||
|
import net.minecraft.world.item.crafting.display.ShapedCraftingRecipeDisplay;
|
||||||
|
import org.bukkit.craftbukkit.CraftRegistry;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.leavesmc.leaves.protocol.rei.ingredient.EntryIngredient;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* see me.shedaniel.rei.plugin.common.displays.crafting.DefaultShapedDisplay#DefaultShapedDisplay(RecipeHolder)
|
||||||
|
*/
|
||||||
|
public class ShapedDisplay extends CraftingDisplay {
|
||||||
|
private final int width;
|
||||||
|
|
||||||
|
private final int height;
|
||||||
|
|
||||||
|
private static final StreamCodec<RegistryFriendlyByteBuf, CraftingDisplay> CODEC = StreamCodec.composite(
|
||||||
|
EntryIngredient.CODEC.apply(ByteBufCodecs.list()),
|
||||||
|
CraftingDisplay::getInputEntries,
|
||||||
|
EntryIngredient.CODEC.apply(ByteBufCodecs.list()),
|
||||||
|
CraftingDisplay::getOutputEntries,
|
||||||
|
ByteBufCodecs.optional(ResourceLocation.STREAM_CODEC),
|
||||||
|
CraftingDisplay::getOptionalLocation,
|
||||||
|
ByteBufCodecs.INT,
|
||||||
|
CraftingDisplay::getWidth,
|
||||||
|
ByteBufCodecs.INT,
|
||||||
|
CraftingDisplay::getHeight,
|
||||||
|
ShapedDisplay::of
|
||||||
|
);
|
||||||
|
|
||||||
|
private static final ResourceLocation SERIALIZER_ID = ResourceLocation.tryBuild("minecraft", "default/crafting/shaped");
|
||||||
|
|
||||||
|
public ShapedDisplay(@NotNull RecipeHolder<ShapedRecipe> recipeHolder) {
|
||||||
|
super(
|
||||||
|
ofIngredient(recipeHolder.value()),
|
||||||
|
List.of(EntryIngredient.of(recipeHolder.value().assemble(CraftingInput.EMPTY, CraftRegistry.getMinecraftRegistry()))),
|
||||||
|
recipeHolder.id().location()
|
||||||
|
);
|
||||||
|
this.width = recipeHolder.value().getWidth();
|
||||||
|
this.height = recipeHolder.value().getHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShapedDisplay(@NotNull ShapedCraftingRecipeDisplay recipeDisplay, ResourceLocation id) {
|
||||||
|
super(
|
||||||
|
Display.ofSlotDisplays(recipeDisplay.ingredients()),
|
||||||
|
List.of(ofSlotDisplay(recipeDisplay.result())),
|
||||||
|
id
|
||||||
|
);
|
||||||
|
this.width = recipeDisplay.width();
|
||||||
|
this.height = recipeDisplay.height();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getWidth() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getHeight() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getSerializerId() {
|
||||||
|
return SERIALIZER_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StreamCodec<RegistryFriendlyByteBuf, CraftingDisplay> streamCodec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||||
|
private static CraftingDisplay of(List<EntryIngredient> inputs, List<EntryIngredient> outputs, Optional<ResourceLocation> location, int width, int height) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<EntryIngredient> ofIngredient(ShapedRecipe recipe) {
|
||||||
|
return recipe.getIngredients().stream().map(ingredient -> {
|
||||||
|
if (ingredient.isEmpty()) {
|
||||||
|
return EntryIngredient.empty();
|
||||||
|
}
|
||||||
|
ItemStack[] itemStacks = ingredient.get().items()
|
||||||
|
.map(itemHolder -> new ItemStack(itemHolder, 1))
|
||||||
|
.toArray(ItemStack[]::new);
|
||||||
|
return EntryIngredient.of(itemStacks);
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package org.leavesmc.leaves.protocol.rei.display;
|
||||||
|
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.item.crafting.CraftingInput;
|
||||||
|
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||||
|
import net.minecraft.world.item.crafting.ShapelessRecipe;
|
||||||
|
import net.minecraft.world.item.crafting.display.ShapelessCraftingRecipeDisplay;
|
||||||
|
import org.apache.commons.lang.NotImplementedException;
|
||||||
|
import org.bukkit.craftbukkit.CraftRegistry;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.leavesmc.leaves.protocol.rei.ingredient.EntryIngredient;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class ShapelessDisplay extends CraftingDisplay {
|
||||||
|
private static final StreamCodec<RegistryFriendlyByteBuf, CraftingDisplay> CODEC = StreamCodec.composite(
|
||||||
|
EntryIngredient.CODEC.apply(ByteBufCodecs.list()),
|
||||||
|
CraftingDisplay::getInputEntries,
|
||||||
|
EntryIngredient.CODEC.apply(ByteBufCodecs.list()),
|
||||||
|
CraftingDisplay::getOutputEntries,
|
||||||
|
ByteBufCodecs.optional(ResourceLocation.STREAM_CODEC),
|
||||||
|
CraftingDisplay::getOptionalLocation,
|
||||||
|
ShapelessDisplay::of
|
||||||
|
);
|
||||||
|
|
||||||
|
private static final ResourceLocation SERIALIZER_ID = ResourceLocation.tryBuild("minecraft", "default/crafting/shapeless");
|
||||||
|
|
||||||
|
public ShapelessDisplay(@NotNull List<EntryIngredient> inputs,
|
||||||
|
@NotNull List<EntryIngredient> outputs,
|
||||||
|
@NotNull ResourceLocation location) {
|
||||||
|
super(inputs, outputs, location);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShapelessDisplay(@NotNull RecipeHolder<ShapelessRecipe> recipeHolder) {
|
||||||
|
this(
|
||||||
|
recipeHolder.value().placementInfo().ingredients().stream().map(EntryIngredient::ofIngredient).toList(),
|
||||||
|
List.of(EntryIngredient.of(recipeHolder.value().assemble(CraftingInput.EMPTY, CraftRegistry.getMinecraftRegistry()))),
|
||||||
|
recipeHolder.id().location()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShapelessDisplay(@NotNull ShapelessCraftingRecipeDisplay recipeDisplay, ResourceLocation id) {
|
||||||
|
this(
|
||||||
|
ofSlotDisplays(recipeDisplay.ingredients()),
|
||||||
|
List.of(ofSlotDisplay(recipeDisplay.result())),
|
||||||
|
id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getWidth() {
|
||||||
|
return getInputEntries().size() > 4 ? 3 : 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getHeight() {
|
||||||
|
return getInputEntries().size() > 4 ? 3 : 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getSerializerId() {
|
||||||
|
return SERIALIZER_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StreamCodec<RegistryFriendlyByteBuf, CraftingDisplay> streamCodec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||||
|
private static CraftingDisplay of(List<EntryIngredient> inputs, List<EntryIngredient> outputs, Optional<ResourceLocation> location) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package org.leavesmc.leaves.protocol.rei.display;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||||
|
import net.minecraft.world.item.crafting.SmeltingRecipe;
|
||||||
|
|
||||||
|
public class SmeltingDisplay extends CookingDisplay {
|
||||||
|
public SmeltingDisplay(RecipeHolder<SmeltingRecipe> recipe) {
|
||||||
|
super(recipe);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final ResourceLocation SERIALIZER_ID = ResourceLocation.tryBuild("minecraft", "default/smelting");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getSerializerId() {
|
||||||
|
return SERIALIZER_ID;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package org.leavesmc.leaves.protocol.rei.display;
|
||||||
|
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.util.ByIdMap;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.leavesmc.leaves.protocol.rei.ingredient.EntryIngredient;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.IntFunction;
|
||||||
|
|
||||||
|
public class SmithingDisplay extends Display {
|
||||||
|
private static final StreamCodec<RegistryFriendlyByteBuf, SmithingDisplay> CODEC = StreamCodec.composite(
|
||||||
|
EntryIngredient.CODEC.apply(ByteBufCodecs.list()),
|
||||||
|
SmithingDisplay::getInputEntries,
|
||||||
|
EntryIngredient.CODEC.apply(ByteBufCodecs.list()),
|
||||||
|
SmithingDisplay::getOutputEntries,
|
||||||
|
ByteBufCodecs.optional(SmithingRecipeType.STREAM_CODEC),
|
||||||
|
SmithingDisplay::getOptionalType,
|
||||||
|
ByteBufCodecs.optional(ResourceLocation.STREAM_CODEC),
|
||||||
|
SmithingDisplay::getOptionalLocation,
|
||||||
|
SmithingDisplay::of
|
||||||
|
);
|
||||||
|
|
||||||
|
private static final ResourceLocation SERIALIZER_ID = ResourceLocation.tryBuild("minecraft", "default/smithing");
|
||||||
|
|
||||||
|
private final SmithingRecipeType type;
|
||||||
|
|
||||||
|
public Optional<SmithingRecipeType> getOptionalType() {
|
||||||
|
return Optional.of(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getSerializerId() {
|
||||||
|
return SERIALIZER_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StreamCodec<RegistryFriendlyByteBuf, ? extends Display> streamCodec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SmithingDisplay(
|
||||||
|
@NotNull List<EntryIngredient> inputs,
|
||||||
|
@NotNull List<EntryIngredient> outputs,
|
||||||
|
@NotNull SmithingRecipeType type,
|
||||||
|
@NotNull ResourceLocation location
|
||||||
|
) {
|
||||||
|
super(inputs, outputs, location);
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||||
|
private static SmithingDisplay of(List<EntryIngredient> inputs, List<EntryIngredient> outputs, Optional<SmithingRecipeType> type, Optional<ResourceLocation> location) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SmithingRecipeType {
|
||||||
|
TRIM,
|
||||||
|
TRANSFORM,
|
||||||
|
;
|
||||||
|
|
||||||
|
public static final Codec<SmithingRecipeType> CODEC = Codec.STRING.xmap(SmithingRecipeType::valueOf, SmithingRecipeType::name);
|
||||||
|
public static final IntFunction<SmithingRecipeType> BY_ID = ByIdMap.continuous(Enum::ordinal, values(), ByIdMap.OutOfBoundsStrategy.ZERO);
|
||||||
|
public static final StreamCodec<ByteBuf, SmithingRecipeType> STREAM_CODEC = ByteBufCodecs.idMapper(BY_ID, Enum::ordinal);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package org.leavesmc.leaves.protocol.rei.display;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.item.crafting.AbstractCookingRecipe;
|
||||||
|
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||||
|
|
||||||
|
public class SmokingDisplay extends CookingDisplay {
|
||||||
|
public SmokingDisplay(RecipeHolder<? extends AbstractCookingRecipe> recipe) {
|
||||||
|
super(recipe);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final ResourceLocation SERIALIZER_ID = ResourceLocation.tryBuild("minecraft", "default/smoking");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getSerializerId() {
|
||||||
|
return SERIALIZER_ID;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
package org.leavesmc.leaves.protocol.rei.display;
|
||||||
|
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||||
|
import net.minecraft.world.item.crafting.SingleRecipeInput;
|
||||||
|
import net.minecraft.world.item.crafting.StonecutterRecipe;
|
||||||
|
import org.bukkit.craftbukkit.CraftRegistry;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.leavesmc.leaves.protocol.rei.ingredient.EntryIngredient;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* see me.shedaniel.rei.plugin.common.displays.DefaultStoneCuttingDisplay
|
||||||
|
*/
|
||||||
|
public class StoneCuttingDisplay extends Display {
|
||||||
|
private static final StreamCodec<RegistryFriendlyByteBuf, StoneCuttingDisplay> CODEC = StreamCodec.composite(
|
||||||
|
EntryIngredient.CODEC.apply(ByteBufCodecs.list()),
|
||||||
|
StoneCuttingDisplay::getInputEntries,
|
||||||
|
EntryIngredient.CODEC.apply(ByteBufCodecs.list()),
|
||||||
|
StoneCuttingDisplay::getOutputEntries,
|
||||||
|
ByteBufCodecs.optional(ResourceLocation.STREAM_CODEC),
|
||||||
|
StoneCuttingDisplay::getOptionalLocation,
|
||||||
|
StoneCuttingDisplay::of
|
||||||
|
);
|
||||||
|
|
||||||
|
private static final ResourceLocation SERIALIZER_ID = ResourceLocation.tryBuild("minecraft", "default/stone_cutting");
|
||||||
|
|
||||||
|
public StoneCuttingDisplay(@NotNull List<EntryIngredient> inputs, @NotNull List<EntryIngredient> outputs, @NotNull ResourceLocation id) {
|
||||||
|
super(inputs, outputs, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StoneCuttingDisplay(RecipeHolder<StonecutterRecipe> recipeHolder) {
|
||||||
|
this(
|
||||||
|
List.of(EntryIngredient.ofIngredient(recipeHolder.value().input())),
|
||||||
|
List.of(EntryIngredient.of(recipeHolder.value().assemble(new SingleRecipeInput(ItemStack.EMPTY), CraftRegistry.getMinecraftRegistry()))),
|
||||||
|
recipeHolder.id().location()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation getSerializerId() {
|
||||||
|
return SERIALIZER_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StreamCodec<RegistryFriendlyByteBuf, StoneCuttingDisplay> streamCodec() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||||
|
private static StoneCuttingDisplay of(@NotNull List<EntryIngredient> inputs, @NotNull List<EntryIngredient> outputs, @NotNull Optional<ResourceLocation> id) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
package org.leavesmc.leaves.protocol.rei.ingredient;
|
||||||
|
|
||||||
|
import net.minecraft.core.Holder;
|
||||||
|
import net.minecraft.core.component.DataComponentPatch;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.crafting.Ingredient;
|
||||||
|
import net.minecraft.world.level.ItemLike;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class EntryIngredient {
|
||||||
|
private static final StreamCodec<RegistryFriendlyByteBuf, Holder<Item>> ITEM_STREAM_CODEC = ByteBufCodecs.holderRegistry(Registries.ITEM);
|
||||||
|
public static final StreamCodec<RegistryFriendlyByteBuf, EntryIngredient> CODEC = new StreamCodec<>() {
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public EntryIngredient decode(@NotNull RegistryFriendlyByteBuf buffer) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(@NotNull RegistryFriendlyByteBuf buffer, @NotNull EntryIngredient value) {
|
||||||
|
ByteBufCodecs.writeCount(buffer, value.size(), Integer.MAX_VALUE);
|
||||||
|
value.stream().forEach(itemStack -> {
|
||||||
|
buffer.writeResourceLocation(ITEM_ID);
|
||||||
|
if (itemStack.isEmpty()) {
|
||||||
|
buffer.writeVarInt(0);
|
||||||
|
} else {
|
||||||
|
buffer.writeVarInt(itemStack.getCount());
|
||||||
|
ITEM_STREAM_CODEC.encode(buffer, itemStack.getItemHolder());
|
||||||
|
DataComponentPatch.STREAM_CODEC.encode(buffer, itemStack.components.asPatch());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final ResourceLocation ITEM_ID = ResourceLocation.withDefaultNamespace("item");
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private final ItemStack[] array;
|
||||||
|
|
||||||
|
private static final EntryIngredient EMPTY = new EntryIngredient(new ItemStack[0]);
|
||||||
|
|
||||||
|
private EntryIngredient(@NotNull ItemStack[] array) {
|
||||||
|
this.array = Objects.requireNonNull(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream<ItemStack> stream() {
|
||||||
|
return Arrays.stream(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EntryIngredient empty() {
|
||||||
|
return EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return array.length == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemStack get(int index) {
|
||||||
|
return array[index].copy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return array.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EntryIngredient ofItemHolder(@NotNull Holder<? extends ItemLike> item) {
|
||||||
|
return EntryIngredient.of(item.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EntryIngredient of(@NotNull ItemLike item) {
|
||||||
|
return EntryIngredient.of(new ItemStack(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EntryIngredient of(@NotNull ItemStack itemStack) {
|
||||||
|
return new EntryIngredient(new ItemStack[]{itemStack});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EntryIngredient of(@NotNull ItemStack... itemStacks) {
|
||||||
|
return new EntryIngredient(Arrays.copyOf(itemStacks, itemStacks.length));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EntryIngredient ofIngredient(Ingredient ingredient) {
|
||||||
|
if (ingredient.isEmpty()) {
|
||||||
|
return EntryIngredient.empty();
|
||||||
|
}
|
||||||
|
ItemStack[] itemStacks = ingredient.items()
|
||||||
|
.map(itemHolder -> new ItemStack(itemHolder, 1))
|
||||||
|
.toArray(ItemStack[]::new);
|
||||||
|
return EntryIngredient.of(itemStacks);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package org.leavesmc.leaves.protocol.rei.payload;
|
||||||
|
|
||||||
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
|
||||||
|
|
||||||
|
public record BufCustomPacketPayload(
|
||||||
|
Type<BufCustomPacketPayload> type,
|
||||||
|
byte[] payload
|
||||||
|
) implements LeavesCustomPayload<BufCustomPacketPayload> {
|
||||||
|
@New
|
||||||
|
public static BufCustomPacketPayload create(ResourceLocation location, @NotNull FriendlyByteBuf buf) {
|
||||||
|
return new BufCustomPacketPayload(new Type<>(location), buf.readByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(FriendlyByteBuf buf) {
|
||||||
|
FriendlyByteBuf.writeByteArray(buf, this.payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceLocation id() {
|
||||||
|
return type.id();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Type<BufCustomPacketPayload> type() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package org.leavesmc.leaves.protocol.rei.payload;
|
|
||||||
|
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
|
|
||||||
import org.leavesmc.leaves.protocol.rei.REIServerProtocol;
|
|
||||||
|
|
||||||
public record CreateItemGrabPayload(ItemStack item) implements LeavesCustomPayload<CreateItemGrabPayload> {
|
|
||||||
|
|
||||||
private static final ResourceLocation ID = REIServerProtocol.id("create_item_grab");
|
|
||||||
|
|
||||||
@New
|
|
||||||
public CreateItemGrabPayload(ResourceLocation location, @NotNull FriendlyByteBuf buf) {
|
|
||||||
this(buf.readJsonWithCodec(ItemStack.OPTIONAL_CODEC));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(@NotNull FriendlyByteBuf buf) {
|
|
||||||
buf.writeJsonWithCodec(ItemStack.OPTIONAL_CODEC, item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResourceLocation id() {
|
|
||||||
return ID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package org.leavesmc.leaves.protocol.rei.payload;
|
|
||||||
|
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
|
|
||||||
import org.leavesmc.leaves.protocol.rei.REIServerProtocol;
|
|
||||||
|
|
||||||
public record CreateItemHotbarPayload(ItemStack item, int hotbarSlot) implements LeavesCustomPayload<CreateItemHotbarPayload> {
|
|
||||||
|
|
||||||
private static final ResourceLocation ID = REIServerProtocol.id("create_item_hotbar");
|
|
||||||
|
|
||||||
@New
|
|
||||||
public CreateItemHotbarPayload(ResourceLocation location, FriendlyByteBuf buf) {
|
|
||||||
this(buf.readJsonWithCodec(ItemStack.OPTIONAL_CODEC), buf.readVarInt());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(FriendlyByteBuf buf) {
|
|
||||||
buf.writeJsonWithCodec(ItemStack.OPTIONAL_CODEC, item);
|
|
||||||
buf.writeVarInt(hotbarSlot);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResourceLocation id() {
|
|
||||||
return ID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package org.leavesmc.leaves.protocol.rei.payload;
|
|
||||||
|
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
|
|
||||||
import org.leavesmc.leaves.protocol.rei.REIServerProtocol;
|
|
||||||
|
|
||||||
public record CreateItemMessagePayload(ItemStack item, String playerName) implements LeavesCustomPayload<CreateItemMessagePayload> {
|
|
||||||
|
|
||||||
private static final ResourceLocation ID = REIServerProtocol.id("ci_msg");
|
|
||||||
|
|
||||||
@New
|
|
||||||
public CreateItemMessagePayload(ResourceLocation location, @NotNull FriendlyByteBuf buf) {
|
|
||||||
this(buf.readJsonWithCodec(ItemStack.OPTIONAL_CODEC), buf.readUtf());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(@NotNull FriendlyByteBuf buf) {
|
|
||||||
buf.writeJsonWithCodec(ItemStack.OPTIONAL_CODEC, item);
|
|
||||||
buf.writeUtf(playerName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResourceLocation id() {
|
|
||||||
return ID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package org.leavesmc.leaves.protocol.rei.payload;
|
|
||||||
|
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
|
|
||||||
import org.leavesmc.leaves.protocol.rei.REIServerProtocol;
|
|
||||||
|
|
||||||
public record CreateItemPayload(ItemStack item) implements LeavesCustomPayload<CreateItemPayload> {
|
|
||||||
|
|
||||||
private static final ResourceLocation ID = REIServerProtocol.id("create_item");
|
|
||||||
|
|
||||||
@New
|
|
||||||
public CreateItemPayload(ResourceLocation location, @NotNull FriendlyByteBuf buf) {
|
|
||||||
this(buf.readJsonWithCodec(ItemStack.OPTIONAL_CODEC));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(@NotNull FriendlyByteBuf buf) {
|
|
||||||
buf.writeJsonWithCodec(ItemStack.OPTIONAL_CODEC, item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResourceLocation id() {
|
|
||||||
return ID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package org.leavesmc.leaves.protocol.rei.payload;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.ByteBufUtil;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||||
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
|
import net.minecraft.network.codec.StreamCodec;
|
||||||
|
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||||
|
import net.minecraft.util.ByIdMap;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.leavesmc.leaves.LeavesLogger;
|
||||||
|
import org.leavesmc.leaves.protocol.rei.REIServerProtocol;
|
||||||
|
import org.leavesmc.leaves.protocol.rei.display.Display;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.IntFunction;
|
||||||
|
import java.util.function.UnaryOperator;
|
||||||
|
|
||||||
|
public record DisplaySyncPayload(
|
||||||
|
SyncType syncType,
|
||||||
|
Collection<Display> displays,
|
||||||
|
long version
|
||||||
|
) implements CustomPacketPayload {
|
||||||
|
public static final CustomPacketPayload.Type<DisplaySyncPayload> TYPE = new CustomPacketPayload.Type<>(REIServerProtocol.SYNC_DISPLAYS_PACKET);
|
||||||
|
public static final StreamCodec<? super RegistryFriendlyByteBuf, DisplaySyncPayload> STREAM_CODEC = StreamCodec.composite(
|
||||||
|
SyncType.STREAM_CODEC,
|
||||||
|
DisplaySyncPayload::syncType,
|
||||||
|
Display.dispatchCodec().apply(codec -> new StreamCodec<RegistryFriendlyByteBuf, Display>() {
|
||||||
|
@Override
|
||||||
|
public void encode(@NotNull RegistryFriendlyByteBuf buf, @NotNull Display display) {
|
||||||
|
RegistryFriendlyByteBuf tmpBuf = new RegistryFriendlyByteBuf(Unpooled.buffer(), buf.registryAccess());
|
||||||
|
try {
|
||||||
|
codec.encode(tmpBuf, display);
|
||||||
|
} catch (Exception e) {
|
||||||
|
tmpBuf.release();
|
||||||
|
buf.writeBoolean(false);
|
||||||
|
LeavesLogger.LOGGER.warning("Failed to encode display: " + display, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buf.writeBoolean(true);
|
||||||
|
RegistryFriendlyByteBuf.writeByteArray(buf, ByteBufUtil.getBytes(tmpBuf));
|
||||||
|
tmpBuf.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Display decode(@NotNull RegistryFriendlyByteBuf buf) {
|
||||||
|
// The DisplayDecoder will not be called on the server side
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).apply(ByteBufCodecs.<RegistryFriendlyByteBuf, Display, Collection<Display>>collection(ArrayList::new)).map(
|
||||||
|
collection -> collection.stream().filter(Objects::nonNull).toList(),
|
||||||
|
UnaryOperator.identity()
|
||||||
|
),
|
||||||
|
DisplaySyncPayload::displays,
|
||||||
|
ByteBufCodecs.LONG,
|
||||||
|
DisplaySyncPayload::version,
|
||||||
|
DisplaySyncPayload::new
|
||||||
|
);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NotNull
|
||||||
|
public Type<? extends CustomPacketPayload> type() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum SyncType {
|
||||||
|
APPEND,
|
||||||
|
SET;
|
||||||
|
|
||||||
|
public static final IntFunction<SyncType> BY_ID = ByIdMap.continuous(Enum::ordinal, values(), ByIdMap.OutOfBoundsStrategy.ZERO);
|
||||||
|
public static final StreamCodec<ByteBuf, SyncType> STREAM_CODEC = ByteBufCodecs.idMapper(BY_ID, Enum::ordinal);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
package org.leavesmc.leaves.protocol.rei.payload;
|
|
||||||
|
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
|
|
||||||
import org.leavesmc.leaves.protocol.rei.REIServerProtocol;
|
|
||||||
|
|
||||||
public record MoveItemPayload(ResourceLocation category, boolean isShift, CompoundTag nbt) implements LeavesCustomPayload<MoveItemPayload> {
|
|
||||||
|
|
||||||
private static final ResourceLocation ID = REIServerProtocol.id("move_items_new");
|
|
||||||
|
|
||||||
@New
|
|
||||||
public MoveItemPayload(ResourceLocation location, @NotNull FriendlyByteBuf buf) {
|
|
||||||
this(buf.readResourceLocation(), buf.readBoolean(), buf.readNbt());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(@NotNull FriendlyByteBuf buf) {
|
|
||||||
buf.writeResourceLocation(category);
|
|
||||||
buf.writeBoolean(isShift);
|
|
||||||
buf.writeNbt(nbt);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResourceLocation id() {
|
|
||||||
return ID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user