diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 03a8c83599..ab15839af1 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -40,6 +40,9 @@ dependencies { testImplementation(testlibs.bundles.junit) testImplementation(libs.netty) testImplementation(libs.classgraph) + testImplementation(project(":spigot")) + testImplementation("org.junit.jupiter:junit-jupiter-api:5.11.2") + testImplementation("org.junit.jupiter:junit-jupiter-params:5.11.2") } mappingCompression { diff --git a/api/src/main/java/com/github/retrooper/packetevents/PacketEventsAPI.java b/api/src/main/java/com/github/retrooper/packetevents/PacketEventsAPI.java index 7a8229f408..2df543d601 100644 --- a/api/src/main/java/com/github/retrooper/packetevents/PacketEventsAPI.java +++ b/api/src/main/java/com/github/retrooper/packetevents/PacketEventsAPI.java @@ -22,8 +22,11 @@ import com.github.retrooper.packetevents.injector.ChannelInjector; import com.github.retrooper.packetevents.manager.player.PlayerManager; import com.github.retrooper.packetevents.manager.protocol.ProtocolManager; +import com.github.retrooper.packetevents.manager.registry.ItemRegistry; +import com.github.retrooper.packetevents.manager.registry.RegistryManager; import com.github.retrooper.packetevents.manager.server.ServerManager; import com.github.retrooper.packetevents.netty.NettyManager; +import com.github.retrooper.packetevents.protocol.item.type.ItemType; import com.github.retrooper.packetevents.settings.PacketEventsSettings; import com.github.retrooper.packetevents.util.LogManager; import com.github.retrooper.packetevents.util.PEVersion; @@ -32,6 +35,7 @@ import com.github.retrooper.packetevents.util.updatechecker.UpdateChecker; import java.util.logging.Logger; +import org.jetbrains.annotations.Nullable; public abstract class PacketEventsAPI { private final EventManager eventManager = new EventManager(); @@ -88,4 +92,23 @@ public LogManager getLogManager() { public abstract NettyManager getNettyManager(); public abstract ChannelInjector getInjector(); + + public RegistryManager getRegistryManager() { + return new RegistryManager() { + @Override + public ItemRegistry getItemRegistry() { + return new ItemRegistry() { + @Override + public @Nullable ItemType getByName(String name) { + return null; + } + + @Override + public @Nullable ItemType getById(int id) { + return null; + } + }; + } + }; + } } diff --git a/api/src/main/java/com/github/retrooper/packetevents/manager/registry/ItemRegistry.java b/api/src/main/java/com/github/retrooper/packetevents/manager/registry/ItemRegistry.java new file mode 100644 index 0000000000..115e29796c --- /dev/null +++ b/api/src/main/java/com/github/retrooper/packetevents/manager/registry/ItemRegistry.java @@ -0,0 +1,9 @@ +package com.github.retrooper.packetevents.manager.registry; + +import com.github.retrooper.packetevents.protocol.item.type.ItemType; +import org.jetbrains.annotations.Nullable; + +public interface ItemRegistry { + @Nullable ItemType getByName(String name); + @Nullable ItemType getById(int id); +} diff --git a/api/src/main/java/com/github/retrooper/packetevents/manager/registry/RegistryManager.java b/api/src/main/java/com/github/retrooper/packetevents/manager/registry/RegistryManager.java new file mode 100644 index 0000000000..fff24bf007 --- /dev/null +++ b/api/src/main/java/com/github/retrooper/packetevents/manager/registry/RegistryManager.java @@ -0,0 +1,5 @@ +package com.github.retrooper.packetevents.manager.registry; + +public interface RegistryManager { + ItemRegistry getItemRegistry(); +} diff --git a/api/src/main/java/com/github/retrooper/packetevents/protocol/item/type/ItemTypes.java b/api/src/main/java/com/github/retrooper/packetevents/protocol/item/type/ItemTypes.java index 568c42daa3..564ff1fc29 100644 --- a/api/src/main/java/com/github/retrooper/packetevents/protocol/item/type/ItemTypes.java +++ b/api/src/main/java/com/github/retrooper/packetevents/protocol/item/type/ItemTypes.java @@ -18,6 +18,7 @@ package com.github.retrooper.packetevents.protocol.item.type; +import com.github.retrooper.packetevents.PacketEvents; import com.github.retrooper.packetevents.netty.buffer.ByteBufHelper; import com.github.retrooper.packetevents.netty.buffer.UnpooledByteBufAllocationHelper; import com.github.retrooper.packetevents.protocol.component.ComponentType; @@ -1638,11 +1639,19 @@ public static ItemType define(int maxAmount, String key, ItemType craftRemainder } public static @Nullable ItemType getByName(String name) { - return REGISTRY.getByName(name); + ItemType itemType = REGISTRY.getByName(name); + if (itemType == null) { + return PacketEvents.getAPI().getRegistryManager().getItemRegistry().getByName(name); + } + return itemType; } public static @Nullable ItemType getById(ClientVersion version, int id) { - return REGISTRY.getById(version, id); + ItemType itemType = REGISTRY.getById(version, id); + if (itemType == null) { + return PacketEvents.getAPI().getRegistryManager().getItemRegistry().getById(id); + } + return itemType; } public static ItemType getTypePlacingState(StateType type) { diff --git a/api/src/main/java/com/github/retrooper/packetevents/protocol/world/states/defaulttags/BlockTags.java b/api/src/main/java/com/github/retrooper/packetevents/protocol/world/states/defaulttags/BlockTags.java index 45bfe0449e..92ea8c86aa 100644 --- a/api/src/main/java/com/github/retrooper/packetevents/protocol/world/states/defaulttags/BlockTags.java +++ b/api/src/main/java/com/github/retrooper/packetevents/protocol/world/states/defaulttags/BlockTags.java @@ -335,6 +335,30 @@ public class BlockTags { */ public static final BlockTags DEAD_CORAL_PLANTS = bind("dead_coral_plants"); + /** + * Unofficial tag for all blocks added in 1.20.5 + */ + @VisibleForTesting @ApiStatus.Internal + public static final BlockTags V_1_20_5 = bind("V_1_20_5"); + + /** + * Unofficial tag for all blocks added in 1.21.2 + */ + @VisibleForTesting @ApiStatus.Internal + public static final BlockTags V_1_21_2 = bind("V_1_21_2"); + + /** + * Unofficial tag for all blocks added in 1.21.4 + */ + @VisibleForTesting @ApiStatus.Internal + public static final BlockTags V_1_21_4 = bind("V_1_21_4"); + + /** + * Unofficial tag for all blocks added in 1.21.5 + */ + @VisibleForTesting @ApiStatus.Internal + public static final BlockTags V_1_21_5 = bind("V_1_21_5"); + static { BlockTags.WOOL.add(StateTypes.WHITE_WOOL, StateTypes.ORANGE_WOOL, StateTypes.MAGENTA_WOOL, StateTypes.LIGHT_BLUE_WOOL, StateTypes.YELLOW_WOOL, StateTypes.LIME_WOOL, StateTypes.PINK_WOOL, StateTypes.GRAY_WOOL, StateTypes.LIGHT_GRAY_WOOL, StateTypes.CYAN_WOOL, StateTypes.PURPLE_WOOL, StateTypes.BLUE_WOOL, StateTypes.BROWN_WOOL, StateTypes.GREEN_WOOL, StateTypes.RED_WOOL, StateTypes.BLACK_WOOL); BlockTags.PLANKS.add(StateTypes.OAK_PLANKS, StateTypes.SPRUCE_PLANKS, StateTypes.BIRCH_PLANKS, StateTypes.JUNGLE_PLANKS, StateTypes.ACACIA_PLANKS, StateTypes.DARK_OAK_PLANKS, StateTypes.PALE_OAK_PLANKS, StateTypes.CRIMSON_PLANKS, StateTypes.WARPED_PLANKS, StateTypes.MANGROVE_PLANKS, StateTypes.BAMBOO_PLANKS, StateTypes.CHERRY_PLANKS); @@ -536,6 +560,10 @@ public class BlockTags { BlockTags.GLASS_PANES.add(StateTypes.GLASS_PANE, StateTypes.WHITE_STAINED_GLASS_PANE, StateTypes.ORANGE_STAINED_GLASS_PANE, StateTypes.MAGENTA_STAINED_GLASS_PANE, StateTypes.LIGHT_BLUE_STAINED_GLASS_PANE, StateTypes.YELLOW_STAINED_GLASS_PANE, StateTypes.LIME_STAINED_GLASS_PANE, StateTypes.PINK_STAINED_GLASS_PANE, StateTypes.GRAY_STAINED_GLASS_PANE, StateTypes.LIGHT_GRAY_STAINED_GLASS_PANE, StateTypes.CYAN_STAINED_GLASS_PANE, StateTypes.PURPLE_STAINED_GLASS_PANE, StateTypes.BLUE_STAINED_GLASS_PANE, StateTypes.BROWN_STAINED_GLASS_PANE, StateTypes.GREEN_STAINED_GLASS_PANE, StateTypes.RED_STAINED_GLASS_PANE, StateTypes.BLACK_STAINED_GLASS_PANE); BlockTags.ALL_CORAL_PLANTS.add(StateTypes.TUBE_CORAL, StateTypes.BRAIN_CORAL, StateTypes.BUBBLE_CORAL, StateTypes.FIRE_CORAL, StateTypes.HORN_CORAL, StateTypes.DEAD_TUBE_CORAL, StateTypes.DEAD_BRAIN_CORAL, StateTypes.DEAD_BUBBLE_CORAL, StateTypes.DEAD_FIRE_CORAL, StateTypes.DEAD_HORN_CORAL); BlockTags.DEAD_CORAL_PLANTS.add(StateTypes.DEAD_TUBE_CORAL, StateTypes.DEAD_BRAIN_CORAL, StateTypes.DEAD_BUBBLE_CORAL, StateTypes.DEAD_FIRE_CORAL, StateTypes.DEAD_HORN_CORAL); + BlockTags.V_1_20_5.add(StateTypes.VAULT, StateTypes.HEAVY_CORE); + BlockTags.V_1_21_2.add(StateTypes.PALE_OAK_WOOD, StateTypes.PALE_OAK_PLANKS, StateTypes.PALE_OAK_SAPLING, StateTypes.PALE_OAK_LOG, StateTypes.STRIPPED_PALE_OAK_LOG, StateTypes.STRIPPED_PALE_OAK_WOOD, StateTypes.PALE_OAK_LEAVES, StateTypes.CREAKING_HEART, StateTypes.PALE_OAK_SIGN, StateTypes.PALE_OAK_WALL_SIGN, StateTypes.PALE_OAK_HANGING_SIGN, StateTypes.PALE_OAK_WALL_HANGING_SIGN, StateTypes.PALE_OAK_PRESSURE_PLATE, StateTypes.PALE_OAK_TRAPDOOR, StateTypes.POTTED_PALE_OAK_SAPLING, StateTypes.PALE_OAK_BUTTON, StateTypes.PALE_OAK_STAIRS, StateTypes.PALE_OAK_SLAB, StateTypes.PALE_OAK_FENCE_GATE, StateTypes.PALE_OAK_FENCE, StateTypes.PALE_OAK_DOOR, StateTypes.PALE_MOSS_BLOCK, StateTypes.PALE_MOSS_CARPET, StateTypes.PALE_HANGING_MOSS); + BlockTags.V_1_21_4.add(StateTypes.RESIN_CLUMP, StateTypes.RESIN_BLOCK, StateTypes.RESIN_BRICKS, StateTypes.RESIN_BRICK_STAIRS, StateTypes.RESIN_BRICK_SLAB, StateTypes.RESIN_BRICK_WALL, StateTypes.CHISELED_RESIN_BRICKS, StateTypes.OPEN_EYEBLOSSOM, StateTypes.CLOSED_EYEBLOSSOM, StateTypes.POTTED_OPEN_EYEBLOSSOM, StateTypes.POTTED_CLOSED_EYEBLOSSOM); + BlockTags.V_1_21_5.add(StateTypes.BUSH, StateTypes.FIREFLY_BUSH, StateTypes.SHORT_DRY_GRASS, StateTypes.TALL_DRY_GRASS, StateTypes.WILDFLOWERS, StateTypes.LEAF_LITTER, StateTypes.CACTUS_FLOWER, StateTypes.TEST_BLOCK, StateTypes.TEST_INSTANCE_BLOCK); } String name; diff --git a/api/src/main/java/com/github/retrooper/packetevents/wrapper/play/server/WrapperPlayServerMapData.java b/api/src/main/java/com/github/retrooper/packetevents/wrapper/play/server/WrapperPlayServerMapData.java index efcde021af..e99bafee14 100644 --- a/api/src/main/java/com/github/retrooper/packetevents/wrapper/play/server/WrapperPlayServerMapData.java +++ b/api/src/main/java/com/github/retrooper/packetevents/wrapper/play/server/WrapperPlayServerMapData.java @@ -1,302 +1,302 @@ -/* - * This file is part of packetevents - https://github.com/retrooper/packetevents - * Copyright (C) 2024 retrooper and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.github.retrooper.packetevents.wrapper.play.server; - -import com.github.retrooper.packetevents.event.PacketSendEvent; -import com.github.retrooper.packetevents.manager.server.ServerVersion; -import com.github.retrooper.packetevents.protocol.item.mapdecoration.MapDecorationType; -import com.github.retrooper.packetevents.protocol.item.mapdecoration.MapDecorationTypes; -import com.github.retrooper.packetevents.protocol.packettype.PacketType; -import com.github.retrooper.packetevents.wrapper.PacketWrapper; -import net.kyori.adventure.text.Component; -import org.jetbrains.annotations.Nullable; - -import java.util.List; - -public class WrapperPlayServerMapData extends PacketWrapper { - - private int mapId; - private byte scale; - private boolean trackingPosition; - private boolean locked; - private @Nullable List decorations; - private int columns; - private int rows; - private int x; - private int z; - private byte @Nullable [] data; - - public WrapperPlayServerMapData(PacketSendEvent event) { - super(event); - } - - public WrapperPlayServerMapData(int mapId, byte scale, @Nullable List decorations) { - this(mapId, scale, false, decorations); - } - - public WrapperPlayServerMapData(int mapId, byte scale, boolean locked, @Nullable List decorations) { - this(mapId, scale, false, locked, decorations, 0, 0, 0, 0, null); - } - - public WrapperPlayServerMapData( - int mapId, byte scale, boolean trackingPosition, - boolean locked, @Nullable List decorations, - int columns, int rows, int x, int z, byte @Nullable [] data - ) { - super(PacketType.Play.Server.MAP_DATA); - this.mapId = mapId; - this.scale = scale; - this.trackingPosition = trackingPosition; - this.locked = locked; - this.decorations = decorations; - this.columns = columns; - this.rows = rows; - this.x = x; - this.z = z; - this.data = data; - } - - @Override - public void read() { - this.mapId = this.readVarInt(); - this.scale = this.readByte(); - if (this.serverVersion.isNewerThanOrEquals(ServerVersion.V_1_9) - && this.serverVersion.isOlderThan(ServerVersion.V_1_17)) { - this.trackingPosition = this.readBoolean(); - } - this.locked = this.serverVersion.isNewerThanOrEquals(ServerVersion.V_1_14) && this.readBoolean(); - - if (this.serverVersion.isOlderThan(ServerVersion.V_1_17) || this.readBoolean()) { - this.decorations = this.readList(MapDecoration::read); - } - - this.columns = this.readUnsignedByte(); - if (this.columns > 0) { - this.rows = this.readUnsignedByte(); - this.x = this.readUnsignedByte(); - this.z = this.readUnsignedByte(); - this.data = this.readByteArray(); - } - } - - @Override - public void write() { - this.writeVarInt(this.mapId); - this.writeByte(this.scale); - if (this.serverVersion.isNewerThanOrEquals(ServerVersion.V_1_9) - && this.serverVersion.isOlderThan(ServerVersion.V_1_17)) { - this.writeBoolean(this.trackingPosition); - } - if (this.serverVersion.isNewerThanOrEquals(ServerVersion.V_1_14)) { - this.writeBoolean(this.locked); - } - - if (this.decorations != null) { - if (this.serverVersion.isNewerThanOrEquals(ServerVersion.V_1_17)) { - this.writeBoolean(true); // decorations are present - } - this.writeList(this.decorations, MapDecoration::write); - } else if (this.serverVersion.isNewerThanOrEquals(ServerVersion.V_1_17)) { - this.writeBoolean(false); // not present - } else { - // write empty list size - this.writeVarInt(0); - } - - if (this.data != null) { - this.writeByte(this.columns); - if (this.columns > 0) { - this.writeByte(this.rows); - this.writeByte(this.x); - this.writeByte(this.z); - this.writeByteArray(this.data); - } - } else { - this.writeByte(0); // 0 columns mean no data - } - } - - public static class MapDecoration { - - private MapDecorationType type; - private byte x; - private byte y; - private byte direction; // 0 to 15 - private @Nullable Component displayName; - - public MapDecoration(MapDecorationType type, byte x, byte y, byte direction, @Nullable Component displayName) { - this.type = type; - this.x = x; - this.y = y; - this.direction = direction; - this.displayName = displayName; - } - - public static MapDecoration read(PacketWrapper wrapper) { - // first 4 bits determine decoration type, last 4 bits determine rotation below 1.13 - boolean v113 = wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_13); - byte flags = v113 ? 0 : wrapper.readByte(); - MapDecorationType type = !v113 - ? MapDecorationTypes.getById(wrapper.getServerVersion().toClientVersion(), (flags >> 4) & 0xF) - : wrapper.readMappedEntity(MapDecorationTypes::getById); - - byte x = wrapper.readByte(); - byte y = wrapper.readByte(); - byte direction = (byte) ((v113 ? wrapper.readByte() : flags) & 0xF); - Component displayName = v113 ? wrapper.readOptional(PacketWrapper::readComponent) : null; - return new MapDecoration(type, x, y, direction, displayName); - } - - public static void write(PacketWrapper wrapper, MapDecoration decoration) { - boolean v113 = wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_13); - if (v113) { - wrapper.writeMappedEntity(decoration.getType()); - } else { - int typeId = decoration.getType().getId(wrapper.getServerVersion().toClientVersion()); - wrapper.writeByte((typeId & 0xF) << 4 | (decoration.getDirection() & 0xF)); - } - - wrapper.writeByte(decoration.getX()); - wrapper.writeByte(decoration.getY()); - if (v113) { - wrapper.writeByte(decoration.getDirection()); - wrapper.writeOptional(decoration.getDisplayName(), PacketWrapper::writeComponent); - } - } - - public MapDecorationType getType() { - return this.type; - } - - public void setType(MapDecorationType type) { - this.type = type; - } - - public byte getX() { - return this.x; - } - - public void setX(byte x) { - this.x = x; - } - - public byte getY() { - return this.y; - } - - public void setY(byte y) { - this.y = y; - } - - public byte getDirection() { - return this.direction; - } - - public void setDirection(byte direction) { - this.direction = direction; - } - - public @Nullable Component getDisplayName() { - return this.displayName; - } - - public void setDisplayName(@Nullable Component displayName) { - this.displayName = displayName; - } - } - - public int getMapId() { - return this.mapId; - } - - public void setMapId(int mapId) { - this.mapId = mapId; - } - - public byte getScale() { - return this.scale; - } - - public void setScale(byte scale) { - this.scale = scale; - } - - public boolean isTrackingPosition() { - return this.trackingPosition; - } - - public void setTrackingPosition(boolean trackingPosition) { - this.trackingPosition = trackingPosition; - } - - public boolean isLocked() { - return this.locked; - } - - public void setLocked(boolean locked) { - this.locked = locked; - } - - public @Nullable List getDecorations() { - return this.decorations; - } - - public void setDecorations(@Nullable List decorations) { - this.decorations = decorations; - } - - public int getColumns() { - return this.columns; - } - - public void setColumns(int columns) { - this.columns = columns; - } - - public int getRows() { - return this.rows; - } - - public void setRows(int rows) { - this.rows = rows; - } - - public int getX() { - return this.x; - } - - public void setX(int x) { - this.x = x; - } - - public int getZ() { - return this.z; - } - - public void setZ(int z) { - this.z = z; - } - - public byte @Nullable [] getData() { - return this.data; - } - - public void setData(byte @Nullable [] data) { - this.data = data; - } -} +/* + * This file is part of packetevents - https://github.com/retrooper/packetevents + * Copyright (C) 2024 retrooper and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.github.retrooper.packetevents.wrapper.play.server; + +import com.github.retrooper.packetevents.event.PacketSendEvent; +import com.github.retrooper.packetevents.manager.server.ServerVersion; +import com.github.retrooper.packetevents.protocol.item.mapdecoration.MapDecorationType; +import com.github.retrooper.packetevents.protocol.item.mapdecoration.MapDecorationTypes; +import com.github.retrooper.packetevents.protocol.packettype.PacketType; +import com.github.retrooper.packetevents.wrapper.PacketWrapper; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class WrapperPlayServerMapData extends PacketWrapper { + + private int mapId; + private byte scale; + private boolean trackingPosition; + private boolean locked; + private @Nullable List decorations; + private int columns; + private int rows; + private int x; + private int z; + private byte @Nullable [] data; + + public WrapperPlayServerMapData(PacketSendEvent event) { + super(event); + } + + public WrapperPlayServerMapData(int mapId, byte scale, @Nullable List decorations) { + this(mapId, scale, false, decorations); + } + + public WrapperPlayServerMapData(int mapId, byte scale, boolean locked, @Nullable List decorations) { + this(mapId, scale, false, locked, decorations, 0, 0, 0, 0, null); + } + + public WrapperPlayServerMapData( + int mapId, byte scale, boolean trackingPosition, + boolean locked, @Nullable List decorations, + int columns, int rows, int x, int z, byte @Nullable [] data + ) { + super(PacketType.Play.Server.MAP_DATA); + this.mapId = mapId; + this.scale = scale; + this.trackingPosition = trackingPosition; + this.locked = locked; + this.decorations = decorations; + this.columns = columns; + this.rows = rows; + this.x = x; + this.z = z; + this.data = data; + } + + @Override + public void read() { + this.mapId = this.readVarInt(); + this.scale = this.readByte(); + if (this.serverVersion.isNewerThanOrEquals(ServerVersion.V_1_9) + && this.serverVersion.isOlderThan(ServerVersion.V_1_17)) { + this.trackingPosition = this.readBoolean(); + } + this.locked = this.serverVersion.isNewerThanOrEquals(ServerVersion.V_1_14) && this.readBoolean(); + + if (this.serverVersion.isOlderThan(ServerVersion.V_1_17) || this.readBoolean()) { + this.decorations = this.readList(MapDecoration::read); + } + + this.columns = this.readUnsignedByte(); + if (this.columns > 0) { + this.rows = this.readUnsignedByte(); + this.x = this.readUnsignedByte(); + this.z = this.readUnsignedByte(); + this.data = this.readByteArray(); + } + } + + @Override + public void write() { + this.writeVarInt(this.mapId); + this.writeByte(this.scale); + if (this.serverVersion.isNewerThanOrEquals(ServerVersion.V_1_9) + && this.serverVersion.isOlderThan(ServerVersion.V_1_17)) { + this.writeBoolean(this.trackingPosition); + } + if (this.serverVersion.isNewerThanOrEquals(ServerVersion.V_1_14)) { + this.writeBoolean(this.locked); + } + + if (this.decorations != null) { + if (this.serverVersion.isNewerThanOrEquals(ServerVersion.V_1_17)) { + this.writeBoolean(true); // decorations are present + } + this.writeList(this.decorations, MapDecoration::write); + } else if (this.serverVersion.isNewerThanOrEquals(ServerVersion.V_1_17)) { + this.writeBoolean(false); // not present + } else { + // write empty list size + this.writeVarInt(0); + } + + if (this.data != null) { + this.writeByte(this.columns); + if (this.columns > 0) { + this.writeByte(this.rows); + this.writeByte(this.x); + this.writeByte(this.z); + this.writeByteArray(this.data); + } + } else { + this.writeByte(0); // 0 columns mean no data + } + } + + public static class MapDecoration { + + private MapDecorationType type; + private byte x; + private byte y; + private byte direction; // 0 to 15 + private @Nullable Component displayName; + + public MapDecoration(MapDecorationType type, byte x, byte y, byte direction, @Nullable Component displayName) { + this.type = type; + this.x = x; + this.y = y; + this.direction = direction; + this.displayName = displayName; + } + + public static MapDecoration read(PacketWrapper wrapper) { + // first 4 bits determine decoration type, last 4 bits determine rotation below 1.13 + boolean v113 = wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_13); + byte flags = v113 ? 0 : wrapper.readByte(); + MapDecorationType type = !v113 + ? MapDecorationTypes.getById(wrapper.getServerVersion().toClientVersion(), (flags >> 4) & 0xF) + : wrapper.readMappedEntity(MapDecorationTypes::getById); + + byte x = wrapper.readByte(); + byte y = wrapper.readByte(); + byte direction = (byte) ((v113 ? wrapper.readByte() : flags) & 0xF); + Component displayName = v113 ? wrapper.readOptional(PacketWrapper::readComponent) : null; + return new MapDecoration(type, x, y, direction, displayName); + } + + public static void write(PacketWrapper wrapper, MapDecoration decoration) { + boolean v113 = wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_13); + if (v113) { + wrapper.writeMappedEntity(decoration.getType()); + } else { + int typeId = decoration.getType().getId(wrapper.getServerVersion().toClientVersion()); + wrapper.writeByte((typeId & 0xF) << 4 | (decoration.getDirection() & 0xF)); + } + + wrapper.writeByte(decoration.getX()); + wrapper.writeByte(decoration.getY()); + if (v113) { + wrapper.writeByte(decoration.getDirection()); + wrapper.writeOptional(decoration.getDisplayName(), PacketWrapper::writeComponent); + } + } + + public MapDecorationType getType() { + return this.type; + } + + public void setType(MapDecorationType type) { + this.type = type; + } + + public byte getX() { + return this.x; + } + + public void setX(byte x) { + this.x = x; + } + + public byte getY() { + return this.y; + } + + public void setY(byte y) { + this.y = y; + } + + public byte getDirection() { + return this.direction; + } + + public void setDirection(byte direction) { + this.direction = direction; + } + + public @Nullable Component getDisplayName() { + return this.displayName; + } + + public void setDisplayName(@Nullable Component displayName) { + this.displayName = displayName; + } + } + + public int getMapId() { + return this.mapId; + } + + public void setMapId(int mapId) { + this.mapId = mapId; + } + + public byte getScale() { + return this.scale; + } + + public void setScale(byte scale) { + this.scale = scale; + } + + public boolean isTrackingPosition() { + return this.trackingPosition; + } + + public void setTrackingPosition(boolean trackingPosition) { + this.trackingPosition = trackingPosition; + } + + public boolean isLocked() { + return this.locked; + } + + public void setLocked(boolean locked) { + this.locked = locked; + } + + public @Nullable List getDecorations() { + return this.decorations; + } + + public void setDecorations(@Nullable List decorations) { + this.decorations = decorations; + } + + public int getColumns() { + return this.columns; + } + + public void setColumns(int columns) { + this.columns = columns; + } + + public int getRows() { + return this.rows; + } + + public void setRows(int rows) { + this.rows = rows; + } + + public int getX() { + return this.x; + } + + public void setX(int x) { + this.x = x; + } + + public int getZ() { + return this.z; + } + + public void setZ(int z) { + this.z = z; + } + + public byte @Nullable [] getData() { + return this.data; + } + + public void setData(byte @Nullable [] data) { + this.data = data; + } +} diff --git a/api/src/test/java/com/github/retrooper/packetevents/test/StateTypeMappingTest.java b/api/src/test/java/com/github/retrooper/packetevents/test/StateTypeMappingTest.java new file mode 100644 index 0000000000..722642faf0 --- /dev/null +++ b/api/src/test/java/com/github/retrooper/packetevents/test/StateTypeMappingTest.java @@ -0,0 +1,207 @@ +package com.github.retrooper.packetevents.test; + +import com.github.retrooper.packetevents.manager.server.ServerVersion; +import com.github.retrooper.packetevents.protocol.player.ClientVersion; +import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState; +import com.github.retrooper.packetevents.protocol.world.states.defaulttags.BlockTags; +import com.github.retrooper.packetevents.protocol.world.states.type.StateType; +import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes; +import com.github.retrooper.packetevents.test.base.BaseDummyAPITest; +import io.github.retrooper.packetevents.util.SpigotConversionUtil; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Function; + +import com.github.retrooper.packetevents.PacketEvents; +import org.bukkit.Material; +import org.bukkit.material.MaterialData; //Needed for 1.12 and below support. +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public class StateTypeMappingTest extends BaseDummyAPITest { + + private Collection cachedStateValues = null; + + private Collection getNonObsoleteStateTypes() { + if (cachedStateValues == null) { + cachedStateValues = computeNonObsoleteStateTypes(); + } + return cachedStateValues; + } + + private Collection computeNonObsoleteStateTypes() { + Collection all = StateTypes.values(); + List filtered = new ArrayList<>(all); + // Case 0: Ignore obsolete entries (temporary workaround to GRASS_PATH rename/fix) + Set EXEMPT_STATE_TYPES = new HashSet<>(Arrays.asList( + StateTypes.GRASS_PATH + )); + filtered.removeAll(EXEMPT_STATE_TYPES); + return filtered; + } + + @ParameterizedTest + @EnumSource(ClientVersion.class) + @DisplayName("Verify StateType mappings for all client versions") + public void testStateTypeMappings(ClientVersion version) { + testStateTypeMappings(version, false); + } + + @Test + @DisplayName("Verify StateType mappings (fail fast)") + public void testStateTypeMappingsFailFast() { + // Use the server version. + ServerVersion serverVersion = PacketEvents.getAPI().getServerManager().getVersion(); + ClientVersion version = serverVersion.toClientVersion(); + testStateTypeMappings(version, true); + } + + public void testStateTypeMappings(ClientVersion version, boolean failFast) { + final ServerVersion serverVersion = PacketEvents.getAPI().getServerManager().getVersion(); + Function blockStateFunction = getBlockStateFunction(serverVersion); + + StringBuilder errorMessages = new StringBuilder(); // Accumulate error messages for diagnostic mode + + int found = 0; + int idsMatched = 0; + + + Collection stateValues = getNonObsoleteStateTypes(); + + // Get all BlockTags for versions newer than the server version + List newerBlockTags = getVersionBlockTagsNewerThan(serverVersion); + int expected = stateValues.size(); + + // Check if the client version is newer than the server version + ClientVersion serverClientVersion = serverVersion.toClientVersion(); + boolean isClientNewer = version.isNewerThan(serverClientVersion); + + for (StateType value : stateValues) { + String name = value.getName(); + int id = value.getMapped().getId(version); + + // Case 1: Block is from a newer version than the server (id == -1) + if (id == -1 && isBlockFromNewerVersion(value, newerBlockTags)) { + // Skip validation for blocks from newer versions and mark as found so there is no count error at the end + expected--; + continue; + } + + Material material = Material.matchMaterial(name); // This will return null for materials like potted_open_eyeblossom (added in 1.21.4) on 1.21 server + + // Case 2: Client is newer, block exists in client (id != -1), but not in server (material == null) + if (isClientNewer && id != -1 && material == null && isBlockFromNewerVersion(value, newerBlockTags)) { + // This is expected behavior: the client knows the block, but the server does not + expected--; + continue; + } + + // Case 3: Material is missing unexpectedly (not from a newer version) + if (material == null) { + String errorMessage = String.format("Material not found for statetype %s, id=%d", name, id); + if (failFast) { + fail(errorMessage); + return; // Just to make sure it exits. + } else { + errorMessages.append(errorMessage).append("\n"); + } + continue; + } + found++; + + WrappedBlockState state = blockStateFunction.apply(material); + if (state == null) { + String errorMessage = String.format("Failed to create BlockState from material %s, id=%d", material.name(), id); + if (failFast) { + fail(errorMessage); + return; + } else { + errorMessages.append(errorMessage).append("\n"); + } + continue; + } + + if (state.getType() != value) { + String errorMessage = String.format("State type mismatch for material %s, type=%s, value=%s", material.name(), state.getType(), value); + if (failFast) { + fail(errorMessage); + return; + } else { + errorMessages.append(errorMessage).append("\n"); + } + continue; + } + idsMatched++; + } + + final int missing = expected - found; + + // Diagnostic output (non-fail-fast mode) + if (!failFast && errorMessages.length() > 0) { + System.err.println("StateType Mapping Errors:"); + System.err.println(errorMessages); + + // Output summary + System.err.println(String.format("%d/%d statetypes found", found, expected)); + if (missing > 0) { + double percent = ((double) found / expected) * 100; + System.err.println(String.format("%d missing (%.2f%%)", missing, percent)); + } + System.err.println(String.format("%d/%d ids matched", idsMatched, found)); + } + + // Only fail the test if there are unexpected missing StateTypes + assertEquals(expected, found, String.format("Not all StateTypes found for version %s. Missing: %d. See error log for details.", version.getReleaseName(), missing)); + assertEquals(found, idsMatched, String.format("Not all StateType IDs matched for version %s. See error log for details.", version.getReleaseName())); + } + + private Function getBlockStateFunction(ServerVersion serverVersion) { + if (serverVersion.isOlderThanOrEquals(ServerVersion.V_1_12)) { + return material -> SpigotConversionUtil.fromBukkitMaterialData(new MaterialData(material)); + } else { + return material -> SpigotConversionUtil.fromBukkitBlockData(material.createBlockData()); + } + } + + /** + * Gets all BlockTags for versions newer than the server version. + * Relies on BlockTags existing with names V_1_20_5, V_1_21_2, V_1_21_4, etc... + * for versions newer than the Mocked server version (currently 1.21.1 from MockBukkit) + */ + private List getVersionBlockTagsNewerThan(ServerVersion serverVersion) { + List blockTags = new ArrayList<>(); + for (ServerVersion version : ServerVersion.values()) { + if (version.isNewerThan(serverVersion)) { // Use isNewerThan to exclude the server's own version + BlockTags blockTag = BlockTags.getByName(version.name()); // Use name() to match enum naming convention + if (blockTag != null) { // Only add non-null tags + blockTags.add(blockTag); + } + } + } + return blockTags; + } + + /** + * Determines if the block is from a version newer than the server's version. + */ + private boolean isBlockFromNewerVersion(StateType stateType, List newerBlockTags) { + // Check if the StateType is tagged in any of the newer BlockTags + for (BlockTags tag : newerBlockTags) { + if (tag.contains(stateType)) { + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/api/src/test/java/com/github/retrooper/packetevents/test/base/TestPacketEventsBuilder.java b/api/src/test/java/com/github/retrooper/packetevents/test/base/TestPacketEventsBuilder.java index 5d6ff3d839..955c417901 100644 --- a/api/src/test/java/com/github/retrooper/packetevents/test/base/TestPacketEventsBuilder.java +++ b/api/src/test/java/com/github/retrooper/packetevents/test/base/TestPacketEventsBuilder.java @@ -15,6 +15,7 @@ import io.github.retrooper.packetevents.impl.netty.NettyManagerImpl; import io.github.retrooper.packetevents.impl.netty.manager.protocol.ProtocolManagerAbstract; import io.github.retrooper.packetevents.impl.netty.manager.server.ServerManagerAbstract; +import io.github.retrooper.packetevents.manager.server.ServerManagerImpl; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.Nullable; @@ -60,12 +61,7 @@ public ProtocolVersion getPlatformVersion() { return ProtocolVersion.UNKNOWN; } }; - private final ServerManager serverManager = new ServerManagerAbstract() { - @Override - public ServerVersion getVersion() { - return ServerVersion.getLatest(); - } - }; + private final ServerManager serverManager = new ServerManagerImpl(); private final NettyManager nettyManager = new NettyManagerImpl(); private final LogManager logManager = new LogManager() { diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index f864b0b242..cf001bfd63 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -4,63 +4,138 @@ plugins { } repositories { - maven("https://repo.codemc.io/repository/maven-snapshots/") - maven { - name = "ParchmentMC" - url = uri("https://maven.parchmentmc.org") - } + mavenCentral() + maven("https://repo.viaversion.com/") + maven("https://jitpack.io") // Conditional Mixin } val minecraft_version: String by project -val parchment_minecraft_version: String by project -val parchment_mappings: String by project +val yarn_mappings: String by project val loader_version: String by project dependencies { api(libs.bundles.adventure) api(project(":api", "shadow")) api(project(":netty-common")) + modApi("com.github.Fallen-Breath.conditional-mixin:conditional-mixin-fabric:0.6.4") include(libs.bundles.adventure) include(project(":api", "shadow")) include(project(":netty-common")) + include("com.github.Fallen-Breath.conditional-mixin:conditional-mixin-fabric:0.6.4") // To change the versions, see the gradle.properties file minecraft("com.mojang:minecraft:$minecraft_version") - mappings(loom.layered { - officialMojangMappings() - parchment("org.parchmentmc.data:parchment-$parchment_minecraft_version:$parchment_mappings") - }) + mappings("net.fabricmc:yarn:$yarn_mappings") + + compileOnly(libs.via.version) + compileOnly("org.slf4j:slf4j-simple:2.0.16") +} + +loom { + mods { + register("packetevents-${project.name}") { + sourceSet(sourceSets.main.get()) + } + } +} + +allprojects { + apply(plugin = "fabric-loom") + + repositories { + maven("https://repo.codemc.io/repository/maven-snapshots/") + } + + dependencies { + modImplementation("net.fabricmc:fabric-loader:$loader_version") + } + + tasks { + withType { + val targetJavaVersion = 17 + if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible) { + options.release = targetJavaVersion + } + } + + remapJar { + archiveBaseName = "${rootProject.name}-fabric${if (project.name != "fabric") "-${project.name}" else ""}" + archiveVersion = rootProject.ext["versionNoHash"] as String + } - modImplementation("net.fabricmc:fabric-loader:$loader_version") + remapSourcesJar { + archiveVersion = rootProject.ext["versionNoHash"] as String + } + } + + loom { + mixin { + // Replaces strings in annotations instead of using refmap + // This allows us to write mixins that target methodName* and have them work across versions + // Even as the signature changes without having to use @Dynamic and intermediary names + // This preserves some compile-time safety, reduces jar size but be careful to not inject into wrong methods + useLegacyMixinAp.set(false) + } + + val accessWidenerFile = sourceSets["main"].resources.srcDirs.first() + .resolve("${rootProject.name}.accesswidener") + + if (accessWidenerFile.exists()) { + accessWidenerPath.set(accessWidenerFile) + } + } } -tasks { - withType { - val targetJavaVersion = 17 - if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible) { - options.release = targetJavaVersion +subprojects { + version = rootProject.version + val minecraft_version: String by project + + repositories { + maven { + name = "ParchmentMC" + url = uri("https://maven.parchmentmc.org") } } - remapJar { - archiveBaseName = "${rootProject.name}-fabric" - archiveVersion = rootProject.ext["versionNoHash"] as String + dependencies { + compileOnly(project(":api", "shadow")) + compileOnly(project(":netty-common")) + compileOnly(project(":fabric", configuration = "namedElements")) } - remapSourcesJar { - archiveVersion = rootProject.ext["versionNoHash"] as String + // version replacement already processed for :fabric in packetevents.`library-conventions` + tasks { + processResources { + // Declare the inputs to allow Gradle to track changes + inputs.property("version", project.version) + inputs.property("modName", "packetevents-${project.name}") + inputs.property("minecraft_version", minecraft_version) // Add if you use this + + // Match and expand variables in fabric.mod.json + filesMatching("fabric.mod.json") { + expand( + mapOf( + "version" to project.version, + "modName" to "packetevents-${project.name}", + "minecraft_version" to minecraft_version // Or pull from a variable + ) + ) + } + } } } -loom { - splitEnvironmentSourceSets() - mods { - register("packetevents") { - sourceSet(sourceSets.main.get()) - sourceSet(sourceSets.maybeCreate("client")) +subprojects.forEach { + tasks.named("remapJar").configure { + dependsOn("${it.path}:remapJar") + } +} + +tasks.remapJar.configure { + subprojects.forEach { subproject -> + subproject.tasks.matching { it.name == "remapJar" }.configureEach { + nestedJars.from(this) } } - accessWidenerPath = sourceSets.main.get().resources.srcDirs.single() - .resolve("${rootProject.name}.accesswidener") } diff --git a/fabric/gradle.properties b/fabric/gradle.properties index 1433bd04d9..2d250e0e3e 100644 --- a/fabric/gradle.properties +++ b/fabric/gradle.properties @@ -4,7 +4,6 @@ org.gradle.parallel=true # Fabric Properties # check these on https://fabricmc.net/develop -minecraft_version=1.21.5 -parchment_mappings=2025.03.23 -parchment_minecraft_version=1.21.4 -loader_version=0.16.10 +minecraft_version=1.14 +yarn_mappings=1.14+build.21 +loader_version=0.16.12 diff --git a/fabric/mc1140/build.gradle.kts b/fabric/mc1140/build.gradle.kts new file mode 100644 index 0000000000..3ab7a92613 --- /dev/null +++ b/fabric/mc1140/build.gradle.kts @@ -0,0 +1,24 @@ +val minecraft_version: String by project +val yarn_mappings: String by project + +plugins { + alias(libs.plugins.fabric.loom) +} + +repositories { + mavenCentral() +} + +dependencies { + // To change the versions, see the gradle.properties file + minecraft("com.mojang:minecraft:$minecraft_version") + mappings("net.fabricmc:yarn:$yarn_mappings") +} + +loom { + mods { + register("packetevents-${project.name}") { + sourceSet(sourceSets.main.get()) + } + } +} \ No newline at end of file diff --git a/fabric/mc1140/gradle.properties b/fabric/mc1140/gradle.properties new file mode 100644 index 0000000000..3fbc6e9eb9 --- /dev/null +++ b/fabric/mc1140/gradle.properties @@ -0,0 +1,8 @@ +# Done to increase the memory available to gradle. +org.gradle.jvmargs=-Xmx1G +org.gradle.parallel=true + +# Fabric Properties +# check these on https://fabricmc.net/develop +minecraft_version=1.14 +yarn_mappings=1.14+build.21 \ No newline at end of file diff --git a/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/Fabric1140ChainLoadEntrypoint.java b/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/Fabric1140ChainLoadEntrypoint.java new file mode 100644 index 0000000000..8bdc9e48f3 --- /dev/null +++ b/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/Fabric1140ChainLoadEntrypoint.java @@ -0,0 +1,30 @@ +package io.github.retrooper.packetevents.mc1140; + +import com.github.retrooper.packetevents.manager.server.ServerVersion; +import io.github.retrooper.packetevents.factory.fabric.FabricPacketEventsAPI; +import io.github.retrooper.packetevents.manager.AbstractFabricPlayerManager; +import io.github.retrooper.packetevents.manager.registry.FabricRegistryManager; +import io.github.retrooper.packetevents.mc1140.manager.registry.Fabric1140ItemRegistry; +import io.github.retrooper.packetevents.util.LazyHolder; +import io.github.retrooper.packetevents.loader.ChainLoadData; +import io.github.retrooper.packetevents.loader.ChainLoadEntryPoint; +import io.github.retrooper.packetevents.mc1140.factory.fabric.Fabric1140ServerPlayerManager; + +public class Fabric1140ChainLoadEntrypoint implements ChainLoadEntryPoint { + + protected LazyHolder playerManagerAbstractLazyHolder = LazyHolder.simple(() -> new Fabric1140ServerPlayerManager(FabricPacketEventsAPI.getServerAPI())); + + @Override + public void initialize(ChainLoadData chainLoadData) { + chainLoadData.setPlayerManagerIfNull(playerManagerAbstractLazyHolder); + // Set default registry manager if not already set by any entrypoint + chainLoadData.setRegistryManagerIfNull(LazyHolder.simple(() -> new FabricRegistryManager( + new Fabric1140ItemRegistry() + ))); + } + + @Override + public ServerVersion getNativeVersion() { + return ServerVersion.V_1_14; + } +} diff --git a/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/Fabric1140ClientChainLoadEntrypoint.java b/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/Fabric1140ClientChainLoadEntrypoint.java new file mode 100644 index 0000000000..a5f5205714 --- /dev/null +++ b/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/Fabric1140ClientChainLoadEntrypoint.java @@ -0,0 +1,14 @@ +package io.github.retrooper.packetevents.mc1140; + +import io.github.retrooper.packetevents.factory.fabric.FabricPacketEventsAPI; +import io.github.retrooper.packetevents.loader.ChainLoadData; +import io.github.retrooper.packetevents.mc1140.factory.fabric.Fabric1140ClientPlayerManager; +import io.github.retrooper.packetevents.util.LazyHolder; + +public class Fabric1140ClientChainLoadEntrypoint extends Fabric1140ChainLoadEntrypoint { + + @Override + public void initialize(ChainLoadData chainLoadData) { + chainLoadData.setClientPlayerManagerIfNull(LazyHolder.simple(() -> new Fabric1140ClientPlayerManager(FabricPacketEventsAPI.getClientAPI()))); + } +} diff --git a/fabric/src/client/java/io/github/retrooper/packetevents/factory/fabric/FabricClientPlayerManager.java b/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/factory/fabric/Fabric1140ClientPlayerManager.java similarity index 63% rename from fabric/src/client/java/io/github/retrooper/packetevents/factory/fabric/FabricClientPlayerManager.java rename to fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/factory/fabric/Fabric1140ClientPlayerManager.java index 84591d065d..7aa494c058 100644 --- a/fabric/src/client/java/io/github/retrooper/packetevents/factory/fabric/FabricClientPlayerManager.java +++ b/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/factory/fabric/Fabric1140ClientPlayerManager.java @@ -16,34 +16,39 @@ * along with this program. If not, see . */ -package io.github.retrooper.packetevents.factory.fabric; +package io.github.retrooper.packetevents.mc1140.factory.fabric; -import net.minecraft.client.Minecraft; -import net.minecraft.client.multiplayer.PlayerInfo; -import net.minecraft.client.player.LocalPlayer; +import com.github.retrooper.packetevents.PacketEventsAPI; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.network.PlayerListEntry; import org.jetbrains.annotations.NotNull; -public class FabricClientPlayerManager extends FabricPlayerManager { +public class Fabric1140ClientPlayerManager extends Fabric1140ServerPlayerManager { + + public Fabric1140ClientPlayerManager(PacketEventsAPI packetEventsAPI) { + super(packetEventsAPI); + } @Override public int getPing(@NotNull Object playerObj) { - if (playerObj instanceof LocalPlayer player) { - PlayerInfo info = player.connection.getPlayerInfo(player.getUUID()); + if (playerObj instanceof ClientPlayerEntity player) { + PlayerListEntry info = player.networkHandler.getPlayerListEntry(player.getUuid()); if (info != null) { return info.getLatency(); } // if the server doesn't show the player info of // the player itself, try to fall back to potential // latency sampling data - which is often not present - return (int) Minecraft.getInstance().getDebugOverlay().getPingLogger().get(0); + // TODO fix later for 1.20.1 + return -1; } return super.getPing(playerObj); } @Override public Object getChannel(@NotNull Object player) { - if (player instanceof LocalPlayer) { - return ((LocalPlayer) player).connection.getConnection().channel; + if (player instanceof ClientPlayerEntity) { + return ((ClientPlayerEntity) player).networkHandler.connection.channel; } return super.getChannel(player); } diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/factory/fabric/FabricPlayerManager.java b/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/factory/fabric/Fabric1140ServerPlayerManager.java similarity index 55% rename from fabric/src/main/java/io/github/retrooper/packetevents/factory/fabric/FabricPlayerManager.java rename to fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/factory/fabric/Fabric1140ServerPlayerManager.java index e7a6fe4197..fa55c6232d 100644 --- a/fabric/src/main/java/io/github/retrooper/packetevents/factory/fabric/FabricPlayerManager.java +++ b/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/factory/fabric/Fabric1140ServerPlayerManager.java @@ -16,27 +16,38 @@ * along with this program. If not, see . */ -package io.github.retrooper.packetevents.factory.fabric; +package io.github.retrooper.packetevents.mc1140.factory.fabric; -import io.github.retrooper.packetevents.impl.netty.manager.player.PlayerManagerAbstract; -import net.minecraft.server.level.ServerPlayer; +import com.github.retrooper.packetevents.PacketEventsAPI; +import io.github.retrooper.packetevents.manager.AbstractFabricPlayerManager; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.server.network.ServerPlayerEntity; import org.jetbrains.annotations.NotNull; -public class FabricPlayerManager extends PlayerManagerAbstract { +public class Fabric1140ServerPlayerManager extends AbstractFabricPlayerManager { + + public Fabric1140ServerPlayerManager(PacketEventsAPI packetEventsAPI) { + super(packetEventsAPI); + } @Override public int getPing(@NotNull Object player) { - if (player instanceof ServerPlayer) { - return ((ServerPlayer) player).connection.latency(); + if (player instanceof ServerPlayerEntity) { + return ((ServerPlayerEntity) player).field_13967; // pingMilliseconds in modern yarn } throw new UnsupportedOperationException("Unsupported player implementation: " + player); } @Override public Object getChannel(@NotNull Object player) { - if (player instanceof ServerPlayer) { - return ((ServerPlayer) player).connection.connection.channel; + if (player instanceof ServerPlayerEntity) { + return ((ServerPlayerEntity) player).networkHandler.client.channel; } throw new UnsupportedOperationException("Unsupported player implementation: " + player); } + + @Override + public void disconnectPlayer(ServerPlayerEntity serverPlayerEntity, String message) { + serverPlayerEntity.networkHandler.disconnect(new TextComponent(message)); + } } diff --git a/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/manager/registry/Fabric1140ItemRegistry.java b/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/manager/registry/Fabric1140ItemRegistry.java new file mode 100644 index 0000000000..e4c21fccb3 --- /dev/null +++ b/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/manager/registry/Fabric1140ItemRegistry.java @@ -0,0 +1,29 @@ +package io.github.retrooper.packetevents.mc1140.manager.registry; + +import com.github.retrooper.packetevents.manager.registry.ItemRegistry; +import com.github.retrooper.packetevents.protocol.item.type.ItemType; +//import io.github.retrooper.packetevents.FabricItemType; +//import net.minecraft.item.Item; +//import net.minecraft.registry.Registries; +//import net.minecraft.util.Identifier; +import io.github.retrooper.packetevents.FabricItemType; +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public class Fabric1140ItemRegistry implements ItemRegistry { + + @Override + public @Nullable ItemType getByName(String name) { + Optional item = Registry.ITEM.getOrEmpty(Identifier.createSplit(name, ':')); + return item.isPresent() ? new FabricItemType(item.get()) : null; + } + + @Override + public @Nullable ItemType getById(int id) { + return null; + } +} diff --git a/fabric/src/client/java/io/github/retrooper/packetevents/mixin/ClientPacketListenerMixin.java b/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/mixin/ClientPlayerNetworkHandlerMixin.java similarity index 54% rename from fabric/src/client/java/io/github/retrooper/packetevents/mixin/ClientPacketListenerMixin.java rename to fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/mixin/ClientPlayerNetworkHandlerMixin.java index 83ec01ea15..cc7a8afe91 100644 --- a/fabric/src/client/java/io/github/retrooper/packetevents/mixin/ClientPacketListenerMixin.java +++ b/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/mixin/ClientPlayerNetworkHandlerMixin.java @@ -16,60 +16,61 @@ * along with this program. If not, see . */ -package io.github.retrooper.packetevents.mixin; +package io.github.retrooper.packetevents.mc1140.mixin; -import com.github.retrooper.packetevents.PacketEvents; import com.llamalad7.mixinextras.sugar.Local; -import net.minecraft.client.Minecraft; -import net.minecraft.client.multiplayer.ClientCommonPacketListenerImpl; -import net.minecraft.client.multiplayer.ClientPacketListener; -import net.minecraft.client.multiplayer.CommonListenerCookie; -import net.minecraft.client.player.LocalPlayer; -import net.minecraft.network.Connection; +import io.github.retrooper.packetevents.factory.fabric.FabricPacketEventsAPI; +import me.fallenbreath.conditionalmixin.api.annotation.Condition; +import me.fallenbreath.conditionalmixin.api.annotation.Restriction; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.network.ClientConnection; import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -@Mixin(ClientPacketListener.class) -public abstract class ClientPacketListenerMixin extends ClientCommonPacketListenerImpl { - - @Shadow public abstract Connection getConnection(); - - public ClientPacketListenerMixin(Minecraft minecraft, Connection connection, CommonListenerCookie cookie) { - super(minecraft, connection, cookie); // dummy ctor - } +@Mixin(ClientPlayNetworkHandler.class) +@Restriction( + require = { + @Condition(value = "minecraft", versionPredicates = {"<1.20.2"}), + } +) +public abstract class ClientPlayerNetworkHandlerMixin { + @Shadow @Final public ClientConnection connection; /** * @reason Associate connection instance with player instance */ @Inject( - method = "handleLogin", + method = "onGameJoin", at = @At( value = "FIELD", opcode = Opcodes.PUTFIELD, - target = "Lnet/minecraft/client/Minecraft;player:Lnet/minecraft/client/player/LocalPlayer;", + target = "Lnet/minecraft/client/MinecraftClient;player:Lnet/minecraft/client/network/ClientPlayerEntity;", shift = At.Shift.AFTER ) ) private void postLoginPlayerConstruct(CallbackInfo ci) { - PacketEvents.getAPI().getInjector().setPlayer(this.getConnection().channel, this.minecraft.player); + FabricPacketEventsAPI.getClientAPI().getInjector().setPlayer(this.connection.channel, MinecraftClient.getInstance().player); } /** * @reason Minecraft creates a new player instance on respawn */ @Inject( - method = "handleRespawn", + method = "onPlayerRespawn", at = @At( // inject immediately after new player instance has been created value = "INVOKE", - target = "Lnet/minecraft/client/multiplayer/ClientPacketListener;startWaitingForNewLevel(Lnet/minecraft/client/player/LocalPlayer;Lnet/minecraft/client/multiplayer/ClientLevel;Lnet/minecraft/client/gui/screens/ReceivingLevelScreen$Reason;)V" + target = "Lnet/minecraft/client/network/ClientPlayerEntity;setEntityId(I)V" ) ) - private void postRespawnPlayerConstruct(CallbackInfo ci, @Local(ordinal = 1) LocalPlayer player) { - PacketEvents.getAPI().getInjector().setPlayer(this.getConnection().channel, player); + private void postRespawnPlayerConstruct(CallbackInfo ci, @Local(ordinal = 1) ClientPlayerEntity player) { + FabricPacketEventsAPI.getClientAPI().getInjector().setPlayer(this.connection.channel, player); } } diff --git a/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/mixin/MixinClientConnectionChInit.java b/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/mixin/MixinClientConnectionChInit.java new file mode 100644 index 0000000000..a74f918ceb --- /dev/null +++ b/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/mixin/MixinClientConnectionChInit.java @@ -0,0 +1,27 @@ +package io.github.retrooper.packetevents.mc1140.mixin; + +import io.github.retrooper.packetevents.util.FabricInjectionUtil; +import io.netty.channel.Channel; +import io.netty.channel.socket.SocketChannel; +import me.fallenbreath.conditionalmixin.api.annotation.Condition; +import me.fallenbreath.conditionalmixin.api.annotation.Restriction; +import net.minecraft.network.NetworkSide; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(targets = "net.minecraft.network.ClientConnection$1", priority = 1500) // Priority of 1500 to Inject after via +@Restriction( + require = { + @Condition(value = "minecraft", versionPredicates = {"<1.19.4"}), + } +) +public class MixinClientConnectionChInit { + @Inject(method = "initChannel", at = @At(value = "TAIL"), remap = false) + private void onInitChannel(Channel channel, CallbackInfo ci) { + if (channel instanceof SocketChannel) { + FabricInjectionUtil.injectAtPipelineBuilder(channel.pipeline(), NetworkSide.CLIENTBOUND); + } + } +} diff --git a/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/mixin/MixinServerNetworkIoChInit.java b/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/mixin/MixinServerNetworkIoChInit.java new file mode 100644 index 0000000000..f25bc0e6f3 --- /dev/null +++ b/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/mixin/MixinServerNetworkIoChInit.java @@ -0,0 +1,27 @@ +package io.github.retrooper.packetevents.mc1140.mixin; + +import io.github.retrooper.packetevents.util.FabricInjectionUtil; +import io.netty.channel.Channel; +import io.netty.channel.socket.SocketChannel; +import me.fallenbreath.conditionalmixin.api.annotation.Condition; +import me.fallenbreath.conditionalmixin.api.annotation.Restriction; +import net.minecraft.network.NetworkSide; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(targets = "net.minecraft.server.ServerNetworkIo$1", priority = 1500) // Priority of 1500 to Inject after via +@Restriction( + require = { + @Condition(value = "minecraft", versionPredicates = {"<1.19.4"}), + } +) +public class MixinServerNetworkIoChInit { + @Inject(method = "initChannel", at = @At(value = "TAIL"), remap = false) + private void onInitChannel(Channel channel, CallbackInfo ci) { + if (channel instanceof SocketChannel) { + FabricInjectionUtil.injectAtPipelineBuilder(channel.pipeline(), NetworkSide.SERVERBOUND); + } + } +} diff --git a/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/mixin/PlayerManagerMixin.java b/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/mixin/PlayerManagerMixin.java new file mode 100644 index 0000000000..16583debfe --- /dev/null +++ b/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/mixin/PlayerManagerMixin.java @@ -0,0 +1,67 @@ +/* + * This file is part of packetevents - https://github.com/retrooper/packetevents + * Copyright (C) 2024 retrooper and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.retrooper.packetevents.mc1140.mixin; + +import com.llamalad7.mixinextras.sugar.Local; +import io.github.retrooper.packetevents.factory.fabric.FabricPacketEventsAPI; +import io.github.retrooper.packetevents.util.FabricInjectionUtil; +import net.minecraft.network.ClientConnection; +import net.minecraft.server.PlayerManager; +import net.minecraft.server.network.ServerPlayerEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(PlayerManager.class) +public abstract class PlayerManagerMixin { + + /** + * @reason Associate connection instance with player instance + */ + @Inject( + method = "onPlayerConnect*", + at = @At("HEAD") + ) + private void onPlayerConnect( + CallbackInfo ci, + @Local(ordinal = 0, argsOnly = true) ClientConnection connection, + @Local(ordinal = 0, argsOnly = true) ServerPlayerEntity player + ) { + FabricPacketEventsAPI.getServerAPI().getInjector().setPlayer(connection.channel, player); + } + + /** + * @reason Associate connection instance with player instance and handle login event + */ + @Inject( + method = "onPlayerConnect*", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/server/PlayerManager;sendToAll(Lnet/minecraft/network/Packet;)V", + shift = At.Shift.AFTER + ) + ) + private void onPlayerLogin( + CallbackInfo ci, + @Local(ordinal = 0, argsOnly = true) ServerPlayerEntity player + ) { + FabricInjectionUtil.fireUserLoginEvent(player); + } +} diff --git a/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/mixin/PlayerManagerRespawnMixin.java b/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/mixin/PlayerManagerRespawnMixin.java new file mode 100644 index 0000000000..5675ccd18a --- /dev/null +++ b/fabric/mc1140/src/main/java/io/github/retrooper/packetevents/mc1140/mixin/PlayerManagerRespawnMixin.java @@ -0,0 +1,37 @@ +package io.github.retrooper.packetevents.mc1140.mixin; + +import io.github.retrooper.packetevents.factory.fabric.FabricPacketEventsAPI; +import io.netty.channel.Channel; +import me.fallenbreath.conditionalmixin.api.annotation.Condition; +import me.fallenbreath.conditionalmixin.api.annotation.Restriction; +import net.minecraft.server.PlayerManager; +import net.minecraft.server.network.ServerPlayerEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Restriction( + require = { + @Condition(value = "minecraft", versionPredicates = {"<1.15.2"}), + } +) +@Mixin(PlayerManager.class) +public class PlayerManagerRespawnMixin { + /** Handles grabbing new player object on respawns for 1.20.1-, a separate mixin is required + * because the location of the field player.connection.connection changes + * from inheritance ServerGamePacketListenerImpl -> ServerCommonPacketListenerImpl thus breaking intermediary compatability + * + * @reason Minecraft creates a new player instance on respawn + */ + @Inject( + method = "respawnPlayer*", + at = @At("RETURN"), + require = 1 + ) + private void postRespawn(CallbackInfoReturnable cir) { + ServerPlayerEntity player = cir.getReturnValue(); + Channel channel = player.networkHandler.client.channel; + FabricPacketEventsAPI.getServerAPI().getInjector().setPlayer(channel, player); + } +} diff --git a/fabric/mc1140/src/main/resources/fabric.mod.json b/fabric/mc1140/src/main/resources/fabric.mod.json new file mode 100644 index 0000000000..77ee14b251 --- /dev/null +++ b/fabric/mc1140/src/main/resources/fabric.mod.json @@ -0,0 +1,28 @@ +{ + "schemaVersion": 1, + "id": "${modName}", + "version": "${version}", + "name": "PacketEvents dependency for ${minecraft_version}+", + "description": "", + "authors": [ + "retrooper" + ], + "contact": {}, + "license": "GPL-3.0", + "environment": "*", + "entrypoints": { + "peMainChainLoad": [ + "io.github.retrooper.packetevents.mc1140.Fabric1140ChainLoadEntrypoint" + ], + "peClientChainLoad": [ + "io.github.retrooper.packetevents.mc1140.Fabric1140ClientChainLoadEntrypoint" + ] + }, + "mixins": [ + "packetevents-mc1140.mixins.json" + ], + "accessWidener": "packetevents.accesswidener", + "depends": { + "fabricloader": "*" + } +} diff --git a/fabric/mc1140/src/main/resources/packetevents-mc1140.mixins.json b/fabric/mc1140/src/main/resources/packetevents-mc1140.mixins.json new file mode 100644 index 0000000000..b914386cd4 --- /dev/null +++ b/fabric/mc1140/src/main/resources/packetevents-mc1140.mixins.json @@ -0,0 +1,19 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "io.github.retrooper.packetevents.mc1140.mixin", + "compatibilityLevel": "JAVA_17", + "client": [ + "ClientPlayerNetworkHandlerMixin", + "MixinClientConnectionChInit" + ], + "mixins": [ + "MixinServerNetworkIoChInit", + "PlayerManagerMixin", + "PlayerManagerRespawnMixin" + ], + "injectors": { + "defaultRequire": 1 + }, + "plugin": "io.github.retrooper.packetevents.manager.PacketEventsMixinManager" +} diff --git a/fabric/mc1140/src/main/resources/packetevents.accesswidener b/fabric/mc1140/src/main/resources/packetevents.accesswidener new file mode 100644 index 0000000000..8be14a21b3 --- /dev/null +++ b/fabric/mc1140/src/main/resources/packetevents.accesswidener @@ -0,0 +1,10 @@ +accessWidener v2 named + + +# fields +accessible field net/minecraft/network/ClientConnection channel Lio/netty/channel/Channel; +accessible field net/minecraft/client/network/ClientPlayNetworkHandler connection Lnet/minecraft/network/ClientConnection; +accessible field net/minecraft/server/network/ServerPlayNetworkHandler client Lnet/minecraft/network/ClientConnection; # Private in newer versions +# methods + +# classes diff --git a/fabric/mc1194/build.gradle.kts b/fabric/mc1194/build.gradle.kts new file mode 100644 index 0000000000..d34cc42f67 --- /dev/null +++ b/fabric/mc1194/build.gradle.kts @@ -0,0 +1,23 @@ +val minecraft_version: String by project +val yarn_mappings: String by project + +repositories { + mavenCentral() +} + +dependencies { + compileOnly(project(":fabric:mc1140", configuration = "namedElements")) + // To change the versions, see the gradle.properties file + minecraft("com.mojang:minecraft:$minecraft_version") + mappings("net.fabricmc:yarn:$yarn_mappings") +} + +loom { + splitEnvironmentSourceSets() + mods { + register("packetevents-${project.name}") { + sourceSet(sourceSets.main.get()) + sourceSet(sourceSets.maybeCreate("client")) + } + } +} \ No newline at end of file diff --git a/fabric/mc1194/gradle.properties b/fabric/mc1194/gradle.properties new file mode 100644 index 0000000000..43696b382c --- /dev/null +++ b/fabric/mc1194/gradle.properties @@ -0,0 +1,8 @@ +# Done to increase the memory available to gradle. +org.gradle.jvmargs=-Xmx1G +org.gradle.parallel=true + +# Fabric Properties +# check these on https://fabricmc.net/develop +minecraft_version=1.19.4 +yarn_mappings=1.19.4+build.2:v2 diff --git a/fabric/mc1194/src/main/java/io/github/retrooper/packetevents/mc1914/Fabric1913ChainLoadEntrypoint.java b/fabric/mc1194/src/main/java/io/github/retrooper/packetevents/mc1914/Fabric1913ChainLoadEntrypoint.java new file mode 100644 index 0000000000..2961fc9b74 --- /dev/null +++ b/fabric/mc1194/src/main/java/io/github/retrooper/packetevents/mc1914/Fabric1913ChainLoadEntrypoint.java @@ -0,0 +1,28 @@ +package io.github.retrooper.packetevents.mc1914; + +import com.github.retrooper.packetevents.manager.server.ServerVersion; +import io.github.retrooper.packetevents.factory.fabric.FabricPacketEventsAPI; +import io.github.retrooper.packetevents.loader.ChainLoadData; +import io.github.retrooper.packetevents.loader.ChainLoadEntryPoint; +import io.github.retrooper.packetevents.manager.FabricServerManager; +import io.github.retrooper.packetevents.manager.registry.FabricRegistryManager; +import io.github.retrooper.packetevents.mc1140.manager.registry.Fabric1140ItemRegistry; +import io.github.retrooper.packetevents.mc1914.factory.fabric.Fabric1190ServerPlayerManager; +import io.github.retrooper.packetevents.mc1914.manager.registry.Fabric1193ItemRegistry; +import io.github.retrooper.packetevents.util.LazyHolder; + +public class Fabric1913ChainLoadEntrypoint implements ChainLoadEntryPoint { + + @Override + public void initialize(ChainLoadData chainLoadData) { + chainLoadData.setPlayerManagerIfNull(LazyHolder.simple(() -> new Fabric1190ServerPlayerManager(FabricPacketEventsAPI.getServerAPI()))); + chainLoadData.setRegistryManagerIfNull(LazyHolder.simple(() -> + new FabricRegistryManager(FabricServerManager.getVersionStatically().isNewerThan(ServerVersion.V_1_19_2) ? new Fabric1193ItemRegistry() : new Fabric1140ItemRegistry()) + )); + } + + @Override + public ServerVersion getNativeVersion() { + return ServerVersion.V_1_19_4; + } +} diff --git a/fabric/mc1194/src/main/java/io/github/retrooper/packetevents/mc1914/factory/fabric/Fabric1190ServerPlayerManager.java b/fabric/mc1194/src/main/java/io/github/retrooper/packetevents/mc1914/factory/fabric/Fabric1190ServerPlayerManager.java new file mode 100644 index 0000000000..21f5bf773d --- /dev/null +++ b/fabric/mc1194/src/main/java/io/github/retrooper/packetevents/mc1914/factory/fabric/Fabric1190ServerPlayerManager.java @@ -0,0 +1,37 @@ +/* + * This file is part of packetevents - https://github.com/retrooper/packetevents + * Copyright (C) 2024 retrooper and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.retrooper.packetevents.mc1914.factory.fabric; + +import com.github.retrooper.packetevents.PacketEventsAPI; +import io.github.retrooper.packetevents.mc1140.factory.fabric.Fabric1140ServerPlayerManager; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; + +public class Fabric1190ServerPlayerManager extends Fabric1140ServerPlayerManager { + + public Fabric1190ServerPlayerManager(PacketEventsAPI packetEventsAPI) { + super(packetEventsAPI); + } + + // new TextComponent -> Text.literal in 1.19 + @Override + public void disconnectPlayer(ServerPlayerEntity serverPlayerEntity, String message) { + serverPlayerEntity.networkHandler.disconnect(Text.literal(message)); + } +} diff --git a/fabric/mc1194/src/main/java/io/github/retrooper/packetevents/mc1914/manager/registry/Fabric1193ItemRegistry.java b/fabric/mc1194/src/main/java/io/github/retrooper/packetevents/mc1914/manager/registry/Fabric1193ItemRegistry.java new file mode 100644 index 0000000000..9fce9150f3 --- /dev/null +++ b/fabric/mc1194/src/main/java/io/github/retrooper/packetevents/mc1914/manager/registry/Fabric1193ItemRegistry.java @@ -0,0 +1,25 @@ +package io.github.retrooper.packetevents.mc1914.manager.registry; + +import com.github.retrooper.packetevents.manager.registry.ItemRegistry; +import com.github.retrooper.packetevents.protocol.item.type.ItemType; +import io.github.retrooper.packetevents.FabricItemType; +import net.minecraft.item.Item; +import net.minecraft.registry.Registries; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public class Fabric1193ItemRegistry implements ItemRegistry { + + @Override + public @Nullable ItemType getByName(String name) { + Optional item = Registries.ITEM.getOrEmpty(Identifier.splitOn(name, ':')); + return item.isPresent() ? new FabricItemType(item.get()) : null; + } + + @Override + public @Nullable ItemType getById(int id) { + return null; + } +} diff --git a/fabric/mc1194/src/main/java/io/github/retrooper/packetevents/mc1914/mixin/ClientConnectionMixin.java b/fabric/mc1194/src/main/java/io/github/retrooper/packetevents/mc1914/mixin/ClientConnectionMixin.java new file mode 100644 index 0000000000..5b55b0ec6f --- /dev/null +++ b/fabric/mc1194/src/main/java/io/github/retrooper/packetevents/mc1914/mixin/ClientConnectionMixin.java @@ -0,0 +1,34 @@ +package io.github.retrooper.packetevents.mc1914.mixin; + +import com.llamalad7.mixinextras.sugar.Local; +import io.github.retrooper.packetevents.util.FabricInjectionUtil; +import io.netty.channel.ChannelPipeline; +import me.fallenbreath.conditionalmixin.api.annotation.Condition; +import me.fallenbreath.conditionalmixin.api.annotation.Restriction; +import net.minecraft.network.ClientConnection; +import net.minecraft.network.NetworkSide; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(value = ClientConnection.class, priority = 1500) // priority to inject after Via +@Restriction( + require = { + @Condition(value = "minecraft", versionPredicates = {">1.19.3"}), + } +) +public class ClientConnectionMixin { + @Inject( + method = "addHandlers*", + at = @At("TAIL"), + require = 1 + ) + private static void addHandlers( + CallbackInfo ci, + @Local(ordinal = 0, argsOnly = true) ChannelPipeline pipeline, + @Local(ordinal = 0, argsOnly = true) NetworkSide flow + ) { + FabricInjectionUtil.injectAtPipelineBuilder(pipeline, flow); + } +} \ No newline at end of file diff --git a/fabric/mc1194/src/main/resources/fabric.mod.json b/fabric/mc1194/src/main/resources/fabric.mod.json new file mode 100644 index 0000000000..3333e4a001 --- /dev/null +++ b/fabric/mc1194/src/main/resources/fabric.mod.json @@ -0,0 +1,26 @@ +{ + "schemaVersion": 1, + "id": "${modName}", + "version": "${version}", + "name": "PacketEvents dependency for 1.19+ compiled against ${minecraft_version}", + "description": "", + "authors": [ + "retrooper" + ], + "contact": {}, + "license": "GPL-3.0", + "environment": "*", + "entrypoints": { + "peMainChainLoad": [ + "io.github.retrooper.packetevents.mc1914.Fabric1913ChainLoadEntrypoint" + ] + }, + "mixins": [ + "packetevents-mc1914.mixins.json" + ], + "accessWidener": "packetevents.accesswidener", + "depends": { + "fabricloader": "*", + "minecraft": ">=1.19" + } +} diff --git a/fabric/mc1194/src/main/resources/packetevents-mc1914.mixins.json b/fabric/mc1194/src/main/resources/packetevents-mc1914.mixins.json new file mode 100644 index 0000000000..a73930fafa --- /dev/null +++ b/fabric/mc1194/src/main/resources/packetevents-mc1914.mixins.json @@ -0,0 +1,13 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "io.github.retrooper.packetevents.mc1914.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "ClientConnectionMixin" + ], + "injectors": { + "defaultRequire": 1 + }, + "plugin": "io.github.retrooper.packetevents.manager.PacketEventsMixinManager" +} diff --git a/fabric/mc1194/src/main/resources/packetevents.accesswidener b/fabric/mc1194/src/main/resources/packetevents.accesswidener new file mode 100644 index 0000000000..3c32feda60 --- /dev/null +++ b/fabric/mc1194/src/main/resources/packetevents.accesswidener @@ -0,0 +1,9 @@ +accessWidener v2 named + + +# fields +accessible field net/minecraft/network/ClientConnection channel Lio/netty/channel/Channel; + +# methods + +# classes diff --git a/fabric/mc1202/build.gradle.kts b/fabric/mc1202/build.gradle.kts new file mode 100644 index 0000000000..765bc2f7fa --- /dev/null +++ b/fabric/mc1202/build.gradle.kts @@ -0,0 +1,20 @@ +val minecraft_version: String by project +val yarn_mappings: String by project + +dependencies { + compileOnly(project(":fabric:mc1140", configuration = "namedElements")) + + // To change the versions, see the gradle.properties file + minecraft("com.mojang:minecraft:$minecraft_version") + mappings("net.fabricmc:yarn:$yarn_mappings") +} + +loom { + splitEnvironmentSourceSets() + mods { + register("packetevents-${project.name}") { + sourceSet(sourceSets.main.get()) + sourceSet(sourceSets.maybeCreate("client")) + } + } +} \ No newline at end of file diff --git a/fabric/mc1202/gradle.properties b/fabric/mc1202/gradle.properties new file mode 100644 index 0000000000..2680af4858 --- /dev/null +++ b/fabric/mc1202/gradle.properties @@ -0,0 +1,8 @@ +# Done to increase the memory available to gradle. +org.gradle.jvmargs=-Xmx1G +org.gradle.parallel=true + +# Fabric Properties +# check these on https://fabricmc.net/develop +minecraft_version=1.20.2 +yarn_mappings=1.20.2+build.4:v2 diff --git a/fabric/mc1202/src/client/java/io/github/retrooper/packetevents/mc1202/factory/fabric/ClientPlayerNetworkHandlerMixin.java b/fabric/mc1202/src/client/java/io/github/retrooper/packetevents/mc1202/factory/fabric/ClientPlayerNetworkHandlerMixin.java new file mode 100644 index 0000000000..218ef532f2 --- /dev/null +++ b/fabric/mc1202/src/client/java/io/github/retrooper/packetevents/mc1202/factory/fabric/ClientPlayerNetworkHandlerMixin.java @@ -0,0 +1,70 @@ +/* + * This file is part of packetevents - https://github.com/retrooper/packetevents + * Copyright (C) 2024 retrooper and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.retrooper.packetevents.mc1202.factory.fabric; + +import com.llamalad7.mixinextras.sugar.Local; +import io.github.retrooper.packetevents.factory.fabric.FabricPacketEventsAPI; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.network.ClientConnection; +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + + +@Mixin(ClientPlayNetworkHandler.class) +public abstract class ClientPlayerNetworkHandlerMixin { + + @Shadow public abstract ClientConnection getConnection(); + + /* + * @reason Associate connection instance with player instance + */ + @Inject( + method = "onGameJoin", + at = @At( + value = "FIELD", + opcode = Opcodes.PUTFIELD, + target = "Lnet/minecraft/client/MinecraftClient;player:Lnet/minecraft/client/network/ClientPlayerEntity;", + shift = At.Shift.AFTER + ) + ) + private void postLoginPlayerConstruct(CallbackInfo ci) { + FabricPacketEventsAPI.getClientAPI().getInjector().setPlayer(this.getConnection().channel, MinecraftClient.getInstance().player); + } + + /** + * @reason Minecraft creates a new player instance on respawn + */ + @Inject( + method = "onPlayerRespawn", + at = @At( + // inject immediately after new player instance has been created + value = "INVOKE", + target = "Lnet/minecraft/client/network/ClientPlayerEntity;setId(I)V" + ) + ) + private void postRespawnPlayerConstruct(CallbackInfo ci, @Local(ordinal = 0) ClientPlayerEntity player) { + FabricPacketEventsAPI.getClientAPI().getInjector().setPlayer(this.getConnection().channel, player); + } +} diff --git a/fabric/mc1202/src/main/java/io/github/retrooper/packetevents/mc1202/Fabric1202ChainLoadEntrypoint.java b/fabric/mc1202/src/main/java/io/github/retrooper/packetevents/mc1202/Fabric1202ChainLoadEntrypoint.java new file mode 100644 index 0000000000..919cff33e5 --- /dev/null +++ b/fabric/mc1202/src/main/java/io/github/retrooper/packetevents/mc1202/Fabric1202ChainLoadEntrypoint.java @@ -0,0 +1,24 @@ +package io.github.retrooper.packetevents.mc1202; + +import com.github.retrooper.packetevents.manager.server.ServerVersion; +import io.github.retrooper.packetevents.factory.fabric.FabricPacketEventsAPI; +import io.github.retrooper.packetevents.manager.AbstractFabricPlayerManager; +import io.github.retrooper.packetevents.util.LazyHolder; +import io.github.retrooper.packetevents.loader.ChainLoadData; +import io.github.retrooper.packetevents.loader.ChainLoadEntryPoint; +import io.github.retrooper.packetevents.mc1202.factory.fabric.Fabric1202ServerPlayerManager; + +public class Fabric1202ChainLoadEntrypoint implements ChainLoadEntryPoint { + + protected LazyHolder playerManagerAbstractLazyHolder = LazyHolder.simple(() -> new Fabric1202ServerPlayerManager(FabricPacketEventsAPI.getServerAPI())); + + @Override + public void initialize(ChainLoadData chainLoadData) { + chainLoadData.setPlayerManagerIfNull(playerManagerAbstractLazyHolder); + } + + @Override + public ServerVersion getNativeVersion() { + return ServerVersion.V_1_20_2; + } +} diff --git a/fabric/mc1202/src/main/java/io/github/retrooper/packetevents/mc1202/factory/fabric/Fabric1202ServerPlayerManager.java b/fabric/mc1202/src/main/java/io/github/retrooper/packetevents/mc1202/factory/fabric/Fabric1202ServerPlayerManager.java new file mode 100644 index 0000000000..bdd717797b --- /dev/null +++ b/fabric/mc1202/src/main/java/io/github/retrooper/packetevents/mc1202/factory/fabric/Fabric1202ServerPlayerManager.java @@ -0,0 +1,54 @@ +/* + * This file is part of packetevents - https://github.com/retrooper/packetevents + * Copyright (C) 2024 retrooper and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.retrooper.packetevents.mc1202.factory.fabric; + +import com.github.retrooper.packetevents.PacketEventsAPI; +import io.github.retrooper.packetevents.mc1140.factory.fabric.Fabric1140ServerPlayerManager; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; +import org.jetbrains.annotations.NotNull; + +public class Fabric1202ServerPlayerManager extends Fabric1140ServerPlayerManager { + + public Fabric1202ServerPlayerManager(PacketEventsAPI packetEventsAPI) { + super(packetEventsAPI); + } + + @Override + public int getPing(@NotNull Object player) { + if (player instanceof ServerPlayerEntity) { + return ((ServerPlayerEntity) player).networkHandler.getLatency(); + } + throw new UnsupportedOperationException("Unsupported player implementation: " + player); + } + + @Override + public Object getChannel(@NotNull Object player) { + if (player instanceof ServerPlayerEntity) { + return ((ServerPlayerEntity) player).networkHandler.connection.channel; + } + throw new UnsupportedOperationException("Unsupported player implementation: " + player); + } + + // disconnect method moved from ServerPlayNetworkHandler -> ServerCommonNetworkHandler in 1.20.2 + @Override + public void disconnectPlayer(ServerPlayerEntity serverPlayerEntity, String message) { + serverPlayerEntity.networkHandler.disconnect(Text.literal(message)); + } +} diff --git a/fabric/mc1202/src/main/java/io/github/retrooper/packetevents/mc1202/mixin/PlayerManagerRespawnMixin.java b/fabric/mc1202/src/main/java/io/github/retrooper/packetevents/mc1202/mixin/PlayerManagerRespawnMixin.java new file mode 100644 index 0000000000..e6a9677b19 --- /dev/null +++ b/fabric/mc1202/src/main/java/io/github/retrooper/packetevents/mc1202/mixin/PlayerManagerRespawnMixin.java @@ -0,0 +1,32 @@ +package io.github.retrooper.packetevents.mc1202.mixin; + +import io.github.retrooper.packetevents.factory.fabric.FabricPacketEventsAPI; +import io.netty.channel.Channel; +import net.minecraft.server.PlayerManager; +import net.minecraft.server.network.ServerPlayerEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(PlayerManager.class) +public class PlayerManagerRespawnMixin { + /** Handles grabbing new player object on respawns for 1.20.2+, a separate mixin is required + * because the location of the field player.connection.connection changes + * from inheritance ServerGamePacketListenerImpl <- ServerCommonPacketListenerImpl thus breaking intermediary compatability + * + * @reason Minecraft creates a new player instance on respawn + */ + @Inject( + method = "respawnPlayer*", + at = @At("RETURN"), + require = 1 + ) + private void postRespawn(CallbackInfoReturnable cir) { + ServerPlayerEntity player = cir.getReturnValue(); + Channel channel = player.networkHandler.connection.channel; + FabricPacketEventsAPI.getServerAPI().getInjector().setPlayer(channel, player); + } +} + + diff --git a/fabric/mc1202/src/main/resources/fabric.mod.json b/fabric/mc1202/src/main/resources/fabric.mod.json new file mode 100644 index 0000000000..6cae6dfacc --- /dev/null +++ b/fabric/mc1202/src/main/resources/fabric.mod.json @@ -0,0 +1,26 @@ +{ + "schemaVersion": 1, + "id": "${modName}", + "version": "${version}", + "name": "PacketEvents dependency for ${minecraft_version}+", + "description": "", + "authors": [ + "retrooper" + ], + "contact": {}, + "license": "GPL-3.0", + "environment": "*", + "entrypoints": { + "peMainChainLoad": [ + "io.github.retrooper.packetevents.mc1202.Fabric1202ChainLoadEntrypoint" + ] + }, + "mixins": [ + "packetevents-mc1202.mixins.json" + ], + "accessWidener": "packetevents.accesswidener", + "depends": { + "fabricloader": "*", + "minecraft": ">=1.20.2" + } +} diff --git a/fabric/mc1202/src/main/resources/packetevents-mc1202.mixins.json b/fabric/mc1202/src/main/resources/packetevents-mc1202.mixins.json new file mode 100644 index 0000000000..a43ca18cc6 --- /dev/null +++ b/fabric/mc1202/src/main/resources/packetevents-mc1202.mixins.json @@ -0,0 +1,13 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "io.github.retrooper.packetevents.mc1202.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "PlayerManagerRespawnMixin" + ], + "injectors": { + "defaultRequire": 1 + }, + "plugin": "io.github.retrooper.packetevents.manager.PacketEventsMixinManager" +} diff --git a/fabric/mc1202/src/main/resources/packetevents.accesswidener b/fabric/mc1202/src/main/resources/packetevents.accesswidener new file mode 100644 index 0000000000..f8686afcf9 --- /dev/null +++ b/fabric/mc1202/src/main/resources/packetevents.accesswidener @@ -0,0 +1,9 @@ +accessWidener v2 named + + +# fields +accessible field net/minecraft/server/network/ServerCommonNetworkHandler connection Lnet/minecraft/network/ClientConnection; +accessible field net/minecraft/network/ClientConnection channel Lio/netty/channel/Channel; +# methods + +# classes diff --git a/fabric/mc1211/build.gradle.kts b/fabric/mc1211/build.gradle.kts new file mode 100644 index 0000000000..35a7903df9 --- /dev/null +++ b/fabric/mc1211/build.gradle.kts @@ -0,0 +1,21 @@ +val minecraft_version: String by project +val yarn_mappings: String by project + +dependencies { + compileOnly(project(":fabric:mc1202", configuration = "namedElements")) + compileOnly(project(":fabric:mc1140", configuration = "namedElements")) + + // To change the versions, see the gradle.properties file + minecraft("com.mojang:minecraft:$minecraft_version") + mappings("net.fabricmc:yarn:$yarn_mappings") +} + +loom { + splitEnvironmentSourceSets() + mods { + register("packetevents-${project.name}") { + sourceSet(sourceSets.main.get()) + sourceSet(sourceSets.maybeCreate("client")) + } + } +} \ No newline at end of file diff --git a/fabric/mc1211/gradle.properties b/fabric/mc1211/gradle.properties new file mode 100644 index 0000000000..90fd019d98 --- /dev/null +++ b/fabric/mc1211/gradle.properties @@ -0,0 +1,8 @@ +# Done to increase the memory available to gradle. +org.gradle.jvmargs=-Xmx1G +org.gradle.parallel=true + +# Fabric Properties +# check these on https://fabricmc.net/develop +minecraft_version=1.21.1 +yarn_mappings=1.21.1+build.3:v2 \ No newline at end of file diff --git a/fabric/mc1211/src/client/java/io/github/retrooper/packetevents/mc1211/Fabric1205ClientChainLoadEntrypoint.java b/fabric/mc1211/src/client/java/io/github/retrooper/packetevents/mc1211/Fabric1205ClientChainLoadEntrypoint.java new file mode 100644 index 0000000000..937d17f17f --- /dev/null +++ b/fabric/mc1211/src/client/java/io/github/retrooper/packetevents/mc1211/Fabric1205ClientChainLoadEntrypoint.java @@ -0,0 +1,21 @@ +package io.github.retrooper.packetevents.mc1211; + +import com.github.retrooper.packetevents.manager.server.ServerVersion; +import io.github.retrooper.packetevents.factory.fabric.FabricPacketEventsAPI; +import io.github.retrooper.packetevents.loader.ChainLoadEntryPoint; +import io.github.retrooper.packetevents.util.LazyHolder; +import io.github.retrooper.packetevents.loader.ChainLoadData; +import io.github.retrooper.packetevents.mc1211.factory.fabric.Fabric1205ClientPlayerManager; + +public class Fabric1205ClientChainLoadEntrypoint implements ChainLoadEntryPoint { + + @Override + public void initialize(ChainLoadData chainLoadData) { + chainLoadData.setClientPlayerManagerIfNull(LazyHolder.simple(() -> new Fabric1205ClientPlayerManager(FabricPacketEventsAPI.getClientAPI()))); + } + + @Override + public ServerVersion getNativeVersion() { + return ServerVersion.V_1_21_1; + } +} diff --git a/fabric/mc1211/src/client/java/io/github/retrooper/packetevents/mc1211/factory/fabric/Fabric1205ClientPlayerManager.java b/fabric/mc1211/src/client/java/io/github/retrooper/packetevents/mc1211/factory/fabric/Fabric1205ClientPlayerManager.java new file mode 100644 index 0000000000..f9a7f3ae3b --- /dev/null +++ b/fabric/mc1211/src/client/java/io/github/retrooper/packetevents/mc1211/factory/fabric/Fabric1205ClientPlayerManager.java @@ -0,0 +1,56 @@ +/* + * This file is part of packetevents - https://github.com/retrooper/packetevents + * Copyright (C) 2024 retrooper and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.retrooper.packetevents.mc1211.factory.fabric; + +import com.github.retrooper.packetevents.PacketEventsAPI; +import io.github.retrooper.packetevents.mc1202.factory.fabric.Fabric1202ServerPlayerManager; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.network.PlayerListEntry; +import org.jetbrains.annotations.NotNull; + +public class Fabric1205ClientPlayerManager extends Fabric1202ServerPlayerManager { + + public Fabric1205ClientPlayerManager(PacketEventsAPI packetEventsAPI) { + super(packetEventsAPI); + } + + @Override + public int getPing(@NotNull Object playerObj) { + if (playerObj instanceof ClientPlayerEntity player) { + PlayerListEntry info = player.networkHandler.getPlayerListEntry(player.getUuid()); + if (info != null) { + return info.getLatency(); + } + // if the server doesn't show the player info of + // the player itself, try to fall back to potential + // latency sampling data - which is often not present + return (int) MinecraftClient.getInstance().getDebugHud().getPingLog().get(0); + } + return super.getPing(playerObj); + } + + @Override + public Object getChannel(@NotNull Object player) { + if (player instanceof ClientPlayerEntity) { + return ((ClientPlayerEntity) player).networkHandler.getConnection().channel; + } + return super.getChannel(player); + } +} diff --git a/fabric/mc1211/src/main/resources/fabric.mod.json b/fabric/mc1211/src/main/resources/fabric.mod.json new file mode 100644 index 0000000000..53ae9aa1a1 --- /dev/null +++ b/fabric/mc1211/src/main/resources/fabric.mod.json @@ -0,0 +1,23 @@ +{ + "schemaVersion": 1, + "id": "${modName}", + "version": "${version}", + "name": "PacketEvents dependency for 1.20.5+, built against 1.21.1", + "description": "", + "authors": [ + "retrooper" + ], + "contact": {}, + "license": "GPL-3.0", + "environment": "*", + "entrypoints": { + "peClientChainLoad": [ + "io.github.retrooper.packetevents.mc1211.Fabric1205ClientChainLoadEntrypoint" + ] + }, + "accessWidener": "packetevents.accesswidener", + "depends": { + "fabricloader": "*", + "minecraft": ">=1.20.5" + } +} diff --git a/fabric/mc1211/src/main/resources/packetevents.accesswidener b/fabric/mc1211/src/main/resources/packetevents.accesswidener new file mode 100644 index 0000000000..3c32feda60 --- /dev/null +++ b/fabric/mc1211/src/main/resources/packetevents.accesswidener @@ -0,0 +1,9 @@ +accessWidener v2 named + + +# fields +accessible field net/minecraft/network/ClientConnection channel Lio/netty/channel/Channel; + +# methods + +# classes diff --git a/fabric/mc1215/build.gradle.kts b/fabric/mc1215/build.gradle.kts new file mode 100644 index 0000000000..96da42c684 --- /dev/null +++ b/fabric/mc1215/build.gradle.kts @@ -0,0 +1,20 @@ +val minecraft_version: String by project +val yarn_mappings: String by project + +dependencies { + compileOnly(project(":fabric:mc1211", configuration = "namedElements")) + + // To change the versions, see the gradle.properties file + minecraft("com.mojang:minecraft:$minecraft_version") + mappings("net.fabricmc:yarn:$yarn_mappings") +} + +loom { + splitEnvironmentSourceSets() + mods { + register("packetevents-${project.name}") { + sourceSet(sourceSets.main.get()) + sourceSet(sourceSets.maybeCreate("client")) + } + } +} \ No newline at end of file diff --git a/fabric/mc1215/gradle.properties b/fabric/mc1215/gradle.properties new file mode 100644 index 0000000000..69cb17f61c --- /dev/null +++ b/fabric/mc1215/gradle.properties @@ -0,0 +1,8 @@ +# Done to increase the memory available to gradle. +org.gradle.jvmargs=-Xmx1G +org.gradle.parallel=true + +# Fabric Properties +# check these on https://fabricmc.net/develop +minecraft_version=1.21.5 +yarn_mappings=1.21.5+build.1:v2 \ No newline at end of file diff --git a/fabric/mc1215/src/main/java/io/github/retrooper/packetevents/mc1215/Fabric1212ChainLoadEntrypoint.java b/fabric/mc1215/src/main/java/io/github/retrooper/packetevents/mc1215/Fabric1212ChainLoadEntrypoint.java new file mode 100644 index 0000000000..d320c73c30 --- /dev/null +++ b/fabric/mc1215/src/main/java/io/github/retrooper/packetevents/mc1215/Fabric1212ChainLoadEntrypoint.java @@ -0,0 +1,23 @@ +package io.github.retrooper.packetevents.mc1215; + +import com.github.retrooper.packetevents.manager.server.ServerVersion; +import io.github.retrooper.packetevents.util.LazyHolder; +import io.github.retrooper.packetevents.loader.ChainLoadData; +import io.github.retrooper.packetevents.loader.ChainLoadEntryPoint; +import io.github.retrooper.packetevents.manager.registry.FabricRegistryManager; +import io.github.retrooper.packetevents.mc1215.manager.registry.Fabric1212ItemRegistry; + +public class Fabric1212ChainLoadEntrypoint implements ChainLoadEntryPoint { + + @Override + public void initialize(ChainLoadData chainLoadData) { + chainLoadData.setRegistryManagerIfNull(LazyHolder.simple(() -> + new FabricRegistryManager(new Fabric1212ItemRegistry()) + )); + } + + @Override + public ServerVersion getNativeVersion() { + return ServerVersion.V_1_21_5; + } +} diff --git a/fabric/mc1215/src/main/java/io/github/retrooper/packetevents/mc1215/manager/registry/Fabric1212ItemRegistry.java b/fabric/mc1215/src/main/java/io/github/retrooper/packetevents/mc1215/manager/registry/Fabric1212ItemRegistry.java new file mode 100644 index 0000000000..ee5f64bcfc --- /dev/null +++ b/fabric/mc1215/src/main/java/io/github/retrooper/packetevents/mc1215/manager/registry/Fabric1212ItemRegistry.java @@ -0,0 +1,27 @@ +package io.github.retrooper.packetevents.mc1215.manager.registry; + +import com.github.retrooper.packetevents.manager.registry.ItemRegistry; +import com.github.retrooper.packetevents.protocol.item.type.ItemType; +import io.github.retrooper.packetevents.FabricItemType; +import net.minecraft.item.Item; +import net.minecraft.registry.Registries; +import net.minecraft.registry.entry.RegistryEntry; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.Nullable; + +import java.util.Optional; + +public class Fabric1212ItemRegistry implements ItemRegistry { + + @Override + public @Nullable ItemType getByName(String name) { + Optional> item = Registries.ITEM.getEntry(Identifier.tryParse(name)); // returns default entry if item doesn't exist + return item.map(itemReference -> new FabricItemType(itemReference.value())).orElse(null); + } + + @Override + public @Nullable ItemType getById(int id) { + Optional> item = Registries.ITEM.getEntry(id); // returns default entry if item doesn't exist + return item.map(itemReference -> new FabricItemType(itemReference.value())).orElse(null); + } +} \ No newline at end of file diff --git a/fabric/mc1215/src/main/resources/fabric.mod.json b/fabric/mc1215/src/main/resources/fabric.mod.json new file mode 100644 index 0000000000..da12739935 --- /dev/null +++ b/fabric/mc1215/src/main/resources/fabric.mod.json @@ -0,0 +1,23 @@ +{ + "schemaVersion": 1, + "id": "${modName}", + "version": "${version}", + "name": "PacketEvents dependency for 1.21.2+, compiled against ${minecraft_version}", + "description": "", + "authors": [ + "retrooper" + ], + "contact": {}, + "license": "GPL-3.0", + "environment": "*", + "entrypoints": { + "peMainChainLoad": [ + "io.github.retrooper.packetevents.mc1215.Fabric1212ChainLoadEntrypoint" + ] + }, + "accessWidener": "packetevents.accesswidener", + "depends": { + "fabricloader": "*", + "minecraft": ">=1.21.2" + } +} diff --git a/fabric/mc1215/src/main/resources/packetevents.accesswidener b/fabric/mc1215/src/main/resources/packetevents.accesswidener new file mode 100644 index 0000000000..0a799b02a8 --- /dev/null +++ b/fabric/mc1215/src/main/resources/packetevents.accesswidener @@ -0,0 +1,2 @@ +accessWidener v2 named +accessible field net/minecraft/network/ClientConnection channel Lio/netty/channel/Channel; \ No newline at end of file diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/FabricItemType.java b/fabric/src/main/java/io/github/retrooper/packetevents/FabricItemType.java new file mode 100644 index 0000000000..68ed8439b2 --- /dev/null +++ b/fabric/src/main/java/io/github/retrooper/packetevents/FabricItemType.java @@ -0,0 +1,67 @@ +package io.github.retrooper.packetevents; + +import com.github.retrooper.packetevents.protocol.item.type.ItemType; +import com.github.retrooper.packetevents.protocol.item.type.ItemTypes; +import com.github.retrooper.packetevents.protocol.player.ClientVersion; +import com.github.retrooper.packetevents.protocol.world.states.type.StateType; +import com.github.retrooper.packetevents.resources.ResourceLocation; +import java.util.Set; + +import net.minecraft.item.Item; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import org.jetbrains.annotations.Nullable; + +public class FabricItemType implements ItemType { + + private final Item item; + + public FabricItemType(Item item) { + this.item = item; + } + + @Override + public int getMaxAmount() { + return item.getMaxAmount(); + } + + @Override + public int getMaxDurability() { + return item.getDurability(); + } + + @Override + public ItemType getCraftRemainder() { + return new FabricItemType(item.getRecipeRemainder()); + } + + @Override + public @Nullable StateType getPlacedType() { + throw new UnsupportedOperationException(); + } + + @Override + public Set getAttributes() { + throw new UnsupportedOperationException(); + } + + @Override + public ResourceLocation getName() { + Identifier resourceLocation = Registry.ITEM.getId(item); + return new ResourceLocation(resourceLocation.getNamespace(), resourceLocation.getPath()); + } + + @Override + public int getId(ClientVersion version) { + return Registry.ITEM.getRawId(item); + } + + @Override + public boolean equals(Object o) { + if (o instanceof FabricItemType) { + FabricItemType fabricItemType = (FabricItemType) o; + return this.item == fabricItemType.item; + } + return false; + } +} diff --git a/fabric/src/client/java/io/github/retrooper/packetevents/PacketEventsClientMod.java b/fabric/src/main/java/io/github/retrooper/packetevents/PacketEventsClientMod.java similarity index 57% rename from fabric/src/client/java/io/github/retrooper/packetevents/PacketEventsClientMod.java rename to fabric/src/main/java/io/github/retrooper/packetevents/PacketEventsClientMod.java index 6fe46ecc5b..d0852d0391 100644 --- a/fabric/src/client/java/io/github/retrooper/packetevents/PacketEventsClientMod.java +++ b/fabric/src/main/java/io/github/retrooper/packetevents/PacketEventsClientMod.java @@ -18,27 +18,13 @@ package io.github.retrooper.packetevents; -import com.github.retrooper.packetevents.PacketEvents; -import io.github.retrooper.packetevents.factory.fabric.FabricClientPlayerManager; import io.github.retrooper.packetevents.factory.fabric.FabricPacketEventsAPI; -import io.github.retrooper.packetevents.impl.netty.manager.player.PlayerManagerAbstract; import net.fabricmc.api.EnvType; -import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint; -public class PacketEventsClientMod implements PreLaunchEntrypoint { +public class PacketEventsClientMod { + // Exists solely for backwards compatability for mods that may have used this internal method public static FabricPacketEventsAPI constructApi(String modid) { - return new FabricPacketEventsAPI(modid, EnvType.CLIENT) { - @Override - protected PlayerManagerAbstract constructPlayerManager() { - return new FabricClientPlayerManager(); - } - }; - } - - @Override - public void onPreLaunch() { - PacketEvents.setAPI(constructApi(PacketEventsMod.MOD_ID)); - PacketEvents.getAPI().load(); + return new FabricPacketEventsAPI(modid, EnvType.CLIENT); } } diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/PacketEventsMod.java b/fabric/src/main/java/io/github/retrooper/packetevents/PacketEventsMod.java index 4e83dd3b57..70fc17afe9 100644 --- a/fabric/src/main/java/io/github/retrooper/packetevents/PacketEventsMod.java +++ b/fabric/src/main/java/io/github/retrooper/packetevents/PacketEventsMod.java @@ -20,24 +20,85 @@ import com.github.retrooper.packetevents.PacketEvents; import com.github.retrooper.packetevents.PacketEventsAPI; +import io.github.retrooper.packetevents.factory.fabric.FabricPacketEventsAPI; +import io.github.retrooper.packetevents.factory.fabric.FabricPacketEventsAPIManagerFactory; +import io.github.retrooper.packetevents.loader.ChainLoadData; +import io.github.retrooper.packetevents.loader.ChainLoadEntryPoint; +import net.fabricmc.api.EnvType; import net.fabricmc.api.ModInitializer; import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint; +import java.util.List; + public class PacketEventsMod implements PreLaunchEntrypoint, ModInitializer { + public static PacketEventsMod INSTANCE; public static final String MOD_ID = "packetevents"; @Override public void onPreLaunch() { + INSTANCE = this; FabricLoader loader = FabricLoader.getInstance(); - String entrypoint = switch (loader.getEnvironmentType()) { - case CLIENT -> "pePreLaunchClient"; - case SERVER -> "pePreLaunchServer"; - }; - loader.invokeEntrypoints(entrypoint, - PreLaunchEntrypoint.class, - PreLaunchEntrypoint::onPreLaunch); + + String chainLoadEntryPointName = "peMainChainLoad"; + String clientChainLoadEntryPointName = "peClientChainLoad"; // For client-specific entrypoints + + // Collect peMainChainLoad entrypoints (always present) and sort by version + List mainChainLoadEntryPoints = loader.getEntrypoints(chainLoadEntryPointName, ChainLoadEntryPoint.class); + mainChainLoadEntryPoints.sort((a, b) -> b.getNativeVersion().getProtocolVersion() - a.getNativeVersion().getProtocolVersion()); + + List allEntryPoints; + switch (loader.getEnvironmentType()) { + case CLIENT -> { + // Collect clientChainLoad entrypoints (only on client, might be empty) and sort by version then append main entry points + List clientChainLoadEntryPoints = loader.getEntrypoints(clientChainLoadEntryPointName, ChainLoadEntryPoint.class); + clientChainLoadEntryPoints.sort((a, b) -> b.getNativeVersion().getProtocolVersion() - a.getNativeVersion().getProtocolVersion()); + clientChainLoadEntryPoints.addAll(mainChainLoadEntryPoints); + + // 1.21.1 Client -> 1.20.1 Client -> 1.21.4 Main -> 1.21.1 Main -> 1.20.1 Main + allEntryPoints = clientChainLoadEntryPoints; + } + case SERVER -> { + // 1.21.4 Main -> 1.21.1 Main -> 1.20.1 Main + allEntryPoints = mainChainLoadEntryPoints; + } + default -> throw new IllegalStateException("Unexpected value: " + loader.getEnvironmentType()); + } + + // Initialize single chainload data instance + ChainLoadData chainLoadData = new ChainLoadData(); + + // Execute all entrypoints using the same ChainLoadData instance + for (ChainLoadEntryPoint chainLoadEntryPoint : allEntryPoints) { + try { + chainLoadEntryPoint.initialize(chainLoadData); + } catch (Exception e) { + // Log error but continue with next entrypoint + System.err.println("Error processing entrypoint for version " + + chainLoadEntryPoint.getNativeVersion() + ": " + e.getMessage()); + e.printStackTrace(); + } + } + + // Ordinarily I wouldn't be using a static here but since we need to maintain compile-time backwards compatibility + // We need to preserve the ABI of FactoryPacketEventsAPI and do this static awfulness + FabricPacketEventsAPIManagerFactory.init(chainLoadData); + + FabricPacketEventsAPI fabricPacketEventsAPI = new FabricPacketEventsAPI(PacketEventsMod.MOD_ID, loader.getEnvironmentType()); + + PacketEvents.setAPI(fabricPacketEventsAPI); + PacketEvents.getAPI().load(); + + switch (loader.getEnvironmentType()) { + case CLIENT -> { + FabricPacketEventsAPI.setClientAPI(fabricPacketEventsAPI); + FabricPacketEventsAPI fabricServerPacketEventsAPI = new FabricPacketEventsAPI(PacketEventsMod.MOD_ID, EnvType.SERVER); + FabricPacketEventsAPI.setServerAPI(fabricPacketEventsAPI); + fabricServerPacketEventsAPI.load(); + } + case SERVER -> FabricPacketEventsAPI.setServerAPI(fabricPacketEventsAPI); + } } @Override @@ -46,5 +107,6 @@ public void onInitialize() { if (api != null) { api.init(); } + if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT) FabricPacketEventsAPI.getClientAPI().init(); } } diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/PacketEventsServerMod.java b/fabric/src/main/java/io/github/retrooper/packetevents/PacketEventsServerMod.java index d575d473d4..9f8453b587 100644 --- a/fabric/src/main/java/io/github/retrooper/packetevents/PacketEventsServerMod.java +++ b/fabric/src/main/java/io/github/retrooper/packetevents/PacketEventsServerMod.java @@ -20,18 +20,15 @@ import com.github.retrooper.packetevents.PacketEvents; import io.github.retrooper.packetevents.factory.fabric.FabricPacketEventsAPI; +import io.github.retrooper.packetevents.impl.netty.manager.player.PlayerManagerAbstract; +import java.util.function.Supplier; import net.fabricmc.api.EnvType; import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint; -public class PacketEventsServerMod implements PreLaunchEntrypoint { +public class PacketEventsServerMod { + // Exists solely for backwards compatability for mods that may have used this internal method public static FabricPacketEventsAPI constructApi(String modid) { return new FabricPacketEventsAPI(modid, EnvType.SERVER); } - - @Override - public void onPreLaunch() { - PacketEvents.setAPI(constructApi(PacketEventsMod.MOD_ID)); - PacketEvents.getAPI().load(); - } } diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/factory/fabric/FabricChannelInjector.java b/fabric/src/main/java/io/github/retrooper/packetevents/factory/fabric/FabricChannelInjector.java index a04c48add4..a9b8962a7e 100644 --- a/fabric/src/main/java/io/github/retrooper/packetevents/factory/fabric/FabricChannelInjector.java +++ b/fabric/src/main/java/io/github/retrooper/packetevents/factory/fabric/FabricChannelInjector.java @@ -18,7 +18,7 @@ package io.github.retrooper.packetevents.factory.fabric; -import com.github.retrooper.packetevents.PacketEvents; +import com.github.retrooper.packetevents.PacketEventsAPI; import com.github.retrooper.packetevents.injector.ChannelInjector; import com.github.retrooper.packetevents.protocol.PacketSide; import com.github.retrooper.packetevents.protocol.player.User; @@ -26,7 +26,7 @@ import io.github.retrooper.packetevents.handler.PacketEncoder; import io.netty.channel.Channel; import net.fabricmc.api.EnvType; -import net.minecraft.world.entity.player.Player; +import net.minecraft.entity.player.PlayerEntity; import static com.github.retrooper.packetevents.PacketEvents.DECODER_NAME; import static com.github.retrooper.packetevents.PacketEvents.ENCODER_NAME; @@ -34,8 +34,10 @@ public class FabricChannelInjector implements ChannelInjector { private final PacketSide packetSide; + private final PacketEventsAPI packetEventsAPI; - public FabricChannelInjector(EnvType environment) { + public FabricChannelInjector(PacketEventsAPI packetEventsAPI, EnvType environment) { + this.packetEventsAPI = packetEventsAPI; this.packetSide = switch (environment) { case SERVER -> PacketSide.SERVER; case CLIENT -> PacketSide.CLIENT; @@ -65,7 +67,7 @@ public boolean isPlayerSet(Object ch) { @Override public void updateUser(Object channel, User user) { - if (!PacketEvents.getAPI().getProtocolManager().hasChannel(channel)) { + if (!packetEventsAPI.getProtocolManager().hasChannel(channel)) { return; // this channel isn't injected by packetevents } Channel ch = (Channel) channel; @@ -75,12 +77,12 @@ public void updateUser(Object channel, User user) { @Override public void setPlayer(Object channel, Object player) { - if (!PacketEvents.getAPI().getProtocolManager().hasChannel(channel)) { + if (!packetEventsAPI.getProtocolManager().hasChannel(channel)) { return; // this channel isn't injected by packetevents } Channel ch = (Channel) channel; - ((PacketDecoder) ch.pipeline().get(DECODER_NAME)).player = (Player) player; - ((PacketEncoder) ch.pipeline().get(ENCODER_NAME)).player = (Player) player; + ((PacketDecoder) ch.pipeline().get(DECODER_NAME)).player = (PlayerEntity) player; + ((PacketEncoder) ch.pipeline().get(ENCODER_NAME)).player = (PlayerEntity) player; } @Override diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/factory/fabric/FabricLogger.java b/fabric/src/main/java/io/github/retrooper/packetevents/factory/fabric/FabricLogger.java deleted file mode 100644 index 6c6bdaff23..0000000000 --- a/fabric/src/main/java/io/github/retrooper/packetevents/factory/fabric/FabricLogger.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * This file is part of packetevents - https://github.com/retrooper/packetevents - * Copyright (C) 2024 retrooper and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package io.github.retrooper.packetevents.factory.fabric; - -import com.github.retrooper.packetevents.util.LogManager; -import net.kyori.adventure.text.format.NamedTextColor; -import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.event.Level; - -import java.util.Map; - -public class FabricLogger extends LogManager { - - private static final Map LEVEL_CONVERSION = Map.of( - java.util.logging.Level.FINEST, Level.TRACE, - java.util.logging.Level.FINER, Level.TRACE, - java.util.logging.Level.FINE, Level.DEBUG, - java.util.logging.Level.INFO, Level.INFO, - java.util.logging.Level.WARNING, Level.WARN, - java.util.logging.Level.SEVERE, Level.ERROR - ); - - private final Logger logger; - - public FabricLogger(Logger logger) { - this.logger = logger; - } - - @Override - protected void log(java.util.logging.Level level, @Nullable NamedTextColor color, String message) { - String plainMessage = STRIP_COLOR_PATTERN.matcher(message).replaceAll(""); - Level logLevel = LEVEL_CONVERSION.getOrDefault(level, Level.INFO); - this.logger.makeLoggingEventBuilder(logLevel).log(plainMessage); - } -} diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/factory/fabric/FabricPacketEventsAPI.java b/fabric/src/main/java/io/github/retrooper/packetevents/factory/fabric/FabricPacketEventsAPI.java index 0e506835db..7a71348087 100644 --- a/fabric/src/main/java/io/github/retrooper/packetevents/factory/fabric/FabricPacketEventsAPI.java +++ b/fabric/src/main/java/io/github/retrooper/packetevents/factory/fabric/FabricPacketEventsAPI.java @@ -21,66 +21,62 @@ import com.github.retrooper.packetevents.PacketEvents; import com.github.retrooper.packetevents.PacketEventsAPI; import com.github.retrooper.packetevents.injector.ChannelInjector; -import com.github.retrooper.packetevents.manager.InternalPacketListener; -import com.github.retrooper.packetevents.manager.player.PlayerManager; import com.github.retrooper.packetevents.manager.protocol.ProtocolManager; +import com.github.retrooper.packetevents.manager.registry.RegistryManager; import com.github.retrooper.packetevents.manager.server.ServerManager; import com.github.retrooper.packetevents.netty.NettyManager; +import com.github.retrooper.packetevents.protocol.PacketSide; import com.github.retrooper.packetevents.protocol.packettype.PacketType; import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState; import com.github.retrooper.packetevents.settings.PacketEventsSettings; import com.github.retrooper.packetevents.util.LogManager; +import io.github.retrooper.packetevents.PacketEventsMod; import io.github.retrooper.packetevents.impl.netty.NettyManagerImpl; -import io.github.retrooper.packetevents.impl.netty.manager.player.PlayerManagerAbstract; +import io.github.retrooper.packetevents.manager.AbstractFabricPlayerManager; +import io.github.retrooper.packetevents.manager.FabricLoggerManager; +import io.github.retrooper.packetevents.manager.FabricProtocolManager; +import io.github.retrooper.packetevents.manager.FabricServerManager; +import io.github.retrooper.packetevents.manager.InternalFabricPacketListener; +import io.github.retrooper.packetevents.manager.logger.jul.JULoggerFactory; +import io.github.retrooper.packetevents.util.viaversion.ViaVersionUtil; import net.fabricmc.api.EnvType; -import net.fabricmc.loader.api.FabricLoader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import net.fabricmc.api.ModInitializer; import java.util.Locale; +import java.util.logging.Logger; -public class FabricPacketEventsAPI extends PacketEventsAPI { - - private static final Logger LOGGER = LoggerFactory.getLogger("PacketEvents"); - +public class FabricPacketEventsAPI extends PacketEventsAPI { private final String modId; private final EnvType environment; private final PacketEventsSettings settings; private final ProtocolManager protocolManager; private final ServerManager serverManager; - private final PlayerManagerAbstract playerManager; private final ChannelInjector injector; private final NettyManager nettyManager = new NettyManagerImpl(); - private final LogManager logManager = new FabricLogger(LOGGER); + private final LogManager logManager = FabricLoggerManager.createModLogger("PacketEvents"); + private final Logger logger = JULoggerFactory.createLogger("PacketEvents"); private boolean loaded; private boolean initialized; private boolean terminated; - public FabricPacketEventsAPI(String modId, EnvType environment) { - this(modId, environment, new PacketEventsSettings()); - } + private static FabricPacketEventsAPI serverFabricAPI; + private static FabricPacketEventsAPI clientFabricAPI; public FabricPacketEventsAPI(String modId, EnvType environment, PacketEventsSettings settings) { this.modId = modId; this.environment = environment; this.settings = settings; - - this.protocolManager = new FabricProtocolManager(environment); + this.protocolManager = new FabricProtocolManager(this, environment); this.serverManager = this.constructServerManager(); - this.playerManager = this.constructPlayerManager(); - this.injector = new FabricChannelInjector(environment); + this.injector = new FabricChannelInjector(this, environment); } protected ServerManager constructServerManager() { return new FabricServerManager(); } - protected PlayerManagerAbstract constructPlayerManager() { - return new FabricPlayerManager(); - } - @Override public void load() { if (this.loaded) { @@ -98,7 +94,7 @@ public void load() { // register internal packet listener (should be the first listener) // this listener doesn't do any modifications to the packets, just reads data - this.getEventManager().registerListener(new InternalPacketListener()); + this.getEventManager().registerListener(new InternalFabricPacketListener()); this.loaded = true; } @@ -122,9 +118,19 @@ public void init() { PacketType.Play.Client.load(); PacketType.Play.Server.load(); + + // Let people override this, at their own risk + if (!"true".equalsIgnoreCase(System.getenv("PE_IGNORE_INCOMPATIBILITY"))) { + checkCompatibility(); + } + this.initialized = true; } + private void checkCompatibility() { + ViaVersionUtil.checkIfViaIsPresent(); + } + @Override public boolean isInitialized() { return this.initialized; @@ -145,9 +151,11 @@ public boolean isTerminated() { return this.terminated; } + // Returning ModInitializer instance makes getClass().getClassLoader() return KnotClassLoader + // Which allows us to correctly check for existence of Via, Geyser, etc... @Override - public FabricLoader getPlugin() { - return FabricLoader.getInstance(); + public ModInitializer getPlugin() { + return PacketEventsMod.INSTANCE; } @Override @@ -166,8 +174,13 @@ public LogManager getLogManager() { } @Override - public PlayerManager getPlayerManager() { - return this.playerManager; + public Logger getLogger() { + return this.logger; + } + + @Override + public AbstractFabricPlayerManager getPlayerManager() { + return this.environment == EnvType.SERVER ? FabricPacketEventsAPIManagerFactory.getLazyPlayerManagerHolder().get() : FabricPacketEventsAPIManagerFactory.getClientLazyPlayerManagerHolder().get(); } @Override @@ -184,4 +197,33 @@ public PacketEventsSettings getSettings() { public NettyManager getNettyManager() { return this.nettyManager; } + + @Override + public RegistryManager getRegistryManager() { + return FabricPacketEventsAPIManagerFactory.getLazyRegistryManagerHolder().get(); + } + + public static FabricPacketEventsAPI getAPI(PacketSide pipelineSide) { + return pipelineSide == PacketSide.CLIENT ? clientFabricAPI : serverFabricAPI; + } + + public static void setClientAPI(FabricPacketEventsAPI fabricPacketEventsAPI) { + clientFabricAPI = fabricPacketEventsAPI; + } + + public static void setServerAPI(FabricPacketEventsAPI fabricPacketEventsAPI) { + serverFabricAPI = fabricPacketEventsAPI; + } + + public static FabricPacketEventsAPI getClientAPI() { + return clientFabricAPI; + } + + public static FabricPacketEventsAPI getServerAPI() { + return serverFabricAPI; + } + + public FabricPacketEventsAPI(String modId, EnvType environment) { + this(modId, environment, new PacketEventsSettings()); + } } diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/factory/fabric/FabricPacketEventsAPIManagerFactory.java b/fabric/src/main/java/io/github/retrooper/packetevents/factory/fabric/FabricPacketEventsAPIManagerFactory.java new file mode 100644 index 0000000000..3cc137a900 --- /dev/null +++ b/fabric/src/main/java/io/github/retrooper/packetevents/factory/fabric/FabricPacketEventsAPIManagerFactory.java @@ -0,0 +1,33 @@ +package io.github.retrooper.packetevents.factory.fabric; + +import com.github.retrooper.packetevents.manager.registry.RegistryManager; +import io.github.retrooper.packetevents.manager.AbstractFabricPlayerManager; +import io.github.retrooper.packetevents.util.LazyHolder; +import io.github.retrooper.packetevents.impl.netty.manager.player.PlayerManagerAbstract; +import io.github.retrooper.packetevents.loader.ChainLoadData; + +public class FabricPacketEventsAPIManagerFactory { + // TODO, refactor if booky and retrooper approve, bad design having settable static field + // exists to maintain 100% backward compatability + private static LazyHolder lazyPlayerManagerHolder = () -> null; + private static LazyHolder lazyClientPlayerManagerHolder = () -> null; + private static LazyHolder registryManagerLazyHolder = () -> null; + + public static LazyHolder getLazyPlayerManagerHolder() { + return lazyPlayerManagerHolder; + } + + public static LazyHolder getClientLazyPlayerManagerHolder() { + return lazyClientPlayerManagerHolder; + } + + public static LazyHolder getLazyRegistryManagerHolder() { + return registryManagerLazyHolder; + } + + public static void init(ChainLoadData chainLoadData) { + FabricPacketEventsAPIManagerFactory.lazyPlayerManagerHolder = chainLoadData.getPlayerManagerAbstractLazyHolder(); + FabricPacketEventsAPIManagerFactory.lazyClientPlayerManagerHolder = chainLoadData.getClientPlayerManagerAbstractLazyHolder(); + FabricPacketEventsAPIManagerFactory.registryManagerLazyHolder = chainLoadData.getRegistryManagerLazyHolder(); + } +} diff --git a/fabric/src/client/java/io/github/retrooper/packetevents/factory/fabric/FabricPacketEventsBuilder.java b/fabric/src/main/java/io/github/retrooper/packetevents/factory/fabric/FabricPacketEventsBuilder.java similarity index 90% rename from fabric/src/client/java/io/github/retrooper/packetevents/factory/fabric/FabricPacketEventsBuilder.java rename to fabric/src/main/java/io/github/retrooper/packetevents/factory/fabric/FabricPacketEventsBuilder.java index e9aab1696c..3ee72c4d3d 100644 --- a/fabric/src/client/java/io/github/retrooper/packetevents/factory/fabric/FabricPacketEventsBuilder.java +++ b/fabric/src/main/java/io/github/retrooper/packetevents/factory/fabric/FabricPacketEventsBuilder.java @@ -20,14 +20,14 @@ import com.github.retrooper.packetevents.PacketEventsAPI; import com.github.retrooper.packetevents.settings.PacketEventsSettings; -import io.github.retrooper.packetevents.PacketEventsClientMod; +import net.fabricmc.api.EnvType; /** * You should, if possible, never construct a packetevents instance on your own * and preferable jar-in-jar the packetevents fabric platform. *

* If you still want to create your own packetevents instance, which is not - * recommended, use {@link PacketEventsClientMod#constructApi(String)} or + * recommended, use {@link io.github.retrooper.packetevents.PacketEventsClientMod#constructApi(String)} or * {@link io.github.retrooper.packetevents.PacketEventsServerMod#constructApi(String)}. * * @deprecated flawed concept and doesn't support both client and server @@ -54,6 +54,6 @@ public static PacketEventsAPI buildNoCache(String modId) { } public static PacketEventsAPI buildNoCache(String modId, PacketEventsSettings inSettings) { - return PacketEventsClientMod.constructApi(modId); + return new FabricPacketEventsAPI(modId, EnvType.CLIENT); } } diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/handler/PacketDecoder.java b/fabric/src/main/java/io/github/retrooper/packetevents/handler/PacketDecoder.java index 4788e6bf45..56211e1c9e 100644 --- a/fabric/src/main/java/io/github/retrooper/packetevents/handler/PacketDecoder.java +++ b/fabric/src/main/java/io/github/retrooper/packetevents/handler/PacketDecoder.java @@ -21,20 +21,22 @@ import com.github.retrooper.packetevents.protocol.PacketSide; import com.github.retrooper.packetevents.protocol.player.User; import com.github.retrooper.packetevents.util.PacketEventsImplHelper; +import io.github.retrooper.packetevents.util.FabricInjectionUtil; import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageDecoder; -import net.minecraft.world.entity.player.Player; +import net.minecraft.entity.player.PlayerEntity; import org.jetbrains.annotations.ApiStatus; import java.util.List; -@ApiStatus.Internal +@ApiStatus.Internal @ChannelHandler.Sharable public class PacketDecoder extends MessageToMessageDecoder { private final PacketSide side; public User user; - public Player player; + public PlayerEntity player; public PacketDecoder(PacketSide side, User user) { this.side = side.getOpposite(); @@ -47,9 +49,23 @@ protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List out) return; } PacketEventsImplHelper.handlePacket(ctx.channel(), this.user, this.player, - msg, false, this.side); + msg, true, this.side); if (msg.isReadable()) { out.add(msg.retain()); } } + + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { + boolean kryptonReorder = false; + switch (evt.toString()) { + case "COMPRESSION_THRESHOLD_UPDATED": + case "COMPRESSION_ENABLED": + kryptonReorder = true; + } + if (evt.getClass().getName().equals("com.viaversion.fabric.common.handler.PipelineReorderEvent") || kryptonReorder) { + FabricInjectionUtil.reorderHandlers(ctx, side.getOpposite()); + } + super.userEventTriggered(ctx, evt); + } } diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/handler/PacketEncoder.java b/fabric/src/main/java/io/github/retrooper/packetevents/handler/PacketEncoder.java index eded37b8ec..473d0adcf8 100644 --- a/fabric/src/main/java/io/github/retrooper/packetevents/handler/PacketEncoder.java +++ b/fabric/src/main/java/io/github/retrooper/packetevents/handler/PacketEncoder.java @@ -18,43 +18,234 @@ package io.github.retrooper.packetevents.handler; +import com.github.retrooper.packetevents.PacketEvents; +import com.github.retrooper.packetevents.event.PacketSendEvent; +import com.github.retrooper.packetevents.event.ProtocolPacketEvent; +import com.github.retrooper.packetevents.exception.CancelPacketException; +import com.github.retrooper.packetevents.exception.InvalidDisconnectPacketSend; +import com.github.retrooper.packetevents.exception.PacketProcessException; +import com.github.retrooper.packetevents.manager.server.ServerVersion; +import com.github.retrooper.packetevents.netty.buffer.ByteBufHelper; +import com.github.retrooper.packetevents.protocol.ConnectionState; import com.github.retrooper.packetevents.protocol.PacketSide; import com.github.retrooper.packetevents.protocol.player.User; +import com.github.retrooper.packetevents.util.EventCreationUtil; +import com.github.retrooper.packetevents.util.ExceptionUtil; import com.github.retrooper.packetevents.util.PacketEventsImplHelper; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerDisconnect; +import io.github.retrooper.packetevents.factory.fabric.FabricPacketEventsAPI; +import io.github.retrooper.packetevents.util.FabricCustomPipelineUtil; import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPromise; -import net.minecraft.world.entity.player.Player; +import io.netty.util.ReferenceCountUtil; +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.network.PacketInflater; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.server.network.ServerPlayerEntity; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; -@ApiStatus.Internal +@ApiStatus.Internal @ChannelHandler.Sharable public class PacketEncoder extends ChannelOutboundHandlerAdapter { private final PacketSide side; public User user; - public Player player; + public PlayerEntity player; + private ChannelPromise promise; + private boolean handledCompression; + private final boolean isPre1_20_5 = PacketEvents.getAPI().getServerManager().getVersion().isOlderThan( + ServerVersion.V_1_20_5); public PacketEncoder(PacketSide side, User user) { this.side = side; this.user = user; } + public void read(ChannelHandlerContext originalCtx, ByteBuf buffer, ChannelPromise promise) { + ChannelHandlerContext ctx = this.tryFixCompressorOrder(originalCtx, buffer); + int firstReaderIndex = buffer.readerIndex(); + PacketSendEvent packetSendEvent = EventCreationUtil.createSendEvent(ctx.channel(), user, player, + buffer, true); + int readerIndex = buffer.readerIndex(); + PacketEvents.getAPI().getEventManager().callEvent(packetSendEvent, () -> buffer.readerIndex(readerIndex)); + if (!packetSendEvent.isCancelled()) { + if (packetSendEvent.getLastUsedWrapper() != null) { + ByteBufHelper.clear(packetSendEvent.getByteBuf()); + packetSendEvent.getLastUsedWrapper().writeVarInt(packetSendEvent.getPacketId()); + packetSendEvent.getLastUsedWrapper().write(); + } else { + buffer.readerIndex(firstReaderIndex); + } + ctx.write(buffer, promise); + } else { + ReferenceCountUtil.release(packetSendEvent.getByteBuf()); + } + if (packetSendEvent.hasPostTasks()) { + for (Runnable task : packetSendEvent.getPostTasks()) { + task.run(); + } + } + } + @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { if (!(msg instanceof ByteBuf in)) { ctx.write(msg, promise); return; } - if (!in.isReadable()) { - in.release(); + + // Handle promise management first (matches Spigot) + ChannelPromise oldPromise = this.promise != null && !this.promise.isSuccess() ? this.promise : null; + promise.addListener(p -> this.promise = oldPromise); + this.promise = promise; + + // Process the packet and execute post-send tasks (matches Spigot) + handlePacket(ctx, in, promise); + + // Check for empty packets last (matches Spigot) + if (!ByteBufHelper.isReadable(in)) { + throw CancelPacketException.INSTANCE; + } else { + if (isPre1_20_5) { + this.read(ctx, in, promise); + } else { + ctx.write(in, promise); + } + } + } + + private @Nullable ProtocolPacketEvent handlePacket(ChannelHandlerContext ctx, ByteBuf buffer, ChannelPromise promise) throws Exception { + // Process the packet using PacketEventsImplHelper (similar to Spigot) + ProtocolPacketEvent protocolPacketEvent = PacketEventsImplHelper.handlePacket( + ctx.channel(), this.user, this.player, buffer, true, this.side + ); + + // Execute post-send tasks (required for cross-platform support) + if (protocolPacketEvent instanceof PacketSendEvent packetSendEvent && packetSendEvent.hasTasksAfterSend()) { + promise.addListener((p) -> { + for (Runnable task : packetSendEvent.getTasksAfterSend()) { + task.run(); + } + }); + } + return protocolPacketEvent; + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + // Handle CancelPacketException (similar to Spigot) + if (ExceptionUtil.isException(cause, CancelPacketException.class)) { + return; + } + + // Handle InvalidDisconnectPacketSend (similar to Spigot) + if (ExceptionUtil.isException(cause, InvalidDisconnectPacketSend.class)) { return; } - PacketEventsImplHelper.handlePacket(ctx.channel(), - this.user, this.player, in, false, this.side); - if (in.isReadable()) { - ctx.write(in, promise); + // Handle PacketProcessException (similar to Spigot) + boolean didWeCauseThis = ExceptionUtil.isException(cause, PacketProcessException.class); + if (didWeCauseThis && (user == null || user.getEncoderState() != ConnectionState.HANDSHAKING)) { + if (!isMinecraftServerInstanceDebugging()) { + if (PacketEvents.getAPI().getSettings().isFullStackTraceEnabled()) { + cause.printStackTrace(); + } else { + PacketEvents.getAPI().getLogManager().warn(cause.getMessage()); + } + } + + if (PacketEvents.getAPI().getSettings().isKickOnPacketExceptionEnabled()) { + try { + if (user != null && player instanceof ServerPlayerEntity) { + // Use cross-platform PacketEvents wrapper for disconnect packet + WrapperPlayServerDisconnect disconnectPacket = new WrapperPlayServerDisconnect( + net.kyori.adventure.text.Component.text("Invalid packet") + ); + user.sendPacket(disconnectPacket); + } + } catch (Exception ignored) { + // Ignore exceptions during disconnect (similar to Spigot) + } + ctx.channel().close(); + + if (player instanceof ServerPlayerEntity serverPlayer) { + // Schedule delayed kick (Fabric-specific, using Minecraft's scheduler) + serverPlayer.getServer().execute(() -> { + FabricPacketEventsAPI.getServerAPI().getPlayerManager().disconnectPlayer(serverPlayer, "Invalid packet"); + }); + } + + if (user != null) { + PacketEvents.getAPI().getLogManager().warn( + "Disconnected " + user.getProfile().getName() + " due to invalid packet!" + ); + } + } + } + + super.exceptionCaught(ctx, cause); + } + + // Placeholder for Minecraft server debugging check (Fabric-specific) + private boolean isMinecraftServerInstanceDebugging() { + // TODO: Implement Fabric-specific debugging check + return false; + } + + // TODO this code is shared with bungee, it should really be in cross-platform and not duplicated + private ChannelHandlerContext tryFixCompressorOrder(ChannelHandlerContext ctx, ByteBuf buffer) { + if (this.handledCompression) { + return ctx; + } + ChannelPipeline pipe = ctx.pipeline(); + List pipeNames = pipe.names(); + if (pipeNames.contains("frame-prepender-compress")) { + // "modern" version, no need to handle this here + this.handledCompression = true; + return ctx; + } + int compressorIndex = pipeNames.indexOf("compress"); + if (compressorIndex == -1) { + return ctx; + } + this.handledCompression = true; + if (compressorIndex <= pipeNames.indexOf(PacketEvents.ENCODER_NAME)) { + return ctx; // order already seems to be correct + } + // relocate handlers + ChannelHandler decoder = pipe.remove(PacketEvents.DECODER_NAME); + ChannelHandler encoder = pipe.remove(PacketEvents.ENCODER_NAME); + pipe.addAfter("decompress", PacketEvents.DECODER_NAME, decoder); + pipe.addAfter("compress", PacketEvents.ENCODER_NAME, encoder); + + // manually decompress packet and update context, + // so we don't need to additionally manually re-compress the packet + this.decompress(pipe, buffer); + return pipe.context(PacketEvents.ENCODER_NAME); + } + + private void decompress(ChannelPipeline pipe, ByteBuf buffer) { + PacketInflater decompressor = (PacketInflater) pipe.get("decompress"); + ChannelHandlerContext decompressorCtx = pipe.context("decompress"); + + ByteBuf decompressed = null; + try { + decompressed = (ByteBuf) FabricCustomPipelineUtil.callPacketDecodeByteBuf( + decompressor, decompressorCtx, buffer).get(0); + if (buffer != decompressed) { + buffer.clear().writeBytes(decompressed); + } + } catch (InvocationTargetException exception) { + throw new RuntimeException(exception); + } finally { + ReferenceCountUtil.release(decompressed); } } -} +} \ No newline at end of file diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/loader/ChainLoadData.java b/fabric/src/main/java/io/github/retrooper/packetevents/loader/ChainLoadData.java new file mode 100644 index 0000000000..5506980815 --- /dev/null +++ b/fabric/src/main/java/io/github/retrooper/packetevents/loader/ChainLoadData.java @@ -0,0 +1,43 @@ +package io.github.retrooper.packetevents.loader; + +import com.github.retrooper.packetevents.manager.registry.RegistryManager; +import io.github.retrooper.packetevents.manager.AbstractFabricPlayerManager; +import io.github.retrooper.packetevents.util.LazyHolder; +import io.github.retrooper.packetevents.impl.netty.manager.player.PlayerManagerAbstract; + +public class ChainLoadData { + + private LazyHolder registryManagerLazyHolder = null; + private LazyHolder playerManagerAbstractLazyHolder = null; + private LazyHolder clientPlayerManagerAbstractLazyHolder = null; + + public void setRegistryManagerIfNull(LazyHolder registryManagerLazyHolder) { + if (this.registryManagerLazyHolder == null) { + this.registryManagerLazyHolder = registryManagerLazyHolder; + } + } + + public LazyHolder getRegistryManagerLazyHolder() { + return this.registryManagerLazyHolder; + } + + public void setPlayerManagerIfNull(LazyHolder playerManagerAbstractLazyHolder) { + if (this.playerManagerAbstractLazyHolder == null) { + this.playerManagerAbstractLazyHolder = playerManagerAbstractLazyHolder; + } + } + + public LazyHolder getPlayerManagerAbstractLazyHolder() { + return this.playerManagerAbstractLazyHolder; + } + + public void setClientPlayerManagerIfNull(LazyHolder clientPlayerManagerAbstractLazyHolder) { + if (this.clientPlayerManagerAbstractLazyHolder == null) { + this.clientPlayerManagerAbstractLazyHolder = clientPlayerManagerAbstractLazyHolder; + } + } + + public LazyHolder getClientPlayerManagerAbstractLazyHolder() { + return this.clientPlayerManagerAbstractLazyHolder; + } +} diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/loader/ChainLoadEntryPoint.java b/fabric/src/main/java/io/github/retrooper/packetevents/loader/ChainLoadEntryPoint.java new file mode 100644 index 0000000000..7b89bd3d90 --- /dev/null +++ b/fabric/src/main/java/io/github/retrooper/packetevents/loader/ChainLoadEntryPoint.java @@ -0,0 +1,8 @@ +package io.github.retrooper.packetevents.loader; + +import com.github.retrooper.packetevents.manager.server.ServerVersion; + +public interface ChainLoadEntryPoint { + void initialize(ChainLoadData chainLoadData); + ServerVersion getNativeVersion(); +} diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/manager/AbstractFabricPlayerManager.java b/fabric/src/main/java/io/github/retrooper/packetevents/manager/AbstractFabricPlayerManager.java new file mode 100644 index 0000000000..539c32c4e8 --- /dev/null +++ b/fabric/src/main/java/io/github/retrooper/packetevents/manager/AbstractFabricPlayerManager.java @@ -0,0 +1,84 @@ +package io.github.retrooper.packetevents.manager; + +import com.github.retrooper.packetevents.PacketEventsAPI; +import com.github.retrooper.packetevents.protocol.ConnectionState; +import com.github.retrooper.packetevents.wrapper.PacketWrapper; +import io.github.retrooper.packetevents.impl.netty.manager.player.PlayerManagerAbstract; +import net.minecraft.network.chat.Component; +import net.minecraft.server.network.ServerPlayerEntity; +import org.jetbrains.annotations.NotNull; + +public abstract class AbstractFabricPlayerManager extends PlayerManagerAbstract { + + private final PacketEventsAPI packetEventsAPI; + + public AbstractFabricPlayerManager(PacketEventsAPI packetEventsAPI) { + this.packetEventsAPI = packetEventsAPI; + } + + @Override + public ConnectionState getConnectionState(@NotNull Object player) throws IllegalStateException { + return getUser(player).getConnectionState(); + } + + @Override + public void sendPacket(@NotNull Object player, @NotNull Object byteBuf) { + packetEventsAPI.getProtocolManager().sendPacket(getChannel(player), byteBuf); + } + @Override + public void sendPacket(@NotNull Object player, @NotNull PacketWrapper wrapper) { + packetEventsAPI.getProtocolManager().sendPacket(getChannel(player), wrapper); + } + + @Override + public void sendPacketSilently(@NotNull Object player, @NotNull Object byteBuf) { + packetEventsAPI.getProtocolManager().sendPacketSilently(getChannel(player), byteBuf); + } + + @Override + public void sendPacketSilently(@NotNull Object player, @NotNull PacketWrapper wrapper) { + packetEventsAPI.getProtocolManager().sendPacketSilently(getChannel(player), wrapper); + } + + @Override + public void writePacket(@NotNull Object player, @NotNull Object byteBuf) { + packetEventsAPI.getProtocolManager().writePacket(getChannel(player), byteBuf); + } + + @Override + public void writePacket(@NotNull Object player, @NotNull PacketWrapper wrapper) { + packetEventsAPI.getProtocolManager().writePacket(getChannel(player), wrapper); + } + + @Override + public void writePacketSilently(@NotNull Object player, @NotNull Object byteBuf) { + packetEventsAPI.getProtocolManager().writePacketSilently(getChannel(player), byteBuf); + } + + @Override + public void writePacketSilently(@NotNull Object player, @NotNull PacketWrapper wrapper) { + packetEventsAPI.getProtocolManager().writePacketSilently(getChannel(player), wrapper); + } + + @Override + public void receivePacket(Object player, Object byteBuf) { + packetEventsAPI.getProtocolManager().receivePacket(getChannel(player), byteBuf); + } + + @Override + public void receivePacket(Object player, PacketWrapper wrapper) { + packetEventsAPI.getProtocolManager().receivePacket(getChannel(player), wrapper); + } + + @Override + public void receivePacketSilently(Object player, Object byteBuf) { + packetEventsAPI.getProtocolManager().receivePacketSilently(getChannel(player), byteBuf); + } + + @Override + public void receivePacketSilently(Object player, PacketWrapper wrapper) { + packetEventsAPI.getProtocolManager().receivePacketSilently(getChannel(player), wrapper); + } + + public abstract void disconnectPlayer(ServerPlayerEntity serverPlayerEntity, String message); +} diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/manager/FabricLoggerManager.java b/fabric/src/main/java/io/github/retrooper/packetevents/manager/FabricLoggerManager.java new file mode 100644 index 0000000000..825003c388 --- /dev/null +++ b/fabric/src/main/java/io/github/retrooper/packetevents/manager/FabricLoggerManager.java @@ -0,0 +1,172 @@ +/* + * This file is part of packetevents - https://github.com/retrooper/packetevents + * Copyright (C) 2024 retrooper and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package io.github.retrooper.packetevents.manager; + +import com.github.retrooper.packetevents.util.LogManager; +import net.kyori.adventure.text.format.NamedTextColor; +import java.util.logging.Logger; + +import java.lang.reflect.Method; +import java.util.logging.Level; +import java.util.regex.Pattern; + +public class FabricLoggerManager extends LogManager { + private static final Pattern STRIP_COLOR_PATTERN = Pattern.compile("§[0-9A-FK-ORa-fk-or]"); // Example pattern to strip Minecraft color codes + + private final Object logger; + private final LoggerType loggerType; + + // Enum to represent the type of logger being used + private enum LoggerType { + SLF4J, LOG4J, JDK + } + + private FabricLoggerManager(Object logger, LoggerType loggerType) { + this.logger = logger; + this.loggerType = loggerType; + } + + @Override + public void log(Level level, NamedTextColor color, String message) { + // Strip color codes from the message (if any) + String plainMessage = STRIP_COLOR_PATTERN.matcher(message).replaceAll(""); + + switch (loggerType) { + case SLF4J: + logWithSlf4j(level, plainMessage); + break; + case LOG4J: + logWithLog4j(level, plainMessage); + break; + case JDK: + logWithJdk(level, plainMessage); + break; + } + } + + private void logWithSlf4j(Level level, String message) { + try { + Object slf4jLogger = logger; + Class slf4jLoggerClass = Class.forName("org.slf4j.Logger"); + Method logMethod; + switch (level.getName()) { + case "SEVERE": + logMethod = slf4jLoggerClass.getMethod("error", String.class); + break; + case "WARNING": + logMethod = slf4jLoggerClass.getMethod("warn", String.class); + break; + case "INFO": + logMethod = slf4jLoggerClass.getMethod("info", String.class); + break; + case "CONFIG": + case "FINE": + logMethod = slf4jLoggerClass.getMethod("debug", String.class); + break; + case "FINER": + case "FINEST": + logMethod = slf4jLoggerClass.getMethod("trace", String.class); + break; + default: + logMethod = slf4jLoggerClass.getMethod("info", String.class); // Default to info + break; + } + logMethod.invoke(slf4jLogger, message); + } catch (Exception e) { + // Fallback to println if reflection fails (shouldn't happen) + System.out.println("[" + level + "] " + message); + } + } + + private void logWithLog4j(Level level, String message) { + try { + Object log4jLogger = logger; + Class log4jLoggerClass = Class.forName("org.apache.logging.log4j.Logger"); + Class log4jLevelClass = Class.forName("org.apache.logging.log4j.Level"); + Method logMethod = log4jLoggerClass.getMethod("log", log4jLevelClass, String.class); + + // Map java.util.logging.Level to Log4j Level + Object log4jLevel; + switch (level.getName()) { + case "SEVERE": + log4jLevel = log4jLevelClass.getField("ERROR").get(null); + break; + case "WARNING": + log4jLevel = log4jLevelClass.getField("WARN").get(null); + break; + case "INFO": + log4jLevel = log4jLevelClass.getField("INFO").get(null); + break; + case "CONFIG": + case "FINE": + log4jLevel = log4jLevelClass.getField("DEBUG").get(null); + break; + case "FINER": + case "FINEST": + log4jLevel = log4jLevelClass.getField("TRACE").get(null); + break; + default: + log4jLevel = log4jLevelClass.getField("INFO").get(null); // Default to INFO + break; + } + + logMethod.invoke(log4jLogger, log4jLevel, message); + } catch (Exception e) { + // Fallback to println if reflection fails (shouldn't happen) + System.out.println("[" + level + "] " + message); + } + } + + private void logWithJdk(Level level, String message) { + Logger jdkLogger = (Logger) logger; + jdkLogger.log(level, message); + } + + // Factory method to create a mod-specific logger with fallback + public static FabricLoggerManager createModLogger(String modId) { + // Try SLF4J first + try { + Class slf4jLoggerClass = Class.forName("org.slf4j.Logger"); + Class slf4jLoggerFactoryClass = Class.forName("org.slf4j.LoggerFactory"); + Method getLoggerMethod = slf4jLoggerFactoryClass.getMethod("getLogger", String.class); + Object slf4jLogger = getLoggerMethod.invoke(null, modId); + return new FabricLoggerManager(slf4jLogger, LoggerType.SLF4J); + } catch (ClassNotFoundException e) { + // SLF4J not found, try Log4j next + } catch (Exception e) { + // Reflection failed, try Log4j next + } + + // Try Log4j + try { + Class log4jLogManagerClass = Class.forName("org.apache.logging.log4j.LogManager"); + Method getLoggerMethod = log4jLogManagerClass.getMethod("getLogger", String.class); + Object log4jLogger = getLoggerMethod.invoke(null, modId); + return new FabricLoggerManager(log4jLogger, LoggerType.LOG4J); + } catch (ClassNotFoundException e) { + // Log4j not found, fall back to JDK Logger + } catch (Exception e) { + // Reflection failed, fall back to JDK Logger + } + + // Fall back to JDK Logger + Logger jdkLogger = Logger.getLogger(modId); + return new FabricLoggerManager(jdkLogger, LoggerType.JDK); + } +} diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/factory/fabric/FabricProtocolManager.java b/fabric/src/main/java/io/github/retrooper/packetevents/manager/FabricProtocolManager.java similarity index 63% rename from fabric/src/main/java/io/github/retrooper/packetevents/factory/fabric/FabricProtocolManager.java rename to fabric/src/main/java/io/github/retrooper/packetevents/manager/FabricProtocolManager.java index 299d415bc0..76417eee34 100644 --- a/fabric/src/main/java/io/github/retrooper/packetevents/factory/fabric/FabricProtocolManager.java +++ b/fabric/src/main/java/io/github/retrooper/packetevents/manager/FabricProtocolManager.java @@ -16,19 +16,30 @@ * along with this program. If not, see . */ -package io.github.retrooper.packetevents.factory.fabric; +package io.github.retrooper.packetevents.manager; +import com.github.retrooper.packetevents.PacketEventsAPI; import com.github.retrooper.packetevents.netty.channel.ChannelHelper; import com.github.retrooper.packetevents.protocol.ProtocolVersion; +import com.github.retrooper.packetevents.protocol.player.User; import io.github.retrooper.packetevents.impl.netty.manager.protocol.ProtocolManagerAbstract; import io.netty.buffer.ByteBuf; import net.fabricmc.api.EnvType; +import java.util.Collection; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + public class FabricProtocolManager extends ProtocolManagerAbstract { + private final Map channels = new ConcurrentHashMap<>(); + private final Map users = new ConcurrentHashMap<>(); private final boolean invert; + private final PacketEventsAPI packetEventsAPI; - public FabricProtocolManager(EnvType environment) { + public FabricProtocolManager(PacketEventsAPI packetEventsAPI, EnvType environment) { + this.packetEventsAPI = packetEventsAPI; this.invert = environment == EnvType.CLIENT; } @@ -88,7 +99,7 @@ public void writePacketSilently(Object channel, Object byteBuf) { @Override public void receivePacket(Object channel, Object byteBuf) { if (this.invert) { - // no way to specify wether to flush or not, so just don't + // no way to specify whether to flush or not, so just don't super.writePacket(channel, byteBuf); } else { this.receivePacket0(channel, byteBuf); @@ -98,10 +109,56 @@ public void receivePacket(Object channel, Object byteBuf) { @Override public void receivePacketSilently(Object channel, Object byteBuf) { if (this.invert) { - // no way to specify wether to flush or not, so just don't + // no way to specify whether to flush or not, so just don't super.writePacketSilently(channel, byteBuf); } else { super.receivePacketSilently(channel, byteBuf); } } + + @Override + public Collection getUsers() { + return users.values(); + } + + @Override + public Collection getChannels() { + return channels.values(); + } + + @Override + public User getUser(Object channel) { + Object pipeline = ChannelHelper.getPipeline(channel); + return users.get(pipeline); + } + + @Override + public User removeUser(Object channel) { + Object pipeline = ChannelHelper.getPipeline(channel); + return users.remove(pipeline); + } + + @Override + public void setUser(Object channel, User user) { + synchronized (channel) { + Object pipeline = ChannelHelper.getPipeline(channel); + users.put(pipeline, user); + } + packetEventsAPI.getInjector().updateUser(channel, user); + } + + @Override + public Object getChannel(UUID uuid) { + return channels.get(uuid); + } + + @Override + public void setChannel(UUID uuid, Object channel) { + channels.put(uuid, channel); + } + + @Override + public boolean hasChannel(Object channel) { + return channels.containsValue(channel); + } } diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/factory/fabric/FabricServerManager.java b/fabric/src/main/java/io/github/retrooper/packetevents/manager/FabricServerManager.java similarity index 71% rename from fabric/src/main/java/io/github/retrooper/packetevents/factory/fabric/FabricServerManager.java rename to fabric/src/main/java/io/github/retrooper/packetevents/manager/FabricServerManager.java index 580c93931a..0bc44e887c 100644 --- a/fabric/src/main/java/io/github/retrooper/packetevents/factory/fabric/FabricServerManager.java +++ b/fabric/src/main/java/io/github/retrooper/packetevents/manager/FabricServerManager.java @@ -16,21 +16,24 @@ * along with this program. If not, see . */ -package io.github.retrooper.packetevents.factory.fabric; +package io.github.retrooper.packetevents.manager; import com.github.retrooper.packetevents.manager.server.ServerVersion; import com.github.retrooper.packetevents.protocol.player.ClientVersion; import com.github.retrooper.packetevents.protocol.player.User; import com.github.retrooper.packetevents.util.mappings.GlobalRegistryHolder; import io.github.retrooper.packetevents.impl.netty.manager.server.ServerManagerAbstract; -import net.minecraft.SharedConstants; +import net.fabricmc.loader.api.FabricLoader; public class FabricServerManager extends ServerManagerAbstract { - private ServerVersion version; + // Its ok for this to be static, client and server version running PE will always be the same even on integrated server + private static ServerVersion version; - private ServerVersion resolveVersion() { - String mcVersion = SharedConstants.getCurrentVersion().getId(); + private static ServerVersion resolveVersion() { + String mcVersion = FabricLoader.getInstance().getModContainer("minecraft") + .flatMap(mod -> mod.getMetadata().getVersion().getFriendlyString().describeConstable()) + .orElse("unknown"); for (ServerVersion version : ServerVersion.reversedValues()) { if (mcVersion.contains(version.getReleaseName())) { return version; @@ -42,10 +45,14 @@ private ServerVersion resolveVersion() { @Override public ServerVersion getVersion() { - if (this.version == null) { - this.version = this.resolveVersion(); + return getVersionStatically(); + } + + public static ServerVersion getVersionStatically() { + if (version == null) { + version = resolveVersion(); } - return this.version; + return version; } @Override diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/manager/InternalFabricPacketListener.java b/fabric/src/main/java/io/github/retrooper/packetevents/manager/InternalFabricPacketListener.java new file mode 100644 index 0000000000..6cd4686688 --- /dev/null +++ b/fabric/src/main/java/io/github/retrooper/packetevents/manager/InternalFabricPacketListener.java @@ -0,0 +1,49 @@ +package io.github.retrooper.packetevents.manager; + +import com.github.retrooper.packetevents.PacketEvents; +import com.github.retrooper.packetevents.event.PacketReceiveEvent; +import com.github.retrooper.packetevents.event.PacketSendEvent; +import com.github.retrooper.packetevents.protocol.ConnectionState; +import com.github.retrooper.packetevents.protocol.item.ItemStack; +import com.github.retrooper.packetevents.protocol.packettype.PacketType; +import com.github.retrooper.packetevents.protocol.player.ClientVersion; +import com.github.retrooper.packetevents.protocol.player.User; +import com.github.retrooper.packetevents.util.LogManager; +import com.github.retrooper.packetevents.wrapper.handshaking.client.WrapperHandshakingClientHandshake; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSetCursorItem; +import io.github.retrooper.packetevents.util.viaversion.ViaVersionUtil; + +import java.nio.channels.Channel; + +public class InternalFabricPacketListener extends com.github.retrooper.packetevents.manager.InternalPacketListener { + + @Override + public void onPacketReceive(PacketReceiveEvent event) { + if (event.getPacketType() == PacketType.Handshaking.Client.HANDSHAKE) { + User user = event.getUser(); + WrapperHandshakingClientHandshake packet = new WrapperHandshakingClientHandshake(event); + ClientVersion clientVersion = packet.getClientVersion(); + ConnectionState state = packet.getNextConnectionState(); + + String feature; + if (ViaVersionUtil.isAvailable(user)) { + clientVersion = ClientVersion.getById(ViaVersionUtil.getProtocolVersion(user)); + feature = "ViaVersion"; + } else { + feature = null; + } + + LogManager logger = PacketEvents.getAPI().getLogManager(); + if (logger.isDebug()) { + logger.debug("Processed handshake for " + event.getAddress() + ": " + + state.name() + " / " + packet.getClientVersion().getReleaseName() + + (feature != null ? " (using " + feature + ")" : "")); + } + + user.setClientVersion(clientVersion); + user.setConnectionState(state); + } else { + super.onPacketReceive(event); + } + } +} \ No newline at end of file diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/manager/PacketEventsMixinManager.java b/fabric/src/main/java/io/github/retrooper/packetevents/manager/PacketEventsMixinManager.java new file mode 100644 index 0000000000..428abb5f03 --- /dev/null +++ b/fabric/src/main/java/io/github/retrooper/packetevents/manager/PacketEventsMixinManager.java @@ -0,0 +1,20 @@ +package io.github.retrooper.packetevents.manager; + +import me.fallenbreath.conditionalmixin.api.mixin.RestrictiveMixinConfigPlugin; + +import java.util.List; +import java.util.Set; + +public class PacketEventsMixinManager extends RestrictiveMixinConfigPlugin { + @Override public String getRefMapperConfig() { + return ""; + } + + @Override public void acceptTargets(Set myTargets, Set otherTargets) { + + } + + @Override public List getMixins() { + return List.of(); + } +} diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/manager/logger/AbstractFabricLogger.java b/fabric/src/main/java/io/github/retrooper/packetevents/manager/logger/AbstractFabricLogger.java new file mode 100644 index 0000000000..0983865dea --- /dev/null +++ b/fabric/src/main/java/io/github/retrooper/packetevents/manager/logger/AbstractFabricLogger.java @@ -0,0 +1,31 @@ +package io.github.retrooper.packetevents.manager.logger; + +import com.github.retrooper.packetevents.util.LogManager; + +import java.util.logging.Logger; +import java.util.regex.Pattern; + +public abstract class AbstractFabricLogger extends LogManager { + protected static final Pattern STRIP_COLOR_PATTERN = Pattern.compile("§[0-9A-FK-ORa-fk-or]"); + + protected String stripColorCodes(String message) { + return STRIP_COLOR_PATTERN.matcher(message).replaceAll(""); + } + + public static AbstractFabricLogger createModLogger(String modId) { + // Try SLF4J + try { + org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(modId); + return new Slf4jFabricLogger(logger); + } catch (Throwable ignored) {} + + // Try Log4j + try { + org.apache.logging.log4j.Logger logger = org.apache.logging.log4j.LogManager.getLogger(modId); + return new Log4jFabricLogger(logger); + } catch (Throwable ignored) {} + + // Fallback to JDK logger + return new JdkFabricLogger(Logger.getLogger(modId)); + } +} \ No newline at end of file diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/manager/logger/JdkFabricLogger.java b/fabric/src/main/java/io/github/retrooper/packetevents/manager/logger/JdkFabricLogger.java new file mode 100644 index 0000000000..d62c858fb5 --- /dev/null +++ b/fabric/src/main/java/io/github/retrooper/packetevents/manager/logger/JdkFabricLogger.java @@ -0,0 +1,20 @@ +package io.github.retrooper.packetevents.manager.logger; + +import net.kyori.adventure.text.format.NamedTextColor; + +import java.util.logging.Level; +import java.util.logging.Logger; + +public class JdkFabricLogger extends AbstractFabricLogger { + private final Logger logger; + + public JdkFabricLogger(Logger logger) { + this.logger = logger; + } + + @Override + public void log(Level level, NamedTextColor color, String message) { + String msg = stripColorCodes(message); + logger.log(level, msg); + } +} \ No newline at end of file diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/manager/logger/Log4jFabricLogger.java b/fabric/src/main/java/io/github/retrooper/packetevents/manager/logger/Log4jFabricLogger.java new file mode 100644 index 0000000000..2eee897958 --- /dev/null +++ b/fabric/src/main/java/io/github/retrooper/packetevents/manager/logger/Log4jFabricLogger.java @@ -0,0 +1,44 @@ +package io.github.retrooper.packetevents.manager.logger; + +import net.kyori.adventure.text.format.NamedTextColor; + +import java.util.logging.Level; + +public class Log4jFabricLogger extends AbstractFabricLogger { + private final org.apache.logging.log4j.Logger logger; + + public Log4jFabricLogger(org.apache.logging.log4j.Logger logger) { + this.logger = logger; + } + + @Override + public void log(Level level, NamedTextColor color, String message) { + String msg = stripColorCodes(message); + org.apache.logging.log4j.Level log4jLevel; + + switch (level.getName()) { + case "SEVERE": + log4jLevel = org.apache.logging.log4j.Level.ERROR; + break; + case "WARNING": + log4jLevel = org.apache.logging.log4j.Level.WARN; + break; + case "INFO": + log4jLevel = org.apache.logging.log4j.Level.INFO; + break; + case "CONFIG": + case "FINE": + log4jLevel = org.apache.logging.log4j.Level.DEBUG; + break; + case "FINER": + case "FINEST": + log4jLevel = org.apache.logging.log4j.Level.TRACE; + break; + default: + log4jLevel = org.apache.logging.log4j.Level.INFO; + break; + } + + logger.log(log4jLevel, msg); + } +} \ No newline at end of file diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/manager/logger/Slf4jFabricLogger.java b/fabric/src/main/java/io/github/retrooper/packetevents/manager/logger/Slf4jFabricLogger.java new file mode 100644 index 0000000000..f2382a463d --- /dev/null +++ b/fabric/src/main/java/io/github/retrooper/packetevents/manager/logger/Slf4jFabricLogger.java @@ -0,0 +1,40 @@ +package io.github.retrooper.packetevents.manager.logger; + +import net.kyori.adventure.text.format.NamedTextColor; + +import java.util.logging.Level; + +public class Slf4jFabricLogger extends AbstractFabricLogger { + private final org.slf4j.Logger logger; + + public Slf4jFabricLogger(org.slf4j.Logger logger) { + this.logger = logger; + } + + @Override + public void log(Level level, NamedTextColor color, String message) { + String msg = stripColorCodes(message); + switch (level.getName()) { + case "SEVERE": + logger.error(msg); + break; + case "WARNING": + logger.warn(msg); + break; + case "INFO": + logger.info(msg); + break; + case "CONFIG": + case "FINE": + logger.debug(msg); + break; + case "FINER": + case "FINEST": + logger.trace(msg); + break; + default: + logger.info(msg); + break; + } + } +} \ No newline at end of file diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/manager/logger/jul/JULoggerFactory.java b/fabric/src/main/java/io/github/retrooper/packetevents/manager/logger/jul/JULoggerFactory.java new file mode 100644 index 0000000000..0e9a8a6b79 --- /dev/null +++ b/fabric/src/main/java/io/github/retrooper/packetevents/manager/logger/jul/JULoggerFactory.java @@ -0,0 +1,18 @@ +package io.github.retrooper.packetevents.manager.logger.jul; + +import java.util.logging.Logger; + +public class JULoggerFactory { + public static Logger createLogger(String name) { + try { + return new Slf4jBackedJULogger(name); + } catch (NoClassDefFoundError | Exception ignored) { + } + try { + return new Log4jBackedJULogger(name); + } catch (NoClassDefFoundError | Exception ignored) { + } + + return Logger.getLogger(name); + } +} diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/manager/logger/jul/Log4jBackedJULogger.java b/fabric/src/main/java/io/github/retrooper/packetevents/manager/logger/jul/Log4jBackedJULogger.java new file mode 100644 index 0000000000..d84ddf47a4 --- /dev/null +++ b/fabric/src/main/java/io/github/retrooper/packetevents/manager/logger/jul/Log4jBackedJULogger.java @@ -0,0 +1,47 @@ +package io.github.retrooper.packetevents.manager.logger.jul; + +import java.util.logging.Level; +import java.util.logging.Logger; + +public class Log4jBackedJULogger extends Logger { + private final org.apache.logging.log4j.Logger log4jLogger; + + protected Log4jBackedJULogger(String name) { + super(name, null); + this.log4jLogger = org.apache.logging.log4j.LogManager.getLogger(name); + } + + @Override + public void log(Level level, String msg) { + if (level == Level.SEVERE) { + log4jLogger.error(msg); + } else if (level == Level.WARNING) { + log4jLogger.warn(msg); + } else if (level == Level.INFO) { + log4jLogger.info(msg); + } else if (level == Level.CONFIG || level == Level.FINE) { + log4jLogger.debug(msg); + } else if (level == Level.FINER || level == Level.FINEST) { + log4jLogger.trace(msg); + } else { + log4jLogger.info(msg); + } + } + + @Override + public void log(Level level, String msg, Throwable thrown) { + if (level == Level.SEVERE) { + log4jLogger.error(msg, thrown); + } else if (level == Level.WARNING) { + log4jLogger.warn(msg, thrown); + } else if (level == Level.INFO) { + log4jLogger.info(msg, thrown); + } else if (level == Level.CONFIG || level == Level.FINE) { + log4jLogger.debug(msg, thrown); + } else if (level == Level.FINER || level == Level.FINEST) { + log4jLogger.trace(msg, thrown); + } else { + log4jLogger.info(msg, thrown); + } + } +} diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/manager/logger/jul/Slf4jBackedJULogger.java b/fabric/src/main/java/io/github/retrooper/packetevents/manager/logger/jul/Slf4jBackedJULogger.java new file mode 100644 index 0000000000..7f29f46f66 --- /dev/null +++ b/fabric/src/main/java/io/github/retrooper/packetevents/manager/logger/jul/Slf4jBackedJULogger.java @@ -0,0 +1,52 @@ +package io.github.retrooper.packetevents.manager.logger.jul; + +import org.slf4j.LoggerFactory; +import org.slf4j.Marker; +import org.slf4j.MarkerFactory; + +import java.util.logging.Level; +import java.util.logging.Logger; + +public class Slf4jBackedJULogger extends Logger { + private final org.slf4j.Logger slf4jLogger; + private static final Marker MARKER = MarkerFactory.getMarker("JUL"); + + public Slf4jBackedJULogger(String name) { + super(name, null); + this.slf4jLogger = LoggerFactory.getLogger(name); + } + + @Override + public void log(Level level, String msg) { + if (level == Level.SEVERE) { + slf4jLogger.error(MARKER, msg); + } else if (level == Level.WARNING) { + slf4jLogger.warn(MARKER, msg); + } else if (level == Level.INFO) { + slf4jLogger.info(MARKER, msg); + } else if (level == Level.CONFIG || level == Level.FINE) { + slf4jLogger.debug(MARKER, msg); + } else if (level == Level.FINER || level == Level.FINEST) { + slf4jLogger.trace(MARKER, msg); + } else { + slf4jLogger.info(MARKER, msg); + } + } + + @Override + public void log(Level level, String msg, Throwable thrown) { + if (level.equals(Level.SEVERE)) { + slf4jLogger.error(MARKER, msg, thrown); + } else if (level.equals(Level.WARNING)) { + slf4jLogger.warn(MARKER, msg, thrown); + } else if (level.equals(Level.INFO)) { + slf4jLogger.info(MARKER, msg, thrown); + } else if (level.equals(Level.CONFIG) || level.equals(Level.FINE)) { + slf4jLogger.debug(MARKER, msg, thrown); + } else if (level.equals(Level.FINER) || level.equals(Level.FINEST)) { + slf4jLogger.trace(MARKER, msg, thrown); + } else { + slf4jLogger.info(MARKER, msg, thrown); + } + } +} diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/manager/registry/FabricRegistryManager.java b/fabric/src/main/java/io/github/retrooper/packetevents/manager/registry/FabricRegistryManager.java new file mode 100644 index 0000000000..0e6f1b2c3e --- /dev/null +++ b/fabric/src/main/java/io/github/retrooper/packetevents/manager/registry/FabricRegistryManager.java @@ -0,0 +1,20 @@ +package io.github.retrooper.packetevents.manager.registry; + +import com.github.retrooper.packetevents.manager.registry.ItemRegistry; +import com.github.retrooper.packetevents.manager.registry.RegistryManager; + +public class FabricRegistryManager implements RegistryManager { + + private final ItemRegistry fabricItemRegistry; + + public FabricRegistryManager( + ItemRegistry fabricItemRegistry + ) { + this.fabricItemRegistry = fabricItemRegistry; + } + + @Override + public ItemRegistry getItemRegistry() { + return fabricItemRegistry; + } +} diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/mixin/ConnectionMixin.java b/fabric/src/main/java/io/github/retrooper/packetevents/mixin/ConnectionMixin.java deleted file mode 100644 index baedaf74c2..0000000000 --- a/fabric/src/main/java/io/github/retrooper/packetevents/mixin/ConnectionMixin.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * This file is part of packetevents - https://github.com/retrooper/packetevents - * Copyright (C) 2024 retrooper and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package io.github.retrooper.packetevents.mixin; - -import com.github.retrooper.packetevents.PacketEvents; -import com.github.retrooper.packetevents.event.UserConnectEvent; -import com.github.retrooper.packetevents.manager.protocol.ProtocolManager; -import com.github.retrooper.packetevents.protocol.ConnectionState; -import com.github.retrooper.packetevents.protocol.PacketSide; -import com.github.retrooper.packetevents.protocol.player.ClientVersion; -import com.github.retrooper.packetevents.protocol.player.User; -import com.github.retrooper.packetevents.protocol.player.UserProfile; -import com.github.retrooper.packetevents.util.PacketEventsImplHelper; -import io.github.retrooper.packetevents.handler.PacketDecoder; -import io.github.retrooper.packetevents.handler.PacketEncoder; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelPipeline; -import net.minecraft.SharedConstants; -import net.minecraft.network.BandwidthDebugMonitor; -import net.minecraft.network.protocol.PacketFlow; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(net.minecraft.network.Connection.class) -public class ConnectionMixin { - - @Shadow public Channel channel; - // doesn't account for mods like ViaFabric - @Unique - private static final ClientVersion CLIENT_VERSION = - ClientVersion.getById(SharedConstants.getProtocolVersion()); - - @Inject( - method = "configureSerialization", - at = @At("TAIL") - ) - private static void configureSerialization( - ChannelPipeline pipeline, PacketFlow flow, boolean memoryOnly, - BandwidthDebugMonitor bandwithDebugMonitor, CallbackInfo ci - ) { - PacketSide pipelineSide = switch (flow) { - case CLIENTBOUND -> PacketSide.CLIENT; - case SERVERBOUND -> PacketSide.SERVER; - }; - PacketSide apiSide = PacketEvents.getAPI().getInjector().getPacketSide(); - if (pipelineSide != apiSide) { - // if pipeline side doesn't match api side, don't inject into - // this pipeline - it probably means this is the pipeline from - // integrated server to minecraft client, which is currently unsupported - PacketEvents.getAPI().getLogManager().debug("Skipped pipeline injection on " + pipelineSide); - return; - } - - PacketEvents.getAPI().getLogManager().debug("Game connected!"); - - Channel channel = pipeline.channel(); - User user = new User(channel, ConnectionState.HANDSHAKING, - CLIENT_VERSION, new UserProfile(null, null)); - PacketEvents.getAPI().getProtocolManager().setUser(channel.pipeline(), user); - - UserConnectEvent connectEvent = new UserConnectEvent(user); - PacketEvents.getAPI().getEventManager().callEvent(connectEvent); - if (connectEvent.isCancelled()) { - channel.unsafe().closeForcibly(); - return; - } - - channel.pipeline().addAfter("splitter", PacketEvents.DECODER_NAME, new PacketDecoder(apiSide, user)); - channel.pipeline().addAfter("prepender", PacketEvents.ENCODER_NAME, new PacketEncoder(apiSide, user)); - channel.closeFuture().addListener((ChannelFutureListener) future -> - PacketEventsImplHelper.handleDisconnection(user.getChannel(), user.getUUID())); - } -} diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/mixin/PlayerListMixin.java b/fabric/src/main/java/io/github/retrooper/packetevents/mixin/PlayerListMixin.java deleted file mode 100644 index cc9b3f0974..0000000000 --- a/fabric/src/main/java/io/github/retrooper/packetevents/mixin/PlayerListMixin.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * This file is part of packetevents - https://github.com/retrooper/packetevents - * Copyright (C) 2024 retrooper and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package io.github.retrooper.packetevents.mixin; - -import com.github.retrooper.packetevents.PacketEvents; -import com.github.retrooper.packetevents.PacketEventsAPI; -import com.github.retrooper.packetevents.event.UserLoginEvent; -import com.github.retrooper.packetevents.protocol.player.User; -import com.github.retrooper.packetevents.util.FakeChannelUtil; -import io.netty.channel.Channel; -import net.minecraft.network.Connection; -import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.network.CommonListenerCookie; -import net.minecraft.server.players.PlayerList; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -@Mixin(PlayerList.class) -public class PlayerListMixin { - - /** - * @reason Associate connection instance with player instance - */ - @Inject( - method = "placeNewPlayer", - at = @At("HEAD") - ) - private void preNewPlayerPlace( - Connection connection, ServerPlayer player, - CommonListenerCookie cookie, CallbackInfo ci - ) { - PacketEvents.getAPI().getInjector().setPlayer(connection.channel, player); - } - - /** - * @reason Associate connection instance with player instance and handle login event - */ - @Inject( - method = "placeNewPlayer", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/server/players/PlayerList;broadcastAll(Lnet/minecraft/network/protocol/Packet;)V", - shift = At.Shift.AFTER - ) - ) - private void onPlayerLogin( - Connection connection, ServerPlayer player, - CommonListenerCookie cookie, CallbackInfo ci - ) { - PacketEventsAPI api = PacketEvents.getAPI(); - - User user = api.getPlayerManager().getUser(player); - if (user == null) { - Object channelObj = api.getPlayerManager().getChannel(player); - - // Check if it's a fake connection - if (!FakeChannelUtil.isFakeChannel(channelObj) && - (!api.isTerminated() || api.getSettings().isKickIfTerminated())) { - // Kick the player if they're not a fake player - player.connection.disconnect(Component.literal("PacketEvents 2.0 failed to inject")); - } - return; - } - - api.getEventManager().callEvent(new UserLoginEvent(user, player)); - } - - /** - * @reason Minecraft creates a new player instance on respawn - */ - @Inject( - method = "respawn", - at = @At("RETURN") - ) - private void postRespawn(CallbackInfoReturnable cir) { - ServerPlayer player = cir.getReturnValue(); - Channel channel = player.connection.connection.channel; - PacketEvents.getAPI().getInjector().setPlayer(channel, player); - } -} diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/util/FabricCustomPipelineUtil.java b/fabric/src/main/java/io/github/retrooper/packetevents/util/FabricCustomPipelineUtil.java new file mode 100644 index 0000000000..5996a9fc92 --- /dev/null +++ b/fabric/src/main/java/io/github/retrooper/packetevents/util/FabricCustomPipelineUtil.java @@ -0,0 +1,98 @@ +package io.github.retrooper.packetevents.util; + +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2016-2022 ViaVersion and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.MappingResolver; +import net.minecraft.network.PacketDeflater; +import net.minecraft.network.PacketInflater; + +public class FabricCustomPipelineUtil { + private static final MethodHandle FABRIC_PACKET_DECODE_BYTEBUF; + + static { + try { + // Get the mapping resolver to handle obfuscated names + MappingResolver resolver = FabricLoader.getInstance().getMappingResolver(); + + // Get the runtime (potentially obfuscated) class for CompressionDecoder + Class compressionDecoderClass = PacketInflater.class; + + // Map the method name from intermediary to runtime (obfuscated) names + String intermediaryMethodName = "decode"; // Intermediary method name + String intermediaryClassName = compressionDecoderClass.getName(); // Intermediary class name + + // Define the method descriptor in intermediary mappings + // Parameters: ChannelHandlerContext, ByteBuf, List + // Return type: void + String methodDescriptor = "(Lio/netty/channel/ChannelHandlerContext;Lio/netty/buffer/ByteBuf;Ljava/util/List;)V"; + + // Map the method name to the runtime (obfuscated) name + String mappedMethodName = resolver.mapMethodName( + "intermediary", + intermediaryClassName, + intermediaryMethodName, + methodDescriptor + ); + + // Create a MethodHandles.Lookup with private access + MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(compressionDecoderClass, MethodHandles.lookup()); + + // Define the method type for the decode method + MethodType methodType = MethodType.methodType( + void.class, // Return type + ChannelHandlerContext.class, // Parameter 1 + ByteBuf.class, // Parameter 2 + List.class // Parameter 3 + ); + + // Find the MethodHandle for the decode method + FABRIC_PACKET_DECODE_BYTEBUF = lookup.findVirtual( + compressionDecoderClass, + mappedMethodName, + methodType + ); + } catch (IllegalAccessException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + public static List callPacketDecodeByteBuf(PacketInflater decoder, ChannelHandlerContext ctx, ByteBuf msg) throws InvocationTargetException { + List output = new ArrayList<>(1); + + try { + FABRIC_PACKET_DECODE_BYTEBUF.invokeExact(decoder, ctx, msg, output); + } catch (Throwable e) { + e.printStackTrace(); + } + return output; + } +} + + diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/util/FabricInjectionUtil.java b/fabric/src/main/java/io/github/retrooper/packetevents/util/FabricInjectionUtil.java new file mode 100644 index 0000000000..a658ce0331 --- /dev/null +++ b/fabric/src/main/java/io/github/retrooper/packetevents/util/FabricInjectionUtil.java @@ -0,0 +1,122 @@ +package io.github.retrooper.packetevents.util; + +import com.github.retrooper.packetevents.PacketEvents; +import com.github.retrooper.packetevents.PacketEventsAPI; +import com.github.retrooper.packetevents.event.UserConnectEvent; +import com.github.retrooper.packetevents.event.UserLoginEvent; +import com.github.retrooper.packetevents.manager.protocol.ProtocolManager; +import com.github.retrooper.packetevents.protocol.ConnectionState; +import com.github.retrooper.packetevents.protocol.PacketSide; +import com.github.retrooper.packetevents.protocol.player.ClientVersion; +import com.github.retrooper.packetevents.protocol.player.User; +import com.github.retrooper.packetevents.protocol.player.UserProfile; +import com.github.retrooper.packetevents.util.FakeChannelUtil; +import com.github.retrooper.packetevents.util.PacketEventsImplHelper; +import io.github.retrooper.packetevents.factory.fabric.FabricPacketEventsAPI; +import io.github.retrooper.packetevents.handler.PacketDecoder; +import io.github.retrooper.packetevents.handler.PacketEncoder; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPipeline; +import net.minecraft.network.NetworkSide; +import net.minecraft.network.chat.TextComponent; +import net.minecraft.server.network.ServerPlayerEntity; + +public class FabricInjectionUtil { + private static final String VIA_DECODER_NAME = "via-decoder"; + private static final String VIA_ENCODER_NAME = "via-encoder"; + + public static void injectAtPipelineBuilder(ChannelPipeline pipeline, NetworkSide flow) { + PacketSide pipelineSide = switch (flow) { + case CLIENTBOUND -> PacketSide.CLIENT; + case SERVERBOUND -> PacketSide.SERVER; + }; + + FabricPacketEventsAPI fabricPacketEventsAPI = FabricPacketEventsAPI.getAPI(pipelineSide); + fabricPacketEventsAPI.getLogManager().debug("Game connected!"); + + Channel channel = pipeline.channel(); + User user = new User(channel, ConnectionState.HANDSHAKING, + null, new UserProfile(null, null)); + + fabricPacketEventsAPI.getProtocolManager().setUser(channel, user); + + UserConnectEvent connectEvent = new UserConnectEvent(user); + fabricPacketEventsAPI.getEventManager().callEvent(connectEvent); + if (connectEvent.isCancelled()) { + channel.unsafe().closeForcibly(); + return; + } + + String decoderName = channel.pipeline().names().contains("inbound_config") ? "inbound_config" : "decoder"; + channel.pipeline().addBefore(decoderName, PacketEvents.DECODER_NAME, new PacketDecoder(pipelineSide, user)); + String encoderName = channel.pipeline().names().contains("outbound_config") ? "outbound_config" : "encoder"; + channel.pipeline().addBefore(encoderName, PacketEvents.ENCODER_NAME, new PacketEncoder(pipelineSide, user)); + channel.closeFuture().addListener((ChannelFutureListener) future -> + PacketEventsImplHelper.handleDisconnection(user.getChannel(), user.getUUID())); + } + + // Shared method to remove handlers if they exist + public static void removeIfExists(ChannelPipeline pipeline, String handlerName) { + if (pipeline.get(handlerName) != null) { + pipeline.remove(handlerName); + } + } + + // Shared method to reorder handlers after ViaVersion + public static void reorderHandlers(ChannelHandlerContext ctx, PacketSide side) { + ChannelPipeline pipeline = ctx.pipeline(); + + // Re-inject decoder handler + ChannelHandler decoder = pipeline.get(PacketEvents.DECODER_NAME); + removeIfExists(pipeline, PacketEvents.DECODER_NAME); + if (decoder != null) { + if (pipeline.get(VIA_DECODER_NAME) != null) { + // If ViaVersion is present, add our decoder after via-decoder + pipeline.addAfter(VIA_DECODER_NAME, PacketEvents.DECODER_NAME, decoder); + } else { + // If ViaVersion is not present, add our decoder after decompress + String decoderName = pipeline.names().contains("inbound_config") ? "inbound_config" : "decoder"; + pipeline.addBefore(decoderName, PacketEvents.DECODER_NAME, decoder); + } + } + + // Re-inject encoder handler + ChannelHandler encoder = pipeline.get(PacketEvents.ENCODER_NAME); + removeIfExists(pipeline, PacketEvents.ENCODER_NAME); + if (encoder != null) { + if (pipeline.get(VIA_ENCODER_NAME) != null) { + // If ViaVersion is present, add our encoder after via-encoder + pipeline.addAfter(VIA_ENCODER_NAME, PacketEvents.ENCODER_NAME, encoder); + } else { + // If ViaVersion is not present, add our encoder after compress + String encoderName = pipeline.names().contains("outbound_config") ? "outbound_config" : "encoder"; + pipeline.addBefore(encoderName, PacketEvents.ENCODER_NAME, encoder); + } + } + + FabricPacketEventsAPI.getAPI(side).getLogManager().debug("Pipeline after reorder: " + pipeline.names()); + } + + public static void fireUserLoginEvent(ServerPlayerEntity player) { + FabricPacketEventsAPI api = FabricPacketEventsAPI.getServerAPI(); + + User user = api.getPlayerManager().getUser(player); + if (user == null) { + Object channelObj = api.getPlayerManager().getChannel(player); + + // Check if it's a fake connection + if (!FakeChannelUtil.isFakeChannel(channelObj) && + (!api.isTerminated() || api.getSettings().isKickIfTerminated())) { + // Kick the player if they're not a fake player + // player.connection.disconnect(Component.literal("PacketEvents 2.0 failed to inject")); + FabricPacketEventsAPI.getServerAPI().getPlayerManager().disconnectPlayer(player, "PacketEvents 2.0 failed to inject"); + } + return; + } + + api.getEventManager().callEvent(new UserLoginEvent(user, player)); + } +} diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/util/LazyHolder.java b/fabric/src/main/java/io/github/retrooper/packetevents/util/LazyHolder.java new file mode 100644 index 0000000000..327c49dc58 --- /dev/null +++ b/fabric/src/main/java/io/github/retrooper/packetevents/util/LazyHolder.java @@ -0,0 +1,57 @@ +package io.github.retrooper.packetevents.util; + +import java.util.function.Supplier; + +public interface LazyHolder { + static LazyHolder threadSafe(Supplier supplier) { + return new ThreadSafeLazyHolder<>(supplier); + } + + static LazyHolder simple(Supplier supplier) { + return new SimpleLazyHolder<>(supplier); + } + + T get(); +} + +final class ThreadSafeLazyHolder implements LazyHolder { + private final Supplier supplier; + private volatile T value; + + ThreadSafeLazyHolder(Supplier supplier) { + this.supplier = supplier; + } + + @Override + public T get() { + T result = value; + if (result == null) { + synchronized (this) { + result = value; + if (result == null) { + result = supplier.get(); + value = result; + } + } + } + return result; + } +} + +final class SimpleLazyHolder implements LazyHolder { + private T value; + private Supplier supplier; + + SimpleLazyHolder(Supplier supplier) { + this.supplier = supplier; + } + + @Override + public T get() { + if (supplier != null) { + value = supplier.get(); + supplier = null; + } + return value; + } +} \ No newline at end of file diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/util/viaversion/ViaVersionAccessor.java b/fabric/src/main/java/io/github/retrooper/packetevents/util/viaversion/ViaVersionAccessor.java new file mode 100644 index 0000000000..d0553bd7e5 --- /dev/null +++ b/fabric/src/main/java/io/github/retrooper/packetevents/util/viaversion/ViaVersionAccessor.java @@ -0,0 +1,8 @@ +package io.github.retrooper.packetevents.util.viaversion; + +import com.github.retrooper.packetevents.protocol.player.User; + +public interface ViaVersionAccessor { + + int getProtocolVersion(User user); +} \ No newline at end of file diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/util/viaversion/ViaVersionAccessorImpl.java b/fabric/src/main/java/io/github/retrooper/packetevents/util/viaversion/ViaVersionAccessorImpl.java new file mode 100644 index 0000000000..1600bf8fd4 --- /dev/null +++ b/fabric/src/main/java/io/github/retrooper/packetevents/util/viaversion/ViaVersionAccessorImpl.java @@ -0,0 +1,29 @@ +package io.github.retrooper.packetevents.util.viaversion; + +import com.github.retrooper.packetevents.PacketEvents; +import com.github.retrooper.packetevents.protocol.player.User; +import com.github.retrooper.packetevents.util.reflection.Reflection; +import com.viaversion.viaversion.api.connection.UserConnection; +import io.netty.channel.Channel; + +import java.lang.reflect.Field; + +public class ViaVersionAccessorImpl implements ViaVersionAccessor { + + private static Field CONNECTION_FIELD; + + @Override + public int getProtocolVersion(User user) { + try { + Object viaEncoder = ((Channel) user.getChannel()).pipeline().get("via-encoder"); + if (CONNECTION_FIELD == null) { + CONNECTION_FIELD = Reflection.getField(viaEncoder.getClass(), "info"); + } + UserConnection connection = (UserConnection) CONNECTION_FIELD.get(viaEncoder); + return connection.getProtocolInfo().getProtocolVersion(); + } catch (IllegalAccessException e) { + PacketEvents.getAPI().getLogManager().warn("Unable to grab ViaVersion client version for player!"); + return -1; + } + } +} \ No newline at end of file diff --git a/fabric/src/main/java/io/github/retrooper/packetevents/util/viaversion/ViaVersionUtil.java b/fabric/src/main/java/io/github/retrooper/packetevents/util/viaversion/ViaVersionUtil.java new file mode 100644 index 0000000000..a1d30f3407 --- /dev/null +++ b/fabric/src/main/java/io/github/retrooper/packetevents/util/viaversion/ViaVersionUtil.java @@ -0,0 +1,64 @@ +package io.github.retrooper.packetevents.util.viaversion; + +import com.github.retrooper.packetevents.protocol.player.User; +import io.netty.channel.Channel; +import net.fabricmc.api.EnvType; +import net.fabricmc.loader.api.FabricLoader; + +public class ViaVersionUtil { + private static ViaState available = ViaState.UNKNOWN; + private static ViaVersionAccessor viaVersionAccessor; + private static final FabricLoader loader = FabricLoader.getInstance(); + private static final EnvType envType = loader.getEnvironmentType(); + + private ViaVersionUtil() { + } + + private static void load(User user) { + if (viaVersionAccessor == null) { + try { + Class.forName("com.viaversion.viaversion.api.Via"); + // Possible for ViaFabricPlus to be loaded but not be activated + if (envType == EnvType.CLIENT && ((Channel) user.getChannel()).pipeline().get("via-encoder") == null) return; + viaVersionAccessor = new ViaVersionAccessorImpl(); + available = ViaState.ENABLED; + } catch (Exception e) { + viaVersionAccessor = null; + } + } + } + + public static void checkIfViaIsPresent() { + boolean present = loader.getModContainer("viaversion").isPresent(); + if (!present) { + available = ViaState.DISABLED; + } else if (envType == EnvType.SERVER) { + available = ViaState.ENABLED; + } else if (envType == EnvType.CLIENT) { + // ViaFabricPlus can be unloaded client side! + available = ViaState.UNKNOWN; + } + } + + public static boolean isAvailable(User user) { + if (available == ViaState.UNKNOWN) { + return getViaVersionAccessor(user) != null; + } + return available == ViaState.ENABLED; + } + + public static ViaVersionAccessor getViaVersionAccessor(User user) { + load(user); + return viaVersionAccessor; + } + + public static int getProtocolVersion(User user) { + return getViaVersionAccessor(user).getProtocolVersion(user); + } +} + +enum ViaState { + UNKNOWN, + DISABLED, + ENABLED +} \ No newline at end of file diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index ada6672aa0..5582fe056f 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -12,12 +12,6 @@ "icon": "assets/packetevents/icon.png", "environment": "*", "entrypoints": { - "pePreLaunchClient": [ - "io.github.retrooper.packetevents.PacketEventsClientMod" - ], - "pePreLaunchServer": [ - "io.github.retrooper.packetevents.PacketEventsServerMod" - ], "preLaunch": [ "io.github.retrooper.packetevents.PacketEventsMod" ], @@ -30,6 +24,6 @@ ], "accessWidener": "packetevents.accesswidener", "depends": { - "fabricloader": ">=0.15.11" + "fabricloader": "*" } } diff --git a/fabric/src/main/resources/packetevents.accesswidener b/fabric/src/main/resources/packetevents.accesswidener index aba82a9b71..1525705a27 100644 --- a/fabric/src/main/resources/packetevents.accesswidener +++ b/fabric/src/main/resources/packetevents.accesswidener @@ -1,9 +1,6 @@ -accessWidener v1 named - +accessWidener v2 named # fields -accessible field net/minecraft/server/network/ServerCommonPacketListenerImpl connection Lnet/minecraft/network/Connection; -accessible field net/minecraft/network/Connection channel Lio/netty/channel/Channel; # methods diff --git a/fabric/src/main/resources/packetevents.mixins.json b/fabric/src/main/resources/packetevents.mixins.json index e815929d31..c82991dcc8 100644 --- a/fabric/src/main/resources/packetevents.mixins.json +++ b/fabric/src/main/resources/packetevents.mixins.json @@ -3,13 +3,6 @@ "minVersion": "0.8", "package": "io.github.retrooper.packetevents.mixin", "compatibilityLevel": "JAVA_17", - "mixins": [ - "ConnectionMixin", - "PlayerListMixin" - ], - "client": [ - "ClientPacketListenerMixin" - ], "injectors": { "defaultRequire": 1 } diff --git a/libs.versions.toml b/libs.versions.toml index 0c5418cd43..761fdadf20 100644 --- a/libs.versions.toml +++ b/libs.versions.toml @@ -10,7 +10,7 @@ paper = "1.20.6-R0.1-SNAPSHOT" bungeecord = "1.21-R0.1-SNAPSHOT" velocity = "3.4.0-SNAPSHOT" run-paper = "2.3.1" -fabric-loom = "1.10.5" +fabric-loom = "1.10-SNAPSHOT" spongeGradle = "2.2.0" classgraph = "4.8.179" diff --git a/netty-common/build.gradle.kts b/netty-common/build.gradle.kts index d8db1ca103..bab0850c06 100644 --- a/netty-common/build.gradle.kts +++ b/netty-common/build.gradle.kts @@ -1,9 +1,8 @@ plugins { - packetevents.`shadow-conventions` packetevents.`library-conventions` } dependencies { compileOnly(libs.netty) - shadow(project(":api", "shadow")) + implementation(project(":api", "shadow")) } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 60cf9969b9..483a7595a9 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -34,5 +34,10 @@ include("bungeecord") include("velocity") include("sponge") include("fabric") +include(":fabric:mc1140") +include(":fabric:mc1194") +include(":fabric:mc1202") +include(":fabric:mc1211") +include(":fabric:mc1215") // Patch modules include(":patch:adventure-text-serializer-gson") diff --git a/testlibs.versions.toml b/testlibs.versions.toml index eafb5cc1cc..f3e2ce38a4 100644 --- a/testlibs.versions.toml +++ b/testlibs.versions.toml @@ -1,5 +1,5 @@ [versions] -mockbukkit = "3.131.0" +mockbukkit = "3.133.2" slf4j = "2.0.16" junit = "5.11.2"