1.20.4 Support

This commit is contained in:
NichtStudioCode 2023-12-17 09:50:37 +01:00
parent 21b8babc31
commit 49b3a74fc7
8 changed files with 587 additions and 0 deletions

@ -0,0 +1,189 @@
package xyz.xenondevs.inventoryaccess.r18;
import net.minecraft.core.BlockPos;
import net.minecraft.core.NonNullList;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket;
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.AnvilMenu;
import net.minecraft.world.inventory.ContainerLevelAccess;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.item.ItemStack;
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_R3.event.CraftEventFactory;
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryAnvil;
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryView;
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack;
import org.bukkit.event.inventory.PrepareAnvilEvent;
import org.bukkit.inventory.Inventory;
import org.jetbrains.annotations.NotNull;
import xyz.xenondevs.inventoryaccess.abstraction.inventory.AnvilInventory;
import xyz.xenondevs.inventoryaccess.component.ComponentWrapper;
import java.util.List;
import java.util.function.Consumer;
class AnvilInventoryImpl extends AnvilMenu implements AnvilInventory {
private final List<Consumer<String>> renameHandlers;
private final CraftInventoryView view;
private final ServerPlayer player;
private String text;
private boolean open;
public AnvilInventoryImpl(org.bukkit.entity.Player player, @NotNull ComponentWrapper title, List<Consumer<String>> renameHandlers) {
this(((CraftPlayer) player).getHandle(), InventoryUtilsImpl.createNMSComponent(title), renameHandlers);
}
public AnvilInventoryImpl(ServerPlayer player, Component title, List<Consumer<String>> renameHandlers) {
super(player.nextContainerCounter(), player.getInventory(),
ContainerLevelAccess.create(player.level(), new BlockPos(0, 0, 0)));
setTitle(title);
this.renameHandlers = renameHandlers;
this.player = player;
CraftInventoryAnvil inventory = new CraftInventoryAnvil(access.getLocation(),
inputSlots, resultSlots, this);
this.view = new CraftInventoryView(player.getBukkitEntity(), inventory, this);
}
public void open() {
open = true;
// call the InventoryOpenEvent
CraftEventFactory.callInventoryOpenEvent(player, this);
// set active container
player.containerMenu = this;
// send open packet
player.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.ANVIL, getTitle()));
// send initial items
NonNullList<ItemStack> itemsList = NonNullList.of(ItemStack.EMPTY, getItem(0), getItem(1), getItem(2));
player.connection.send(new ClientboundContainerSetContentPacket(getActiveWindowId(player), incrementStateId(), itemsList, ItemStack.EMPTY));
// init menu
player.initMenu(this);
}
public void sendItem(int slot) {
player.connection.send(new ClientboundContainerSetSlotPacket(getActiveWindowId(player), incrementStateId(), slot, getItem(slot)));
}
public void setItem(int slot, ItemStack item) {
if (slot < 2) inputSlots.setItem(slot, item);
else resultSlots.setItem(0, item);
if (open) sendItem(slot);
}
private ItemStack getItem(int slot) {
if (slot < 2) return inputSlots.getItem(slot);
else return resultSlots.getItem(0);
}
private int getActiveWindowId(ServerPlayer player) {
AbstractContainerMenu container = player.containerMenu;
return container == null ? -1 : container.containerId;
}
@Override
public void setItem(int slot, org.bukkit.inventory.ItemStack itemStack) {
setItem(slot, CraftItemStack.asNMSCopy(itemStack));
}
@Override
public @NotNull Inventory getBukkitInventory() {
return view.getTopInventory();
}
@Override
public String getRenameText() {
return text;
}
@Override
public boolean isOpen() {
return open;
}
// --- AnvilMenu ---
@Override
public CraftInventoryView getBukkitView() {
return view;
}
/**
* Called every tick to see if the {@link Player} can still use that container.
* (Used to for checking the distance between the {@link Player} and the container
* and closing the window when the distance gets too big.)
*
* @param player The {@link Player}
* @return If the {@link Player} can still use that container
*/
@Override
public boolean stillValid(Player player) {
return true;
}
/**
* Called when the rename text gets changed.
*
* @param s The new rename text
*/
@Override
public boolean setItemName(String s) {
// save rename text
text = s;
// call rename handlers
if (renameHandlers != null)
renameHandlers.forEach(handler -> handler.accept(s));
// the client expects the item to change to its new name and removes it from the inventory, so it needs to be sent again
sendItem(2);
return false;
}
/**
* Called when the container is closed to give the items back.
*
* @param player The {@link Player} that closed this container
*/
@Override
public void removed(Player player) {
open = false;
}
/**
* Called when the container gets closed to put items back into a players
* inventory or drop them in the world.
*
* @param player The {@link Player} that closed this container
* @param container The container
*/
@Override
protected void clearContainer(Player player, Container container) {
open = false;
}
/**
* Called when both items in the {@link AnvilMenu#inputSlots} were set to create
* the resulting product, calculate the level cost and call the {@link PrepareAnvilEvent}.
*/
@Override
public void createResult() {
// empty
}
}

