Skip to content

Commit dcc7554

Browse files
committed
feat!: replace ProtocolLib integration with PacketEvents
PacketEvents becomes the single packet interception backend so inventory protection and tab-complete blocking follow the new platform adapter lifecycle across all deliverables. BREAKING CHANGE: features that previously depended on ProtocolLib now require PacketEvents, and plugin manifests, docs, and test fixtures have been updated to reflect the new optional dependency
1 parent 25ecdb5 commit dcc7554

38 files changed

Lines changed: 517 additions & 386 deletions

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ You can also create your own translation file and, if you want, you can share it
5555
<li><strong>Fully compatible with Citizens2, CombatTag, CombatTagPlus!</strong></li>
5656
<li>Compatible with Minecraft mods like <strong>BuildCraft or RedstoneCraft</strong></li>
5757
<li>Restricted users (associate a username with an IP)</li>
58-
<li>Protect player's inventory until correct authentication (requires ProtocolLib)</li>
58+
<li>Protect player's inventory until correct authentication (requires PacketEvents)</li>
5959
<li>Saves the quit location of the player</li>
6060
<li>Automatic database backup</li>
6161
<li>Available languages: <a href="https://github.com/AuthMe/AuthMeReloaded/blob/master/docs/translations.md">translations</a></li>
@@ -132,7 +132,7 @@ You can also create your own translation file and, if you want, you can share it
132132
> - `AuthMe-*-Spigot-1.21.jar` (Spigot 1.20.x – 1.21.x)
133133
> - `AuthMe-*-Paper.jar` (Paper 1.21+)
134134
> - `AuthMe-*-Folia.jar` (Folia 1.21+)
135-
>- ProtocolLib (optional, required by some features)
135+
>- PacketEvents (optional, required by some features)
136136
137137
## Credits
138138

authme-core/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@
106106
<artifactId>bstats-bukkit</artifactId>
107107
</dependency>
108108
<dependency>
109-
<groupId>com.comphenix.protocol</groupId>
110-
<artifactId>ProtocolLib</artifactId>
109+
<groupId>com.github.retrooper</groupId>
110+
<artifactId>packetevents-spigot</artifactId>
111111
</dependency>
112112
<dependency>
113113
<groupId>net.luckperms</groupId>

authme-core/src/main/java/fr/xephi/authme/AuthMe.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import fr.xephi.authme.platform.CommandRegistrationAdapter;
4242
import fr.xephi.authme.platform.DialogAdapter;
4343
import fr.xephi.authme.platform.EventRegistrationAdapter;
44+
import fr.xephi.authme.platform.PacketInterceptionAdapter;
4445
import fr.xephi.authme.platform.PlatformAdapter;
4546
import fr.xephi.authme.platform.TeleportAdapter;
4647

