Skip to content

Commit fb30751

Browse files
authored
Merge pull request #22 from MinceraftMC/feat/1.21.11
Implement 1.21.11 support
2 parents bf72f71 + ddc45e7 commit fb30751

File tree

9 files changed

+439
-2
lines changed

9 files changed

+439
-2
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import io.papermc.paperweight.userdev.ReobfArtifactConfiguration
2+
3+
plugins {
4+
id("io.papermc.paperweight.userdev")
5+
}
6+
7+
dependencies {
8+
api(project(":platform-common"))
9+
paperweight.paperDevBundle("1.21.11-R0.1-SNAPSHOT")
10+
}
11+
12+
java {
13+
sourceCompatibility = JavaVersion.VERSION_21
14+
targetCompatibility = JavaVersion.VERSION_21
15+
}
16+
17+
// hack to allow depending on this platform in java 17 projects
18+
configurations.runtimeElements.configure {
19+
attributes.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
20+
}
21+
22+
paperweight {
23+
reobfArtifactConfiguration = ReobfArtifactConfiguration.MOJANG_PRODUCTION
24+
}
25+
26+
tasks.reobfJar {
27+
// hacky workaround to make the shadow plugin shadow our mojang-mapped jar
28+
outputJar.set(tasks.jar.map { it.outputs.files.singleFile }.get())
29+
enabled = false
30+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package de.pianoman911.mapengine.common;
2+
3+
import de.pianoman911.mapengine.api.util.PassthroughMode;
4+
import de.pianoman911.mapengine.common.platform.IListenerBridge;
5+
import io.netty.channel.ChannelHandlerContext;
6+
import io.netty.handler.codec.MessageToMessageDecoder;
7+
import net.minecraft.network.protocol.Packet;
8+
import net.minecraft.network.protocol.game.ClientboundAnimatePacket;
9+
import net.minecraft.network.protocol.game.ServerboundInteractPacket;
10+
import net.minecraft.network.protocol.game.ServerboundSwingPacket;
11+
import net.minecraft.world.InteractionHand;
12+
import net.minecraft.world.phys.Vec3;
13+
import org.bukkit.craftbukkit.entity.CraftPlayer;
14+
import org.bukkit.entity.Player;
15+
import org.jetbrains.annotations.NotNull;
16+
17+
import java.util.List;
18+
19+
public final class Paper12111Listener extends MessageToMessageDecoder<Packet<?>> implements ServerboundInteractPacket.Handler {
20+
21+
private final Player player;
22+
private final IListenerBridge bridge;
23+
24+
private int entityId;
25+
private PassthroughMode passthroughMode;
26+
27+
public Paper12111Listener(Player player, IListenerBridge bridge) {
28+
this.player = player;
29+
this.bridge = bridge;
30+
}
31+
32+
@Override
33+
public boolean acceptInboundMessage(Object msg) {
34+
return msg instanceof ServerboundInteractPacket || msg instanceof ServerboundSwingPacket;
35+
}
36+
37+
@Override
38+
protected void decode(ChannelHandlerContext ctx, Packet<?> msg, List<Object> out) {
39+
if (msg instanceof ServerboundInteractPacket interact) {
40+
this.entityId = interact.getEntityId();
41+
interact.dispatch(this);
42+
} else if (msg instanceof ServerboundSwingPacket) {
43+
this.passthroughMode = this.bridge.handleSwing(this.player);
44+
if (this.passthroughMode == PassthroughMode.ONLY_ANIMATION) {
45+
ClientboundAnimatePacket animatePacket = new ClientboundAnimatePacket(((CraftPlayer) this.player).getHandle(),
46+
ClientboundAnimatePacket.SWING_MAIN_HAND);
47+
this.player.getTrackedBy().forEach(player -> ((CraftPlayer) player).getHandle().connection.send(animatePacket));
48+
}
49+
}
50+
if (this.passthroughMode != null && this.passthroughMode != PassthroughMode.ALL) {
51+
return;
52+
}
53+
54+
out.add(msg);
55+
}
56+
57+
@Override
58+
public void onInteraction(@NotNull InteractionHand hand) {
59+
// onInteraction(InteractionHand, Vec3) is called instead
60+
}
61+
62+
@Override
63+
public void onInteraction(@NotNull InteractionHand hand, Vec3 pos) {
64+
this.passthroughMode = this.bridge.handleInteract(this.player, this.entityId, pos.x, pos.y, pos.z);
65+
}
66+
67+
@Override
68+
public void onAttack() {
69+
this.passthroughMode = this.bridge.handleAttack(this.player, this.entityId);
70+
}
71+
}
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
package de.pianoman911.mapengine.common;
2+
3+
import de.pianoman911.mapengine.common.data.MapUpdateData;
4+
import de.pianoman911.mapengine.common.platform.IListenerBridge;
5+
import de.pianoman911.mapengine.common.platform.IPlatform;
6+
import de.pianoman911.mapengine.common.platform.PacketContainer;
7+
import io.netty.channel.Channel;
8+
import io.papermc.paper.adventure.PaperAdventure;
9+
import it.unimi.dsi.fastutil.ints.IntList;
10+
import net.minecraft.SharedConstants;
11+
import net.minecraft.core.component.DataComponents;
12+
import net.minecraft.core.registries.BuiltInRegistries;
13+
import net.minecraft.network.protocol.Packet;
14+
import net.minecraft.network.protocol.game.ClientGamePacketListener;
15+
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
16+
import net.minecraft.network.protocol.game.ClientboundBundlePacket;
17+
import net.minecraft.network.protocol.game.ClientboundMapItemDataPacket;
18+
import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket;
19+
import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket;
20+
import net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket;
21+
import net.minecraft.network.syncher.EntityDataAccessor;
22+
import net.minecraft.network.syncher.EntityDataSerializers;
23+
import net.minecraft.network.syncher.SynchedEntityData;
24+
import net.minecraft.server.MinecraftServer;
25+
import net.minecraft.world.entity.EntityType;
26+
import net.minecraft.world.entity.PositionMoveRotation;
27+
import net.minecraft.world.entity.decoration.ItemFrame;
28+
import net.minecraft.world.item.ItemStack;
29+
import net.minecraft.world.item.Items;
30+
import net.minecraft.world.level.saveddata.maps.MapDecoration;
31+
import net.minecraft.world.level.saveddata.maps.MapId;
32+
import net.minecraft.world.level.saveddata.maps.MapItemSavedData.MapPatch;
33+
import net.minecraft.world.phys.Vec3;
34+
import org.bukkit.Bukkit;
35+
import org.bukkit.block.BlockFace;
36+
import org.bukkit.craftbukkit.entity.CraftPlayer;
37+
import org.bukkit.entity.Player;
38+
import org.bukkit.event.EventHandler;
39+
import org.bukkit.event.Listener;
40+
import org.bukkit.event.player.PlayerJoinEvent;
41+
import org.bukkit.event.player.PlayerQuitEvent;
42+
import org.bukkit.map.MapCursor;
43+
import org.bukkit.map.MapCursorCollection;
44+
import org.bukkit.plugin.Plugin;
45+
import org.bukkit.util.BlockVector;
46+
import org.bukkit.util.Vector;
47+
48+
import java.util.ArrayList;
49+
import java.util.List;
50+
import java.util.Objects;
51+
import java.util.Optional;
52+
import java.util.Set;
53+
import java.util.UUID;
54+
55+
public class Paper12111Platform implements IPlatform<Packet<ClientGamePacketListener>>, Listener {
56+
57+
private static final EntityDataAccessor<Byte> DATA_SHARED_FLAGS_ID = EntityDataSerializers.BYTE.createAccessor(0);
58+
private static final EntityDataAccessor<Float> DATA_INTERACTION_BOX_WIDTH_ID = EntityDataSerializers.FLOAT.createAccessor(8);
59+
private static final EntityDataAccessor<Float> DATA_INTERACTION_BOX_HEIGHT_ID = EntityDataSerializers.FLOAT.createAccessor(9);
60+
private static final EntityDataAccessor<Boolean> DATA_INTERACTION_BOX_RESPONSIVE_ID = EntityDataSerializers.BOOLEAN.createAccessor(10);
61+
62+
private static final Paper12111SynchedDataBuilder ITEM_FRAME_DATA = Paper12111SynchedDataBuilder.builder()
63+
.setDataItem(DATA_SHARED_FLAGS_ID, (byte) 0x00)
64+
.setDataItem(ItemFrame.DATA_ITEM, ItemStack.EMPTY)
65+
.setDataItem(ItemFrame.DATA_ROTATION, 0);
66+
private static final Paper12111SynchedDataBuilder INTERACTION_DATA = Paper12111SynchedDataBuilder.builder()
67+
.setDataItem(DATA_SHARED_FLAGS_ID, (byte) 0x00)
68+
.setDataItem(DATA_INTERACTION_BOX_WIDTH_ID, 0f)
69+
.setDataItem(DATA_INTERACTION_BOX_HEIGHT_ID, 0f)
70+
.setDataItem(DATA_INTERACTION_BOX_RESPONSIVE_ID, false);
71+
72+
private final IListenerBridge bridge;
73+
74+
public Paper12111Platform(Plugin plugin, IListenerBridge bridge) {
75+
this.bridge = bridge;
76+
Bukkit.getPluginManager().registerEvents(this, plugin);
77+
}
78+
79+
@EventHandler
80+
public void onJoin(PlayerJoinEvent event) {
81+
Paper12111Listener listener = new Paper12111Listener(event.getPlayer(), this.bridge);
82+
((CraftPlayer) event.getPlayer()).getHandle().connection.connection.channel
83+
.pipeline().addAfter("decoder", "mapengine", listener);
84+
}
85+
86+
@EventHandler
87+
public void onQuit(PlayerQuitEvent event) {
88+
// remove mapengine pipeline handler if the player is still connected
89+
// while this event is being fired; this prevents errors if the player goes
90+
// through a second configuration phase
91+
Channel ch = ((CraftPlayer) event.getPlayer()).getHandle().connection.connection.channel;
92+
ch.eventLoop().execute(() -> {
93+
if (ch.isActive()) {
94+
ch.pipeline().remove("mapengine");
95+
}
96+
});
97+
}
98+
99+
@Override
100+
public String getDisplayedName() {
101+
return MinecraftServer.getServer().getServerModName()
102+
+ " " + SharedConstants.getCurrentVersion().name();
103+
}
104+
105+
@Override
106+
public void sendPacket(Player player, PacketContainer<Packet<ClientGamePacketListener>> packet) {
107+
((CraftPlayer) player).getHandle().connection.connection.channel.write(packet.getPacket());
108+
}
109+
110+
@Override
111+
public void flush(Player player) {
112+
((CraftPlayer) player).getHandle().connection.connection.channel.flush();
113+
}
114+
115+
@Override
116+
public PacketContainer<Packet<ClientGamePacketListener>> createMapDataPacket(MapUpdateData data, int mapId, MapCursorCollection cursors) {
117+
MapId id = new MapId(mapId);
118+
MapPatch updateData = new MapPatch(data.offsetX(), data.offsetY(), data.width(), data.height(), data.buffer());
119+
120+
List<MapDecoration> decorations;
121+
if (cursors != null && cursors.size() > 0) {
122+
decorations = new ArrayList<>(cursors.size());
123+
for (int i = 0; i < cursors.size(); i++) {
124+
MapCursor cursor = cursors.getCursor(i);
125+
if (!cursor.isVisible()) {
126+
continue;
127+
}
128+
129+
decorations.add(new MapDecoration(BuiltInRegistries.MAP_DECORATION_TYPE.get(cursor.getRawType()).orElseThrow(),
130+
cursor.getX(), cursor.getY(), cursor.getDirection(),
131+
cursor.caption() == null ? Optional.empty() : Optional.of(PaperAdventure.asVanilla(cursor.caption()))));
132+
}
133+
} else {
134+
decorations = null;
135+
}
136+
137+
return PacketContainer.wrap(this, new ClientboundMapItemDataPacket(id,
138+
(byte) 0, decorations != null, decorations, updateData));
139+
}
140+
141+
@Override
142+
public PacketContainer<Packet<ClientGamePacketListener>> createMapEntitySpawnPacket(int entityId, BlockVector pos, BlockFace facing, boolean glowing) {
143+
int facingIndex = switch (facing) {
144+
case UP -> 1;
145+
case NORTH -> 2;
146+
case SOUTH -> 3;
147+
case WEST -> 4;
148+
case EAST -> 5;
149+
default -> 0;
150+
};
151+
152+
return PacketContainer.wrap(this, new ClientboundAddEntityPacket(entityId, UUID.randomUUID(),
153+
pos.getX(), pos.getY(), pos.getZ(), 0, 0,
154+
glowing ? EntityType.GLOW_ITEM_FRAME : EntityType.ITEM_FRAME, facingIndex, Vec3.ZERO, 0));
155+
}
156+
157+
@Override
158+
public PacketContainer<Packet<ClientGamePacketListener>> createMapSetIdPacket(int entityId, int mapId, boolean invisible) {
159+
SynchedEntityData entityData = ITEM_FRAME_DATA.build();
160+
161+
ItemStack mapItem = Items.FILLED_MAP.getDefaultInstance();
162+
163+
mapItem.set(DataComponents.MAP_ID, new MapId(mapId));
164+
165+
entityData.set(ItemFrame.DATA_ITEM, mapItem); // map item
166+
167+
if (invisible) {
168+
entityData.set(DATA_SHARED_FLAGS_ID, (byte) 0x20); // invisible
169+
}
170+
171+
return PacketContainer.wrap(this, new ClientboundSetEntityDataPacket(entityId, Objects.requireNonNull(entityData.packDirty())));
172+
}
173+
174+
@Override
175+
public PacketContainer<Packet<ClientGamePacketListener>> createRemoveEntitiesPacket(IntList entityIds) {
176+
return PacketContainer.wrap(this, new ClientboundRemoveEntitiesPacket(entityIds));
177+
}
178+
179+
@Override
180+
public PacketContainer<?> createInteractionEntitySpawnPacket(int interactionId, Vector pos, BlockFace direction) {
181+
return PacketContainer.wrap(this, new ClientboundAddEntityPacket(interactionId, UUID.randomUUID(),
182+
pos.getX(), pos.getY(), pos.getZ(), 0, 0, EntityType.INTERACTION, 0, Vec3.ZERO, 0));
183+
}
184+
185+
@Override
186+
public PacketContainer<?> createInteractionEntityBlockSizePacket(int interactionId) {
187+
SynchedEntityData entityData = INTERACTION_DATA.build();
188+
189+
entityData.set(DATA_INTERACTION_BOX_WIDTH_ID, 1f);
190+
entityData.set(DATA_INTERACTION_BOX_HEIGHT_ID, 1f);
191+
entityData.set(DATA_INTERACTION_BOX_RESPONSIVE_ID, true);
192+
entityData.set(DATA_SHARED_FLAGS_ID, (byte) 0x20); // invisible
193+
194+
return PacketContainer.wrap(this, new ClientboundSetEntityDataPacket(interactionId, Objects.requireNonNull(entityData.packDirty())));
195+
}
196+
197+
@Override
198+
public PacketContainer<?> createTeleportPacket(int entityId, Vector pos, float yaw, float pitch, boolean onGround) {
199+
PositionMoveRotation posData = new PositionMoveRotation(
200+
new Vec3(pos.getX(), pos.getY(), pos.getZ()), Vec3.ZERO, yaw, pitch);
201+
ClientboundTeleportEntityPacket packet = new ClientboundTeleportEntityPacket(
202+
entityId, posData, Set.of(), onGround);
203+
return PacketContainer.wrap(this, packet);
204+
}
205+
206+
@Override
207+
public PacketContainer<?> createItemRotationPacket(int entityId, int rotation) {
208+
SynchedEntityData entityData = ITEM_FRAME_DATA.build();
209+
210+
entityData.set(ItemFrame.DATA_ROTATION, rotation); // item rotation (0-7)
211+
212+
return PacketContainer.wrap(this, new ClientboundSetEntityDataPacket(entityId, Objects.requireNonNull(entityData.packDirty())));
213+
}
214+
215+
@SuppressWarnings("unchecked")
216+
@Override
217+
public void sendBundled(Player player, PacketContainer<?>... packets) {
218+
List<Packet<? super ClientGamePacketListener>> mcPackets = new ArrayList<>(packets.length);
219+
for (PacketContainer<?> packetContainer : packets) {
220+
mcPackets.add((Packet<ClientGamePacketListener>) packetContainer.getPacket());
221+
}
222+
ClientboundBundlePacket packet = new ClientboundBundlePacket(mcPackets);
223+
((CraftPlayer) player).getHandle().connection.send(packet);
224+
}
225+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package de.pianoman911.mapengine.common;
2+
3+
import de.pianoman911.mapengine.common.platform.IListenerBridge;
4+
import de.pianoman911.mapengine.common.platform.IPlatform;
5+
import de.pianoman911.mapengine.common.platform.IPlatformProvider;
6+
import it.unimi.dsi.fastutil.ints.IntSet;
7+
import org.bukkit.Bukkit;
8+
import org.bukkit.plugin.Plugin;
9+
10+
import java.util.Optional;
11+
12+
public final class Paper12111Provider implements IPlatformProvider {
13+
14+
private static final IntSet SUPPORTED_PROTOCOLS = IntSet.of(
15+
774 // 1.21.11
16+
);
17+
18+
@SuppressWarnings("deprecation") // bukkit unsafe
19+
@Override
20+
public Optional<IPlatform<?>> tryProvide(Plugin plugin, IListenerBridge bridge) {
21+
// check we're actually mojang-mapped
22+
if (IPlatformProvider.existsClass("org.bukkit.craftbukkit.CraftServer")
23+
&& SUPPORTED_PROTOCOLS.contains(Bukkit.getUnsafe().getProtocolVersion())) {
24+
return Optional.of(Paper12111StaticProvider.provide(plugin, bridge));
25+
}
26+
return Optional.empty();
27+
}
28+
}
29+
30+
final class Paper12111StaticProvider {
31+
32+
private Paper12111StaticProvider() {
33+
}
34+
35+
static IPlatform<?> provide(Plugin plugin, IListenerBridge bridge) {
36+
return new Paper12111Platform(plugin, bridge);
37+
}
38+
}

0 commit comments

Comments
 (0)