@ -0,0 +1,137 @@
package xyz.xenondevs.inventoryaccess.r18;
import net.minecraft.core.BlockPos;
import net.minecraft.core.NonNullList;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket;
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.*;
import net.minecraft.world.item.ItemStack;
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_R3.event.CraftEventFactory;
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryCartography;
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventoryView;
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack;
import org.bukkit.inventory.Inventory;
import org.jetbrains.annotations.NotNull;
import xyz.xenondevs.inventoryaccess.abstraction.inventory.CartographyInventory;
import xyz.xenondevs.inventoryaccess.component.ComponentWrapper;
import xyz.xenondevs.inventoryaccess.util.ReflectionUtils;
import java.lang.reflect.Field;
class CartographyInventoryImpl extends CartographyTableMenu implements CartographyInventory {
private static final Field RESULT_CONTAINER_FIELD = ReflectionUtils.getField(
CartographyTableMenu.class,
true,
"SRF(net.minecraft.world.inventory.CartographyTableMenu resultContainer)"
);
private final ResultContainer resultContainer = ReflectionUtils.getFieldValue(RESULT_CONTAINER_FIELD, this);
private final Component title;
private final CraftInventoryView view;
private final ServerPlayer player;
private boolean open;
public CartographyInventoryImpl(org.bukkit.entity.Player player, @NotNull ComponentWrapper title) {
this(((CraftPlayer) player).getHandle(), InventoryUtilsImpl.createNMSComponent(title));
}
public CartographyInventoryImpl(ServerPlayer player, Component title) {
super(player.nextContainerCounter(), player.getInventory(), ContainerLevelAccess.create(player.level(), new BlockPos(0, 0, 0)));
this.player = player;
this.title = title;
CraftInventoryCartography inventory = new CraftInventoryCartography(container, resultContainer);
view = new CraftInventoryView(player.getBukkitEntity(), inventory, this);
}
public void open() {
open = true;
// call the InventoryOpenEvent
CraftEventFactory.callInventoryOpenEvent(player, this);
// set active container
player.containerMenu = this;
// send open packet
player.connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.CARTOGRAPHY_TABLE, title));
// send initial items
NonNullList<ItemStack> itemsList = NonNullList.of(ItemStack.EMPTY, getItem(0), getItem(1), getItem(2));
player.connection.send(new ClientboundContainerSetContentPacket(InventoryUtilsImpl.getActiveWindowId(player), incrementStateId(), itemsList, ItemStack.EMPTY));
// init menu
player.initMenu(this);
}
@Override
public boolean isOpen() {
return open;
}
public void sendItem(int slot) {
player.connection.send(new ClientboundContainerSetSlotPacket(InventoryUtilsImpl.getActiveWindowId(player), slot, incrementStateId(), getItem(slot)));
}
public void setItem(int slot, ItemStack item) {
if (slot < 2) container.setItem(slot, item);
else resultContainer.setItem(0, item);
if (open) sendItem(slot);
}
private ItemStack getItem(int slot) {
if (slot < 2) return container.getItem(slot);
else return resultContainer.getItem(0);
}
@Override
public void setItem(int slot, org.bukkit.inventory.ItemStack itemStack) {
setItem(slot, CraftItemStack.asNMSCopy(itemStack));
}
@Override
public Inventory getBukkitInventory() {
return view.getTopInventory();
}
// --- CartographyTableMenu ---
@Override
public CraftInventoryView getBukkitView() {
return view;
}
@Override
public void slotsChanged(Container container) {
}
@Override
public ItemStack quickMoveStack(Player player, int i) {
return ItemStack.EMPTY;
}
@Override
public boolean canTakeItemForPickAll(ItemStack itemstack, Slot slot) {
return true;
}
@Override
public boolean stillValid(Player player) {
return true;
}
@Override
protected void clearContainer(Player player, Container container) {
// empty
}
}