@@ -222,6 +223,7 @@ private void initialize(PlatformAdapter platformAdapter) {
222223
injector.register(SchedulingAdapter.class, platformAdapter);
223224
injector.register(DialogAdapter.class, platformAdapter);
224225
injector.register(CommandRegistrationAdapter.class, platformAdapter);
226+
injector.register(PacketInterceptionAdapter.class, platformAdapter);
225227

226228
// Get settings and set up logger
227229
settings = injector.getSingleton(Settings.class);

authme-core/src/main/java/fr/xephi/authme/listener/ServerListener.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import fr.xephi.authme.ConsoleLogger;
44
import fr.xephi.authme.output.ConsoleLoggerFactory;
5-
import fr.xephi.authme.listener.protocollib.ProtocolLibService;
5+
import fr.xephi.authme.listener.packetevents.PacketEventsService;
66
import fr.xephi.authme.permission.PermissionsManager;
77
import fr.xephi.authme.service.PluginHookService;
88
import fr.xephi.authme.settings.SpawnLoader;
@@ -26,7 +26,7 @@ public class ServerListener implements Listener {
2626
@Inject
2727
private SpawnLoader spawnLoader;
2828
@Inject
29-
private ProtocolLibService protocolLibService;
29+
private PacketEventsService packetEventsService;
3030
@Inject
3131
private PermissionsManager permissionsManager;
3232

@@ -50,9 +50,9 @@ public void onPluginDisable(PluginDisableEvent event) {
5050
} else if ("EssentialsSpawn".equalsIgnoreCase(pluginName)) {
5151
spawnLoader.unloadEssentialsSpawn();
5252
logger.info("EssentialsSpawn has been disabled: unhooking");
53-
} else if ("ProtocolLib".equalsIgnoreCase(pluginName)) {
54-
protocolLibService.disable();
55-
logger.warning("ProtocolLib has been disabled, unhooking packet adapters!");
53+
} else if ("packetevents".equalsIgnoreCase(pluginName)) {
54+
packetEventsService.disable();
55+
logger.warning("PacketEvents has been disabled, unhooking packet listeners!");
5656
}
5757
}
5858

@@ -72,8 +72,8 @@ public void onPluginEnable(PluginEnableEvent event) {
7272
} else if ("CMI".equalsIgnoreCase(pluginName)) {
7373
pluginHookService.tryHookToCmi();
7474
spawnLoader.loadCmiSpawn();
75-
} else if ("ProtocolLib".equalsIgnoreCase(pluginName)) {
76-
protocolLibService.setup();
75+
} else if ("packetevents".equalsIgnoreCase(pluginName)) {
76+
packetEventsService.setup();
7777
}
7878
}
7979
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package fr.xephi.authme.listener.packetevents;
2+
3+
import com.github.retrooper.packetevents.PacketEvents;
4+
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
5+
import com.github.retrooper.packetevents.event.PacketSendEvent;
6+
import com.github.retrooper.packetevents.protocol.item.ItemStack;
7+
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
8+
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSetSlot;
9+
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerWindowItems;
10+
import fr.xephi.authme.ConsoleLogger;
11+
import fr.xephi.authme.data.auth.PlayerCache;
12+
import fr.xephi.authme.datasource.DataSource;
13+
import fr.xephi.authme.output.ConsoleLoggerFactory;
14+
import org.bukkit.entity.Player;
15+
16+
import java.util.Collections;
17+
18+
public class InventoryPacketListener extends PacketListenerAbstract {
19+
20+
private static final int PLAYER_INVENTORY = 0;
21+
// Crafting (5) + Armor (4) + Main inventory (27) + Hotbar (9) = 45 slots
22+
private static final int INVENTORY_SIZE = 45;
23+
24+
private final ConsoleLogger logger = ConsoleLoggerFactory.get(InventoryPacketListener.class);
25+
private final PlayerCache playerCache;
26+
private final DataSource dataSource;
27+
28+
public InventoryPacketListener(PlayerCache playerCache, DataSource dataSource) {
29+
this.playerCache = playerCache;
30+
this.dataSource = dataSource;
31+
}
32+
33+
@Override
34+
public void onPacketSend(PacketSendEvent event) {
35+
if (event.getPacketType() == PacketType.Play.Server.WINDOW_ITEMS) {
36+
if (new WrapperPlayServerWindowItems(event).getWindowId() == PLAYER_INVENTORY) {
37+
cancelIfShouldHide(event);
38+
}
39+
} else if (event.getPacketType() == PacketType.Play.Server.SET_SLOT) {
40+
if (new WrapperPlayServerSetSlot(event).getWindowId() == PLAYER_INVENTORY) {
41+
cancelIfShouldHide(event);
42+
}
43+
}
44+
}
45+
46+
private void cancelIfShouldHide(PacketSendEvent event) {
47+
Player player = event.getPlayer();
48+
if (player != null && !playerCache.isAuthenticated(player.getName())
49+
&& dataSource.isAuthAvailable(player.getName())) {
50+
event.setCancelled(true);
51+
}
52+
}
53+
54+
public void sendBlankInventoryPacket(Player player) {
55+
WrapperPlayServerWindowItems packet = new WrapperPlayServerWindowItems(
56+
PLAYER_INVENTORY, 0,
57+
Collections.nCopies(INVENTORY_SIZE, ItemStack.EMPTY),
58+
ItemStack.EMPTY
59+
);
60+
try {
61+
PacketEvents.getAPI().getPlayerManager().sendPacket(player, packet);
62+
} catch (Exception e) {
63+
logger.logException("Error during sending blank inventory", e);
64+
}
65+
}
66+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package fr.xephi.authme.listener.packetevents;
2+
3+
import com.github.retrooper.packetevents.PacketEvents;
4+
import fr.xephi.authme.data.auth.PlayerCache;
5+
import fr.xephi.authme.datasource.DataSource;
6+
import fr.xephi.authme.platform.PacketInterceptionAdapter;
7+
import org.bukkit.entity.Player;
8+
9+
/**
10+
* PacketEvents-backed implementation of {@link PacketInterceptionAdapter}.
11+
* Instantiated lazily so PacketEvents classes are only resolved when the dependency is available.
12+
*/
13+
public final class PacketEventsListenerRegistry implements PacketInterceptionAdapter {
14+
15+
private InventoryPacketListener inventoryPacketListener;
16+
private TabCompletePacketListener tabCompletePacketListener;
17+
18+
@Override
19+
public void registerInventoryProtection(PlayerCache playerCache, DataSource dataSource) {
20+
if (inventoryPacketListener == null) {
21+
inventoryPacketListener = new InventoryPacketListener(playerCache, dataSource);
22+
}
23+
PacketEvents.getAPI().getEventManager().registerListener(inventoryPacketListener);
24+
}
25+
26+
@Override
27+
public void unregisterInventoryProtection() {
28+
if (inventoryPacketListener != null) {
29+
PacketEvents.getAPI().getEventManager().unregisterListener(inventoryPacketListener);
30+
inventoryPacketListener = null;
31+
}
32+
}
33+
34+
@Override
35+
public void sendBlankInventoryPacket(Player player) {
36+
if (inventoryPacketListener != null) {
37+
inventoryPacketListener.sendBlankInventoryPacket(player);
38+
}
39+
}
40+
41+
@Override
42+
public void registerTabCompleteBlock(PlayerCache playerCache) {
43+
if (tabCompletePacketListener == null) {
44+
tabCompletePacketListener = new TabCompletePacketListener(playerCache);
45+
}
46+
PacketEvents.getAPI().getEventManager().registerListener(tabCompletePacketListener);
47+
}
48+
49+
@Override
50+
public void unregisterTabCompleteBlock() {
51+
if (tabCompletePacketListener != null) {
52+
PacketEvents.getAPI().getEventManager().unregisterListener(tabCompletePacketListener);
53+
tabCompletePacketListener = null;
54+
}
55+
}
56+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package fr.xephi.authme.listener.packetevents;
2+
3+
import ch.jalu.injector.annotations.NoFieldScan;
4+
import fr.xephi.authme.ConsoleLogger;
5+
import fr.xephi.authme.data.auth.PlayerCache;
6+
import fr.xephi.authme.datasource.DataSource;
7+
import fr.xephi.authme.initialization.SettingsDependent;
8+
import fr.xephi.authme.output.ConsoleLoggerFactory;
9+
import fr.xephi.authme.platform.PacketInterceptionAdapter;
10+
import fr.xephi.authme.service.BukkitService;
11+
import fr.xephi.authme.settings.Settings;
12+
import fr.xephi.authme.settings.properties.RestrictionSettings;
13+
import org.bukkit.Bukkit;
14+
import org.bukkit.entity.Player;
15+
16+
import javax.inject.Inject;
17+
18+
@NoFieldScan
19+
public class PacketEventsService implements SettingsDependent {
20+
21+
private final ConsoleLogger logger = ConsoleLoggerFactory.get(PacketEventsService.class);
22+
23+
private boolean protectInvBeforeLogin;
24+
private boolean denyTabCompleteBeforeLogin;
25+
private boolean inventoryProtectionRegistered;
26+
private boolean tabCompleteBlockRegistered;
27+
28+
private final BukkitService bukkitService;
29+
private final PlayerCache playerCache;
30+
private final DataSource dataSource;
31+
private final PacketInterceptionAdapter packetInterceptionAdapter;
32+
33+
@Inject
34+
PacketEventsService(Settings settings, BukkitService bukkitService, PlayerCache playerCache,
35+
DataSource dataSource, PacketInterceptionAdapter packetInterceptionAdapter) {
36+
this.bukkitService = bukkitService;
37+
this.playerCache = playerCache;
38+
this.dataSource = dataSource;
39+
this.packetInterceptionAdapter = packetInterceptionAdapter;
40+
reload(settings);
41+
}
42+
43+
/**
44+
* Sets up the PacketEvents packet listeners.
45+
*/
46+
public void setup() {
47+
if (!Bukkit.getPluginManager().isPluginEnabled("packetevents")) {
48+
if (protectInvBeforeLogin) {
49+
logger.warning("WARNING! The protectInventory feature requires PacketEvents! Disabling it...");
50+
}
51+
if (denyTabCompleteBeforeLogin) {
52+
logger.warning("WARNING! The denyTabComplete feature requires PacketEvents! Disabling it...");
53+
}
54+
return;
55+
}
56+
57+
if (protectInvBeforeLogin) {
58+
if (!inventoryProtectionRegistered) {
59+
packetInterceptionAdapter.registerInventoryProtection(playerCache, dataSource);
60+
inventoryProtectionRegistered = true;
61+
// Send blank packets to online unauthenticated players (reload scenario)
62+
for (Player player : bukkitService.getOnlinePlayers()) {
63+
if (!playerCache.isAuthenticated(player.getName())
64+
&& dataSource.isAuthAvailable(player.getName())) {
65+
packetInterceptionAdapter.sendBlankInventoryPacket(player);
66+
}
67+
}
68+
}
69+
} else if (inventoryProtectionRegistered) {
70+
packetInterceptionAdapter.unregisterInventoryProtection();
71+
inventoryProtectionRegistered = false;
72+
}
73+
74+
if (denyTabCompleteBeforeLogin) {
75+
if (!tabCompleteBlockRegistered) {
76+
packetInterceptionAdapter.registerTabCompleteBlock(playerCache);
77+
tabCompleteBlockRegistered = true;
78+
}
79+
} else if (tabCompleteBlockRegistered) {
80+
packetInterceptionAdapter.unregisterTabCompleteBlock();
81+
tabCompleteBlockRegistered = false;
82+
}
83+
}
84+
85+
/**
86+
* Stops all PacketEvents-based features.
87+
*/
88+
public void disable() {
89+
if (inventoryProtectionRegistered) {
90+
packetInterceptionAdapter.unregisterInventoryProtection();
91+
inventoryProtectionRegistered = false;
92+
}
93+
if (tabCompleteBlockRegistered) {
94+
packetInterceptionAdapter.unregisterTabCompleteBlock();
95+
tabCompleteBlockRegistered = false;
96+
}
97+
}
98+
99+
/**
100+
* Sends a blank inventory packet to the given player.
101+
*
102+
* @param player the player to send the blank inventory to
103+
*/
104+
public void sendBlankInventoryPacket(Player player) {
105+
if (inventoryProtectionRegistered) {
106+
packetInterceptionAdapter.sendBlankInventoryPacket(player);
107+
}
108+
}
109+
110+
@Override
111+
public void reload(Settings settings) {
112+
boolean oldProtectInventory = this.protectInvBeforeLogin;
113+
114+
this.protectInvBeforeLogin = settings.getProperty(RestrictionSettings.PROTECT_INVENTORY_BEFORE_LOGIN);
115+
this.denyTabCompleteBeforeLogin = settings.getProperty(RestrictionSettings.DENY_TABCOMPLETE_BEFORE_LOGIN);
116+
117+
// If inventory protection was on and is now disabled, restore inventories for online players
118+
if (oldProtectInventory && !protectInvBeforeLogin && inventoryProtectionRegistered) {
119+
packetInterceptionAdapter.unregisterInventoryProtection();
120+
inventoryProtectionRegistered = false;
121+
for (Player onlinePlayer : bukkitService.getOnlinePlayers()) {
122+
if (!playerCache.isAuthenticated(onlinePlayer.getName())) {
123+
onlinePlayer.updateInventory();
124+
}
125+
}
126+
}
127+
setup();
128+
}
129+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package fr.xephi.authme.listener.packetevents;
2+
3+
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
4+
import com.github.retrooper.packetevents.event.PacketReceiveEvent;
5+
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
6+
import fr.xephi.authme.data.auth.PlayerCache;
7+
import org.bukkit.entity.Player;
8+
9+
public class TabCompletePacketListener extends PacketListenerAbstract {
10+
11+
private final PlayerCache playerCache;
12+
13+
public TabCompletePacketListener(PlayerCache playerCache) {
14+
this.playerCache = playerCache;
15+
}
16+
17+
@Override
18+
public void onPacketReceive(PacketReceiveEvent event) {
19+
if (event.getPacketType() == PacketType.Play.Client.TAB_COMPLETE) {
20+
Player player = event.getPlayer();
21+
if (player != null && !playerCache.isAuthenticated(player.getName())) {
22+
event.setCancelled(true);
23+
}
24+
}
25+
}
26+
}

0 commit comments

Comments
 (0)