forked from GrimAnticheat/Grim
-
Notifications
You must be signed in to change notification settings - Fork 51
Expand file tree
/
Copy pathPacketPlayerJoinQuit.java
More file actions
152 lines (133 loc) · 8.23 KB
/
Copy pathPacketPlayerJoinQuit.java
File metadata and controls
152 lines (133 loc) · 8.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package ac.grim.grimac.events.packets;
import ac.grim.grimac.GrimAPI;
import ac.grim.grimac.manager.datastore.PlayerToggleStore;
import ac.grim.grimac.platform.api.player.PlatformPlayer;
import ac.grim.grimac.utils.anticheat.LogUtil;
import com.github.retrooper.packetevents.event.*;
import com.github.retrooper.packetevents.netty.channel.ChannelHelper;
import com.github.retrooper.packetevents.protocol.ConnectionState;
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.UUID;
import java.util.function.BiConsumer;
public class PacketPlayerJoinQuit extends PacketListenerAbstract {
@Override
public void onPacketSend(PacketSendEvent event) {
if (event.getPacketType() == PacketType.Login.Server.LOGIN_SUCCESS) {
// Do this after send to avoid sending packets before the PLAY state
event.getTasksAfterSend().add(() -> {
GrimAPI.INSTANCE.getPlayerDataManager().addUser(event.getUser());
// Prefetch the player's persisted alerts/verbose/brands toggles so onUserLogin can apply them immediately instead of falling back to the permission-default when the read hasn't completed in time.
//
// Runs on the backend's reader executor: never blocks this Netty thread. By the time onUserLogin fires (after chunk load / resource pack / motd) the prefetch has typically settled, and onUserLogin reads from the in-memory cache. A slow DB or a brand-new player with no row yet falls through to the permission-default.
UUID uuid = event.getUser().getUUID();
if (uuid != null) {
GrimAPI.INSTANCE.getDataStoreLifecycle().playerToggleStore().prefetch(uuid);
}
});
}
}
@Override
public void onUserConnect(UserConnectEvent event) {
// Player connected too soon, perhaps late bind is off
// Don't kick everyone on reload
if (event.getUser().getConnectionState() == ConnectionState.PLAY && !GrimAPI.INSTANCE.getPlayerDataManager().exemptUsers.contains(event.getUser())) {
event.setCancelled(true);
}
}
@Override
public void onUserLogin(UserLoginEvent event) {
// fake channel (NPC / spoofer / EmbeddedChannel) — no PacketUser, nothing to track
if (event.getUser() == null) return;
// 26.X fallback: ensure GrimPlayer exists at login time. On the
// fabric-official chain, addUser via onPacketSend(LOGIN_SUCCESS) may
// not fire reliably due to PE pipeline timing.
if (GrimAPI.INSTANCE.getPlayerDataManager().getPlayer(event.getUser()) == null) {
GrimAPI.INSTANCE.getPlayerDataManager().addUser(event.getUser());
}
Object nativePlayerObject = Objects.requireNonNull(event.getPlayer());
// This will never throw a NPE because code is run in OnUserConnect -> onPacketSend -> OnUserLogin order
// And the user will be added to the map before the getPlayer() method call
@NotNull PlatformPlayer platformPlayer = GrimAPI.INSTANCE.getPlatformPlayerFactory().getFromNativePlayerType(nativePlayerObject);
if (GrimAPI.INSTANCE.getConfigManager().getConfig().getBooleanElse("debug-pipeline-on-join", false)) {
LogUtil.info("Pipeline: " + ChannelHelper.pipelineHandlerNamesAsString(event.getUser().getChannel()));
}
PlayerToggleStore toggles = GrimAPI.INSTANCE.getDataStoreLifecycle().playerToggleStore();
applyToggle(platformPlayer, toggles, PlayerToggleStore.KEY_ALERTS,
"grim.alerts", "grim.alerts.enable-on-join", "grim.alerts.enable-on-join.silent",
(p, silent) -> GrimAPI.INSTANCE.getAlertManager().toggleAlerts(p, silent),
(p, value) -> GrimAPI.INSTANCE.getAlertManager().setAlertsEnabled(p, value, true));
applyToggle(platformPlayer, toggles, PlayerToggleStore.KEY_VERBOSE,
"grim.verbose", "grim.verbose.enable-on-join", "grim.verbose.enable-on-join.silent",
(p, silent) -> GrimAPI.INSTANCE.getAlertManager().toggleVerbose(p, silent),
(p, value) -> GrimAPI.INSTANCE.getAlertManager().setVerboseEnabled(p, value, true));
applyToggle(platformPlayer, toggles, PlayerToggleStore.KEY_BRANDS,
"grim.brand", "grim.brand.enable-on-join", "grim.brand.enable-on-join.silent",
(p, silent) -> GrimAPI.INSTANCE.getAlertManager().toggleBrands(p, silent),
(p, value) -> GrimAPI.INSTANCE.getAlertManager().setBrandsEnabled(p, value, true));
if (platformPlayer.hasPermission("grim.spectate") && GrimAPI.INSTANCE.getConfigManager().getConfig().getBooleanElse("spectators.hide-regardless", false)) {
GrimAPI.INSTANCE.getSpectateManager().onLogin(platformPlayer.getUniqueId());
}
GrimAPI.INSTANCE.getDataStoreLifecycle().liveWriteHooks()
.onJoinFromUserLogin(platformPlayer, event.getUser(), System.currentTimeMillis());
}
/**
* Apply one of the three persistable toggles at login time, honouring the
* source-of-truth precedence:
* <pre>
* persisted state (from prefetch) > permission-default > unset.
* </pre>
* If the toggle store is unavailable (datastore disabled / failed init),
* fall through to the legacy permission-only behaviour so the plugin
* keeps working without the v1 datastore.
*
* @param permTogglePath permission required to USE the toggle at all
* @param permEnableOnJoin permission whose default is "on at login"
* @param permSilentJoin permission that suppresses the toggle message
* @param toggle legacy fallback path (with on/off flip behaviour)
* @param applySilent applies a known boolean value silently — used
* when persisted state dictates a specific value
*/
private static void applyToggle(@NotNull PlatformPlayer platformPlayer,
@NotNull PlayerToggleStore toggles,
@NotNull String key,
@NotNull String permTogglePath,
@NotNull String permEnableOnJoin,
@NotNull String permSilentJoin,
@NotNull BiConsumer<PlatformPlayer, Boolean> toggle,
@NotNull BiConsumer<PlatformPlayer, Boolean> applySilent) {
if (!platformPlayer.hasPermission(permTogglePath)) return;
UUID uuid = platformPlayer.getUniqueId();
Boolean persisted = toggles.current(uuid, key);
if (persisted != null) {
// Prefetch already settled — apply silently so reconnects don't
// print the toggle message every time. enable-on-join.silent is
// ignored on this path; persisted state already represents the
// staff member's last explicit choice.
applySilent.accept(platformPlayer, persisted);
return;
}
// No persisted state visible yet (no row for this player, OR the
// prefetch hasn't completed). Fall back to the permission-default
// policy and persist the choice so it sticks. NOOP store treats both
// applyPermissionDefault calls as silent no-ops.
boolean enableOnJoin = platformPlayer.hasPermission(permEnableOnJoin);
boolean silent = platformPlayer.hasPermission(permSilentJoin);
if (enableOnJoin) {
toggle.accept(platformPlayer, silent);
toggles.applyPermissionDefault(uuid, key, true);
} else {
// Persist the negative decision too — without this, an admin who
// grants enable-on-join later would silently flip the toggle on
// for staff who'd already implicitly chosen "off" on prior joins.
toggles.applyPermissionDefault(uuid, key, false);
}
}
@Override
public void onUserDisconnect(UserDisconnectEvent event) {
// Session close + toggle eviction now live in PlayerDataManager.onDisconnect
// so the ClientVersionSetter defensive path also closes sessions.
GrimAPI.INSTANCE.getPlayerDataManager().onDisconnect(event.getUser());
}
}