@ -0,0 +1,73 @@
package xyz.xenondevs.inventoryaccess.r18;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType;
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_R3.event.CraftEventFactory;
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftContainer;
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftInventory;
import org.bukkit.craftbukkit.v1_20_R3.util.CraftChatMessage;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import xyz.xenondevs.inventoryaccess.abstraction.util.InventoryUtils;
import xyz.xenondevs.inventoryaccess.component.ComponentWrapper;
class InventoryUtilsImpl implements InventoryUtils {
public static Component createNMSComponent(ComponentWrapper component) {
if (component == null) return null;
return CraftChatMessage.fromJSON(component.serializeToJson());
}
public static int getActiveWindowId(ServerPlayer player) {
AbstractContainerMenu container = player.containerMenu;
return container == null ? -1 : container.containerId;
}
@Override
public void openCustomInventory(@NotNull Player player, @NotNull Inventory inventory) {
openCustomInventory(player, inventory, null);
}
@Override
public void openCustomInventory(@NotNull Player player, @NotNull Inventory inventory, @Nullable ComponentWrapper title) {
ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle();
MenuType<?> menuType = CraftContainer.getNotchInventoryType(inventory);
if (serverPlayer.connection != null) {
AbstractContainerMenu menu = new CraftContainer(inventory, serverPlayer, serverPlayer.nextContainerCounter());
menu = CraftEventFactory.callInventoryOpenEvent(serverPlayer, menu);
if (menu != null) {
Container container = ((CraftInventory) inventory).getInventory();
Component titleComponent;
if (title == null) {
if (container instanceof MenuProvider)
titleComponent = ((MenuProvider) container).getDisplayName();
else titleComponent = CraftChatMessage.fromString(menu.getBukkitView().getTitle())[0];
} else titleComponent = createNMSComponent(title);
menu.checkReachable = false;
serverPlayer.connection.send(new ClientboundOpenScreenPacket(menu.containerId, menuType, titleComponent));
serverPlayer.containerMenu = menu;
serverPlayer.initMenu(menu);
}
}
}
@Override
public void updateOpenInventoryTitle(@NotNull Player player, @NotNull ComponentWrapper title) {
ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle();
AbstractContainerMenu menu = serverPlayer.containerMenu;
serverPlayer.connection.send(new ClientboundOpenScreenPacket(menu.containerId, menu.getType(), createNMSComponent(title)));
serverPlayer.initMenu(menu);
}
}

