fix: make new packets ComponentHolding

This commit is contained in:
mworzala 2024-11-02 19:20:12 -04:00 committed by Matt Worzala
parent b7d89c84a7
commit 2df89f23c9
11 changed files with 256 additions and 49 deletions

View File

@ -22,6 +22,8 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.IntUnaryOperator;
@ -150,9 +152,9 @@ public sealed interface ItemStack extends TagReadable, DataComponent.Holder, Hov
* Applies a transformation to the value of a component, only if present.
*
* @param component The component type to modify
* @param operator The transformation function
* @param operator The transformation function
* @param <T> The component type
* @return A new ItemStack if the component was transformed, otherwise this.
* @param <T> The component type
*/
default <T> @NotNull ItemStack with(@NotNull DataComponent<T> component, @NotNull UnaryOperator<T> operator) {
T value = get(component);
@ -238,6 +240,7 @@ public sealed interface ItemStack extends TagReadable, DataComponent.Holder, Hov
/**
* Converts this itemstack back into a builder (starting from the current state).
*
* @return this itemstack, as a builder.
*/
@NotNull ItemStack.Builder builder();
@ -259,6 +262,29 @@ public sealed interface ItemStack extends TagReadable, DataComponent.Holder, Hov
}
}
// These functions are mirrors of ComponentHolder, but we can't actually implement that interface
// because it conflicts with DataComponent.Holder.
static @NotNull Collection<Component> textComponents(@NotNull ItemStack itemStack) {
final var components = new ArrayList<>(itemStack.get(ItemComponent.LORE, List.of()));
final var displayName = itemStack.get(ItemComponent.CUSTOM_NAME);
if (displayName != null) components.add(displayName);
final var itemName = itemStack.get(ItemComponent.ITEM_NAME);
if (itemName != null) components.add(itemName);
return List.copyOf(components);
}
static @NotNull ItemStack copyWithOperator(@NotNull ItemStack itemStack, @NotNull UnaryOperator<Component> operator) {
return itemStack
.with(ItemComponent.CUSTOM_NAME, operator)
.with(ItemComponent.ITEM_NAME, operator)
.with(ItemComponent.LORE, (UnaryOperator<List<Component>>) lines -> {
final var translatedComponents = new ArrayList<Component>();
lines.forEach(component -> translatedComponents.add(operator.apply(component)));
return translatedComponents;
});
}
sealed interface Builder permits ItemStackImpl.Builder {
@Contract(value = "_ -> this")

View File

@ -2,11 +2,11 @@ package net.minestom.server.listener;
import net.minestom.server.entity.Player;
import net.minestom.server.network.packet.client.play.ClientPlaceRecipePacket;
import net.minestom.server.network.packet.server.play.PlaceGhostRecipePacket;
public class RecipeListener {
public static void listener(ClientPlaceRecipePacket packet, Player player) {
player.sendPacket(new PlaceGhostRecipePacket(packet.windowId(), packet.recipe()));
// TODO(1.21.2)
// player.sendPacket(new PlaceGhostRecipePacket(packet.windowId(), packet.recipe()));
}
}

View File

@ -6,15 +6,10 @@ import net.minestom.server.network.packet.client.ClientPacket;
import static net.minestom.server.network.NetworkBuffer.*;
public record ClientPlaceRecipePacket(byte windowId, String recipe, boolean makeAll) implements ClientPacket {
public record ClientPlaceRecipePacket(byte windowId, int recipeDisplayId, boolean makeAll) implements ClientPacket {
public static final NetworkBuffer.Type<ClientPlaceRecipePacket> SERIALIZER = NetworkBufferTemplate.template(
BYTE, ClientPlaceRecipePacket::windowId,
STRING, ClientPlaceRecipePacket::recipe,
VAR_INT, ClientPlaceRecipePacket::recipeDisplayId,
BOOLEAN, ClientPlaceRecipePacket::makeAll,
ClientPlaceRecipePacket::new);
public ClientPlaceRecipePacket {
if (recipe.length() > 256) {
throw new IllegalArgumentException("'recipe' cannot be longer than 256 characters.");
}
}
}

View File

@ -2,18 +2,13 @@ package net.minestom.server.network.packet.server.play;
import net.kyori.adventure.text.Component;
import net.minestom.server.entity.EquipmentSlot;
import net.minestom.server.item.ItemComponent;
import net.minestom.server.item.ItemStack;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.server.ServerPacket;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.EnumMap;
import java.util.Map;
import java.util.Objects;
import java.util.*;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import static net.minestom.server.network.NetworkBuffer.BYTE;
import static net.minestom.server.network.NetworkBuffer.VAR_INT;
@ -48,19 +43,18 @@ public record EntityEquipmentPacket(int entityId,
@Override
public @NotNull Collection<Component> components() {
return this.equipments.values()
.stream()
.map(item -> item.get(ItemComponent.CUSTOM_NAME))
.filter(Objects::nonNull)
.collect(Collectors.toList());
final var components = new ArrayList<Component>();
for (var itemStack : this.equipments.values())
components.addAll(ItemStack.textComponents(itemStack));
return List.copyOf(components);
}
@Override
public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) {
final var map = new EnumMap<EquipmentSlot, ItemStack>(EquipmentSlot.class);
this.equipments.forEach((key, value) -> map.put(key, value.with(ItemComponent.CUSTOM_NAME, operator)));
return new EntityEquipmentPacket(this.entityId, map);
final var newEquipment = new EnumMap<EquipmentSlot, ItemStack>(EquipmentSlot.class);
for (var entry : this.equipments.entrySet())
newEquipment.put(entry.getKey(), ItemStack.copyWithOperator(entry.getValue(), operator));
return new EntityEquipmentPacket(this.entityId, newEquipment);
}
private static Map<EquipmentSlot, ItemStack> readEquipments(@NotNull NetworkBuffer reader) {

View File

@ -8,7 +8,7 @@ import org.jetbrains.annotations.NotNull;
import static net.minestom.server.network.NetworkBuffer.STRING;
import static net.minestom.server.network.NetworkBuffer.VAR_INT;
// TODO: Recipe is now a RecipeDisplay object need to look further into it.
// TODO(1.21.2): Recipe is now a RecipeDisplay object need to look further into it.
public record PlaceGhostRecipePacket(int windowId, @NotNull String recipe) implements ServerPacket.Play {
public static final NetworkBuffer.Type<PlaceGhostRecipePacket> SERIALIZER = NetworkBufferTemplate.template(
VAR_INT, PlaceGhostRecipePacket::windowId,

View File

@ -1,5 +1,6 @@
package net.minestom.server.network.packet.server.play;
import net.kyori.adventure.text.Component;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.NetworkBufferTemplate;
import net.minestom.server.network.packet.server.ServerPacket;
@ -10,11 +11,14 @@ import net.minestom.server.recipe.display.RecipeDisplay;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.UnaryOperator;
import static net.minestom.server.network.NetworkBuffer.BOOLEAN;
public record RecipeBookAddPacket(@NotNull List<Entry> entries, boolean replace) implements ServerPacket.Play {
public record RecipeBookAddPacket(@NotNull List<Entry> entries, boolean replace) implements ServerPacket.Play, ServerPacket.ComponentHolding {
public static final byte FLAG_NOTIFICATION = 1;
public static final byte FLAG_HIGHLIGHT = 1 << 1;
@ -55,4 +59,21 @@ public record RecipeBookAddPacket(@NotNull List<Entry> entries, boolean replace)
}
}
@Override
public @NotNull Collection<Component> components() {
final var components = new ArrayList<Component>();
for (Entry entry : entries)
components.addAll(entry.display.components());
return components;
}
@Override
public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) {
final var entries = new ArrayList<Entry>();
for (Entry entry : this.entries) {
entries.add(new Entry(entry.displayId, entry.display.copyWithOperator(operator),
entry.group, entry.category, entry.craftingRequirements, entry.flags));
}
return new RecipeBookAddPacket(entries, replace);
}
}

View File

@ -1,13 +1,27 @@
package net.minestom.server.network.packet.server.play;
import net.kyori.adventure.text.Component;
import net.minestom.server.item.ItemStack;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.NetworkBufferTemplate;
import net.minestom.server.network.packet.server.ServerPacket;
import org.jetbrains.annotations.NotNull;
public record SetCursorItemPacket(@NotNull ItemStack itemStack) implements ServerPacket.Play {
import java.util.Collection;
import java.util.function.UnaryOperator;
public record SetCursorItemPacket(@NotNull ItemStack itemStack) implements ServerPacket.Play, ServerPacket.ComponentHolding {
public static final NetworkBuffer.Type<SetCursorItemPacket> SERIALIZER = NetworkBufferTemplate.template(
ItemStack.NETWORK_TYPE, SetCursorItemPacket::itemStack,
SetCursorItemPacket::new);
@Override
public @NotNull Collection<Component> components() {
return ItemStack.textComponents(itemStack);
}
@Override
public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) {
return new SetCursorItemPacket(ItemStack.copyWithOperator(itemStack, operator));
}
}

View File

@ -1,16 +1,30 @@
package net.minestom.server.network.packet.server.play;
import net.kyori.adventure.text.Component;
import net.minestom.server.item.ItemStack;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.NetworkBufferTemplate;
import net.minestom.server.network.packet.server.ServerPacket;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.function.UnaryOperator;
import static net.minestom.server.network.NetworkBuffer.VAR_INT;
public record SetPlayerInventorySlotPacket(int slot, @NotNull ItemStack itemStack) implements ServerPacket.Play {
public record SetPlayerInventorySlotPacket(int slot, @NotNull ItemStack itemStack) implements ServerPacket.Play, ServerPacket.ComponentHolding {
public static final NetworkBuffer.Type<SetPlayerInventorySlotPacket> SERIALIZER = NetworkBufferTemplate.template(
VAR_INT, SetPlayerInventorySlotPacket::slot,
ItemStack.NETWORK_TYPE, SetPlayerInventorySlotPacket::itemStack,
SetPlayerInventorySlotPacket::new);
@Override
public @NotNull Collection<Component> components() {
return ItemStack.textComponents(itemStack);
}
@Override
public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) {
return new SetPlayerInventorySlotPacket(slot, ItemStack.copyWithOperator(itemStack, operator));
}
}

View File

@ -1,16 +1,13 @@
package net.minestom.server.network.packet.server.play;
import net.kyori.adventure.text.Component;
import net.minestom.server.item.ItemComponent;
import net.minestom.server.item.ItemStack;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.NetworkBufferTemplate;
import net.minestom.server.network.packet.server.ServerPacket;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.UnaryOperator;
import static net.minestom.server.network.NetworkBuffer.SHORT;
@ -27,24 +24,12 @@ public record SetSlotPacket(int windowId, int stateId, short slot,
@Override
public @NotNull Collection<Component> components() {
final var components = new ArrayList<>(this.itemStack.get(ItemComponent.LORE, List.of()));
final var displayName = this.itemStack.get(ItemComponent.CUSTOM_NAME);
if (displayName != null) components.add(displayName);
final var itemName = this.itemStack.get(ItemComponent.ITEM_NAME);
if (itemName != null) components.add(itemName);
return List.copyOf(components);
return ItemStack.textComponents(itemStack);
}
@Override
public @NotNull ServerPacket copyWithOperator(@NotNull UnaryOperator<Component> operator) {
return new SetSlotPacket(this.windowId, this.stateId, this.slot, this.itemStack
.with(ItemComponent.CUSTOM_NAME, operator)
.with(ItemComponent.ITEM_NAME, operator)
.with(ItemComponent.LORE, (UnaryOperator<List<Component>>) lines -> {
final var translatedComponents = new ArrayList<Component>();
lines.forEach(component -> translatedComponents.add(operator.apply(component)));
return translatedComponents;
}));
return new SetSlotPacket(this.windowId, this.stateId, this.slot, ItemStack.copyWithOperator(this.itemStack, operator));
}
}

View File

@ -1,12 +1,17 @@
package net.minestom.server.recipe.display;
import net.kyori.adventure.text.Component;
import net.minestom.server.adventure.ComponentHolder;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.NetworkBufferTemplate;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.UnaryOperator;
public sealed interface RecipeDisplay {
public sealed interface RecipeDisplay extends ComponentHolder<RecipeDisplay> {
@NotNull NetworkBuffer.Type<RecipeDisplay> NETWORK_TYPE = RecipeDisplayType.NETWORK_TYPE
.unionType(RecipeDisplay::dataSerializer, RecipeDisplay::recipeDisplayToType);
@ -22,6 +27,24 @@ public sealed interface RecipeDisplay {
SlotDisplay.NETWORK_TYPE, CraftingShapeless::result,
SlotDisplay.NETWORK_TYPE, CraftingShapeless::craftingStation,
CraftingShapeless::new);
@Override
public @NotNull Collection<Component> components() {
final var components = new ArrayList<Component>();
for (SlotDisplay ingredient : ingredients)
components.addAll(ingredient.components());
components.addAll(result.components());
components.addAll(craftingStation.components());
return List.copyOf(components);
}
@Override
public @NotNull RecipeDisplay copyWithOperator(@NotNull UnaryOperator<Component> operator) {
final var newIngredients = new ArrayList<SlotDisplay>();
for (SlotDisplay ingredient : ingredients)
newIngredients.add(ingredient.copyWithOperator(operator));
return new CraftingShapeless(newIngredients, result.copyWithOperator(operator), craftingStation.copyWithOperator(operator));
}
}
record CraftingShaped(
@ -45,6 +68,24 @@ public sealed interface RecipeDisplay {
throw new IllegalArgumentException("Invalid shaped recipe, ingredients size must be equal to width * height");
ingredients = List.copyOf(ingredients);
}
@Override
public @NotNull Collection<Component> components() {
final var components = new ArrayList<Component>();
for (SlotDisplay ingredient : ingredients)
components.addAll(ingredient.components());
components.addAll(result.components());
components.addAll(craftingStation.components());
return List.copyOf(components);
}
@Override
public @NotNull RecipeDisplay copyWithOperator(@NotNull UnaryOperator<Component> operator) {
final var newIngredients = new ArrayList<SlotDisplay>();
for (SlotDisplay ingredient : ingredients)
newIngredients.add(ingredient.copyWithOperator(operator));
return new CraftingShaped(width, height, newIngredients, result.copyWithOperator(operator), craftingStation.copyWithOperator(operator));
}
}
record Furnace(
@ -62,6 +103,23 @@ public sealed interface RecipeDisplay {
NetworkBuffer.VAR_INT, Furnace::duration,
NetworkBuffer.FLOAT, Furnace::experience,
Furnace::new);
@Override
public @NotNull Collection<Component> components() {
final var components = new ArrayList<Component>();
components.addAll(ingredient.components());
components.addAll(fuel.components());
components.addAll(result.components());
components.addAll(craftingStation.components());
return List.copyOf(components);
}
@Override
public @NotNull RecipeDisplay copyWithOperator(@NotNull UnaryOperator<Component> operator) {
return new Furnace(ingredient.copyWithOperator(operator), fuel.copyWithOperator(operator),
result.copyWithOperator(operator), craftingStation.copyWithOperator(operator),
duration, experience);
}
}
record Stonecutter(
@ -74,6 +132,21 @@ public sealed interface RecipeDisplay {
SlotDisplay.NETWORK_TYPE, Stonecutter::result,
SlotDisplay.NETWORK_TYPE, Stonecutter::craftingStation,
Stonecutter::new);
@Override
public @NotNull Collection<Component> components() {
final var components = new ArrayList<Component>();
components.addAll(ingredient.components());
components.addAll(result.components());
components.addAll(craftingStation.components());
return List.copyOf(components);
}
@Override
public @NotNull RecipeDisplay copyWithOperator(@NotNull UnaryOperator<Component> operator) {
return new Stonecutter(ingredient.copyWithOperator(operator), result.copyWithOperator(operator),
craftingStation.copyWithOperator(operator));
}
}
record Smithing(
@ -90,6 +163,23 @@ public sealed interface RecipeDisplay {
SlotDisplay.NETWORK_TYPE, Smithing::result,
SlotDisplay.NETWORK_TYPE, Smithing::craftingStation,
Smithing::new);
@Override
public @NotNull Collection<Component> components() {
final var components = new ArrayList<Component>();
components.addAll(template.components());
components.addAll(base.components());
components.addAll(addition.components());
components.addAll(result.components());
components.addAll(craftingStation.components());
return List.copyOf(components);
}
@Override
public @NotNull RecipeDisplay copyWithOperator(@NotNull UnaryOperator<Component> operator) {
return new Smithing(template.copyWithOperator(operator), base.copyWithOperator(operator),
addition.copyWithOperator(operator), result.copyWithOperator(operator), craftingStation.copyWithOperator(operator));
}
}
@SuppressWarnings({"unchecked", "rawtypes"})

View File

@ -1,14 +1,19 @@
package net.minestom.server.recipe.display;
import net.kyori.adventure.text.Component;
import net.minestom.server.adventure.ComponentHolder;
import net.minestom.server.item.Material;
import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.NetworkBufferTemplate;
import net.minestom.server.utils.Unit;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.UnaryOperator;
public sealed interface SlotDisplay {
public sealed interface SlotDisplay extends ComponentHolder<SlotDisplay> {
@NotNull NetworkBuffer.Type<SlotDisplay> NETWORK_TYPE = SlotDisplayType.NETWORK_TYPE
.unionType(SlotDisplay::dataSerializer, SlotDisplay::slotDisplayToType);
@ -41,6 +46,16 @@ public sealed interface SlotDisplay {
public static final NetworkBuffer.Type<ItemStack> NETWORK_TYPE = NetworkBufferTemplate.template(
net.minestom.server.item.ItemStack.STRICT_NETWORK_TYPE, ItemStack::itemStack,
ItemStack::new);
@Override
public @NotNull Collection<Component> components() {
return net.minestom.server.item.ItemStack.textComponents(itemStack);
}
@Override
public @NotNull SlotDisplay copyWithOperator(@NotNull UnaryOperator<Component> operator) {
return new ItemStack(net.minestom.server.item.ItemStack.copyWithOperator(itemStack, operator));
}
}
record Tag(@NotNull String tagKey) implements SlotDisplay {
@ -59,6 +74,21 @@ public sealed interface SlotDisplay {
SlotDisplay.NETWORK_TYPE, SmithingTrim::trimMaterial,
SlotDisplay.NETWORK_TYPE, SmithingTrim::trimPattern,
SmithingTrim::new);
@Override
public @NotNull Collection<Component> components() {
final var components = new ArrayList<>(base.components());
components.addAll(trimMaterial.components());
components.addAll(trimPattern.components());
return List.copyOf(components);
}
@Override
public @NotNull SlotDisplay copyWithOperator(@NotNull UnaryOperator<Component> operator) {
return new SmithingTrim(base.copyWithOperator(operator),
trimMaterial.copyWithOperator(operator),
trimPattern.copyWithOperator(operator));
}
}
record WithRemainder(@NotNull SlotDisplay input, @NotNull SlotDisplay remainder) implements SlotDisplay {
@ -66,6 +96,18 @@ public sealed interface SlotDisplay {
SlotDisplay.NETWORK_TYPE, WithRemainder::input,
SlotDisplay.NETWORK_TYPE, WithRemainder::remainder,
WithRemainder::new);
@Override
public @NotNull Collection<Component> components() {
final var components = new ArrayList<>(input.components());
components.addAll(remainder.components());
return List.copyOf(components);
}
@Override
public @NotNull SlotDisplay copyWithOperator(@NotNull UnaryOperator<Component> operator) {
return new WithRemainder(input.copyWithOperator(operator), remainder.copyWithOperator(operator));
}
}
record Composite(@NotNull List<SlotDisplay> contents) implements SlotDisplay {
@ -76,6 +118,32 @@ public sealed interface SlotDisplay {
public Composite {
contents = List.copyOf(contents);
}
@Override
public @NotNull Collection<Component> components() {
final var components = new ArrayList<Component>();
for (var display : contents)
components.addAll(display.components());
return List.copyOf(components);
}
@Override
public @NotNull SlotDisplay copyWithOperator(@NotNull UnaryOperator<Component> operator) {
final var newContents = new ArrayList<SlotDisplay>();
for (var display : contents)
newContents.add(display.copyWithOperator(operator));
return new Composite(newContents);
}
}
@Override
default @NotNull Collection<Component> components() {
return List.of();
}
@Override
default @NotNull SlotDisplay copyWithOperator(@NotNull UnaryOperator<Component> operator) {
return this;
}
private static NetworkBuffer.Type<SlotDisplay> dataSerializer(@NotNull SlotDisplayType type) {