@ -0,0 +1,98 @@
package xyz.xenondevs.inventoryaccess.r18;
import com.mojang.authlib.GameProfile;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtAccounter;
import net.minecraft.nbt.NbtIo;
import net.minecraft.world.item.ItemStack;
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import xyz.xenondevs.inventoryaccess.abstraction.util.ItemUtils;
import xyz.xenondevs.inventoryaccess.component.ComponentWrapper;
import xyz.xenondevs.inventoryaccess.util.ReflectionRegistry;
import xyz.xenondevs.inventoryaccess.util.ReflectionUtils;
import java.io.*;
import java.lang.reflect.Method;
import java.util.List;
import java.util.stream.Collectors;
class ItemUtilsImpl implements ItemUtils {
private static final Method CRAFT_META_SKULL_SET_PROFILE_METHOD = ReflectionUtils.getMethod(
ReflectionRegistry.CB_CRAFT_META_SKULL_CLASS, true, "setProfile", GameProfile.class
);
@Override
public byte[] serializeItemStack(org.bukkit.inventory.@NotNull ItemStack itemStack, boolean compressed) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
serializeItemStack(itemStack, out, compressed);
return out.toByteArray();
}
@Override
public void serializeItemStack(org.bukkit.inventory.@NotNull ItemStack itemStack, @NotNull OutputStream outputStream, boolean compressed) {
try {
ItemStack nmsStack = CraftItemStack.asNMSCopy(itemStack);
CompoundTag nbt = nmsStack.save(new CompoundTag());
if (compressed) {
NbtIo.writeCompressed(nbt, outputStream);
} else {
DataOutputStream dataOut = new DataOutputStream(outputStream);
NbtIo.write(nbt, dataOut);
}
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public org.bukkit.inventory.ItemStack deserializeItemStack(byte[] data, boolean compressed) {
ByteArrayInputStream in = new ByteArrayInputStream(data);
return deserializeItemStack(in, compressed);
}
@Override
public org.bukkit.inventory.ItemStack deserializeItemStack(@NotNull InputStream inputStream, boolean compressed) {
try {
CompoundTag nbt;
if (compressed) {
nbt = NbtIo.readCompressed(inputStream, NbtAccounter.unlimitedHeap());
} else {
DataInputStream dataIn = new DataInputStream(inputStream);
nbt = NbtIo.read(dataIn);
}
ItemStack itemStack = ItemStack.of(nbt);
return CraftItemStack.asCraftMirror(itemStack);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
public void setDisplayName(@NotNull ItemMeta itemMeta, @NotNull ComponentWrapper name) {
ReflectionUtils.setFieldValue(
ReflectionRegistry.CB_CRAFT_META_ITEM_DISPLAY_NAME_FIELD,
itemMeta,
name.serializeToJson()
);
}
@Override
public void setLore(@NotNull ItemMeta itemMeta, @NotNull List<@NotNull ComponentWrapper> lore) {
ReflectionUtils.setFieldValue(
ReflectionRegistry.CB_CRAFT_META_ITEM_LORE_FIELD,
itemMeta,
lore.stream().map(ComponentWrapper::serializeToJson).collect(Collectors.toList())
);
}
}

@ -0,0 +1,83 @@
package xyz.xenondevs.inventoryaccess.r18;
import net.minecraft.network.protocol.game.ClientboundMapItemDataPacket;
import net.minecraft.server.PlayerAdvancements;
import net.minecraft.server.ServerAdvancementManager;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.saveddata.maps.MapDecoration;
import net.minecraft.world.level.saveddata.maps.MapItemSavedData;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_20_R3.CraftServer;
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import xyz.xenondevs.inventoryaccess.abstraction.util.PlayerUtils;
import xyz.xenondevs.inventoryaccess.map.MapIcon;
import xyz.xenondevs.inventoryaccess.map.MapPatch;
import xyz.xenondevs.inventoryaccess.util.ReflectionUtils;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
class PlayerUtilsImpl implements PlayerUtils {
private static final Method REGISTER_LISTENERS_METHOD = ReflectionUtils.getMethod(
PlayerAdvancements.class,
true,
"ASRM(net/minecraft/server/PlayerAdvancements.registerListeners(Lnet/minecraft/server/ServerAdvancementManager;)V)",
ServerAdvancementManager.class
);
@Override
public void stopAdvancementListening(@NotNull Player player) {
stopAdvancementListening(((CraftPlayer) player).getHandle());
}
@Override
public void stopAdvancementListening(@NotNull Object player) {
((ServerPlayer) player).getAdvancements().stopListening();
}
@Override
public void startAdvancementListening(@NotNull Player player) {
startAdvancementListening(((CraftPlayer) player).getHandle());
}
@Override
public void startAdvancementListening(@NotNull Object player) {
PlayerAdvancements advancements = ((ServerPlayer) player).getAdvancements();
ServerAdvancementManager manager = ((CraftServer) Bukkit.getServer()).getServer().getAdvancements();
ReflectionUtils.invokeMethod(REGISTER_LISTENERS_METHOD, advancements, manager);
}
@Override
public void sendMapUpdate(@NotNull Player player, int mapId, byte scale, boolean locked, @Nullable MapPatch mapPatch, @Nullable List<MapIcon> icons) {
List<MapDecoration> decorations = icons != null ? icons.stream().map(this::toMapDecoration).collect(Collectors.toCollection(ArrayList::new)) : null;
MapItemSavedData.MapPatch patch = toMapPatch(mapPatch);
ClientboundMapItemDataPacket packet = new ClientboundMapItemDataPacket(mapId, scale, locked, decorations, patch);
((CraftPlayer) player).getHandle().connection.send(packet);
}
private MapDecoration toMapDecoration(MapIcon icon) {
return new MapDecoration(
MapDecoration.Type.byIcon(icon.getType().getId()),
icon.getX(), icon.getY(),
icon.getRot(),
icon.getComponent() != null ? InventoryUtilsImpl.createNMSComponent(icon.getComponent()) : null
);
}
private MapItemSavedData.MapPatch toMapPatch(MapPatch patch) {
if (patch == null) return null;
return new MapItemSavedData.MapPatch(
patch.getStartX(), patch.getStartY(),
patch.getWidth(), patch.getHeight(),
patch.getColors()
);
}
}

@ -5,6 +5,7 @@ import xyz.xenondevs.inventoryaccess.util.VersionUtils;
public enum InventoryAccessRevision {
// this order is required
R18("r18", "1.20.3"),
R17("r17", "1.20.2"),
R16("r16", "1.20.0"),
R15("r15", "1.19.4"),

@ -108,6 +108,11 @@
<artifactId>inventory-access-r17</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>xyz.xenondevs.invui</groupId>
<artifactId>inventory-access-r18</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

@ -74,6 +74,7 @@
<module>inventoryaccess/inventory-access-r15</module>
<module>inventoryaccess/inventory-access-r16</module>
<module>inventoryaccess/inventory-access-r17</module>
<module>inventoryaccess/inventory-access-r18</module>
<module>invui-core</module>
<module>invui-kotlin</module>
<module>invui</module>