Skip to content
This repository has been archived by the owner on Oct 12, 2024. It is now read-only.

Commit

Permalink
feat: add packet-level chat handling support
Browse files Browse the repository at this point in the history
  • Loading branch information
WiIIiam278 committed Mar 19, 2024
1 parent bf12d44 commit 20b5917
Show file tree
Hide file tree
Showing 11 changed files with 213 additions and 375 deletions.
1 change: 0 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ allprojects {
mavenLocal()
mavenCentral()
maven { url = 'https://repo.william278.net/velocity/' }
maven { url = 'https://repo.papermc.io/repository/maven-public/' }
maven { url = 'https://repo.codemc.io/repository/maven-releases/' }
maven { url = 'https://repo.william278.net/releases/' }
maven { url = 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ public class Settings {
@Comment("Whether to automatically check for plugin updates on startup")
private boolean checkForUpdates = true;

@Comment("Whether to handle chat packets directly for better 1.19+ support (may cause rare compatibility issues)")
private boolean usePacketListening = true;

@Comment("Placeholder settings")
private PlaceholderSettings placeholder = new PlaceholderSettings();

Expand All @@ -74,10 +77,6 @@ public static class PlaceholderSettings {
private long cacheTime = 3000;
}

// todo MOVE
// @Comment("Enabled chat filters")
// private List<ChatFilter> chatFilters = List.of();

@Comment("Message comamnd settings")
private MessageSettings messageCommand = new MessageSettings();

Expand Down
2 changes: 0 additions & 2 deletions velocity/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ plugins {

dependencies {
implementation project(path: ':common')

implementation 'org.bstats:bstats-velocity:3.0.2'
implementation 'com.github.retrooper.packetevents:velocity:2.2.0'

compileOnly "com.velocitypowered:velocity-api:${velocity_api_version}-SNAPSHOT"
compileOnly "com.velocitypowered:velocity-proxy:${velocity_api_version}-SNAPSHOT"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@
import net.william278.huskchat.getter.DataGetter;
import net.william278.huskchat.getter.DefaultDataGetter;
import net.william278.huskchat.getter.LuckPermsDataGetter;
import net.william278.huskchat.packet.PacketManager;
import net.william278.huskchat.listener.VelocityEventChatListener;
import net.william278.huskchat.listener.VelocityPacketChatListener;
import net.william278.huskchat.listener.VelocityPlayerListener;
import net.william278.huskchat.placeholders.DefaultReplacer;
import net.william278.huskchat.placeholders.PAPIProxyBridgeReplacer;
import net.william278.huskchat.placeholders.PlaceholderReplacer;
Expand Down Expand Up @@ -124,8 +126,12 @@ public void onProxyInitialization(@NotNull ProxyInitializeEvent event) {
}

// Register events
// getProxyServer().getEventManager().register(this, new VelocityListener(this));
new PacketManager(this).load(); // todo wip
getProxyServer().getEventManager().register(this, new VelocityPlayerListener(this));
if (getSettings().isUsePacketListening()) {
new VelocityPacketChatListener(this).register();
} else {
getProxyServer().getEventManager().register(this, new VelocityEventChatListener(this));
}

// Register commands & channel shortcuts
VelocityCommand.Type.registerAll(this);
Expand Down Expand Up @@ -173,13 +179,15 @@ public Optional<OnlineUser> getPlayer(@NotNull UUID uuid) {
}

@Override
public @NotNull Collection<OnlineUser> getOnlinePlayers() {
@NotNull
public Collection<OnlineUser> getOnlinePlayers() {
return getProxyServer().getAllPlayers().stream()
.map(player -> (OnlineUser) VelocityUser.adapt(player, this)).toList();
}

@Override
public @NotNull Collection<OnlineUser> getOnlinePlayersOnServer(@NotNull OnlineUser user) {
@NotNull
public Collection<OnlineUser> getOnlinePlayersOnServer(@NotNull OnlineUser user) {
return ((VelocityUser) user).getPlayer().getCurrentServer()
.map(conn -> conn.getServer().getPlayersConnected().stream()
.map(player -> (OnlineUser) VelocityUser.adapt(player, this)).toList())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package net.william278.huskchat.listener;

import com.velocitypowered.api.event.player.PlayerChatEvent;
import net.william278.huskchat.HuskChat;
import net.william278.huskchat.channel.Channel;
import net.william278.huskchat.message.ChatMessage;
import net.william278.huskchat.user.VelocityUser;
import org.jetbrains.annotations.NotNull;

import java.util.Optional;

public interface VelocityChatListener {

default boolean handlePlayerChat(PlayerChatEvent e) {
final VelocityUser player = VelocityUser.adapt(e.getPlayer(), plugin());
final Optional<Channel> channel = plugin().getChannels().getChannel(
plugin().getUserCache().getPlayerChannel(player.getUuid())
);
if (channel.isEmpty()) {
plugin().getLocales().sendMessage(player, "error_no_channel");
return false;
}

// Send the chat message, determine if the event should be canceled
return !new ChatMessage(channel.get(), player, e.getMessage(), plugin()).dispatch();
}

@NotNull
HuskChat plugin();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package net.william278.huskchat.listener;

import com.velocitypowered.api.event.PostOrder;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.player.PlayerChatEvent;
import net.william278.huskchat.HuskChat;
import org.jetbrains.annotations.NotNull;

public record VelocityEventChatListener(@NotNull HuskChat plugin) implements VelocityChatListener {

@Subscribe(order = PostOrder.LATE)
public void onPlayerChat(PlayerChatEvent e) {
if (!e.getResult().isAllowed()) {
return;
}
if (!this.handlePlayerChat(e)) {
e.setResult(PlayerChatEvent.ChatResult.denied());
}
}

@Override
@NotNull
public HuskChat plugin() {
return plugin;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package net.william278.huskchat.listener;

import com.google.common.collect.Sets;
import com.velocitypowered.api.event.AwaitingEventExecutor;
import com.velocitypowered.api.event.EventTask;
import com.velocitypowered.api.event.connection.DisconnectEvent;
import com.velocitypowered.api.event.connection.PostLoginEvent;
import com.velocitypowered.api.event.player.PlayerChatEvent;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.network.Connections;
import com.velocitypowered.proxy.protocol.packet.chat.keyed.KeyedPlayerChatPacket;
import com.velocitypowered.proxy.protocol.packet.chat.legacy.LegacyChatPacket;
import com.velocitypowered.proxy.protocol.packet.chat.session.SessionPlayerChatPacket;
import io.netty.channel.Channel;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.william278.desertwell.util.ThrowingConsumer;
import net.william278.huskchat.HuskChat;
import net.william278.huskchat.VelocityHuskChat;
import org.jetbrains.annotations.NotNull;

import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

public class VelocityPacketChatListener {
private static final String KEY = "huskchat";

private final VelocityHuskChat plugin;
@Getter
private final Set<UUID> huskChatEntries;

public VelocityPacketChatListener(@NotNull VelocityHuskChat plugin) {
this.plugin = plugin;
this.huskChatEntries = Sets.newConcurrentHashSet();
}

public void register() {
this.loadPlayers();
this.loadListeners();
}

private void loadPlayers() {
plugin.getServer().getAllPlayers().forEach(this::injectPlayer);
}

private void loadListeners() {
plugin.getServer().getEventManager().register(plugin, PostLoginEvent.class,
(AwaitingEventExecutor<PostLoginEvent>) postLoginEvent -> EventTask.withContinuation(continuation -> {
injectPlayer(postLoginEvent.getPlayer());
continuation.resume();
}));

plugin.getServer().getEventManager().register(plugin, DisconnectEvent.class,
(AwaitingEventExecutor<DisconnectEvent>) disconnectEvent ->
disconnectEvent.getLoginStatus() == DisconnectEvent.LoginStatus.CONFLICTING_LOGIN
? null
: EventTask.async(() -> removePlayer(disconnectEvent.getPlayer())));
}

public void injectPlayer(@NotNull Player player) {
final PlayerChannelHandler handler = new PlayerChannelHandler(plugin, player);
final ConnectedPlayer connectedPlayer = (ConnectedPlayer) player;
removePlayer(player);
connectedPlayer.getConnection()
.getChannel()
.pipeline()
.addBefore(Connections.HANDLER, KEY, handler);
}

public void removePlayer(@NotNull Player player) {
final ConnectedPlayer connectedPlayer = (ConnectedPlayer) player;
final Channel channel = connectedPlayer.getConnection().getChannel();
if (channel.pipeline().get(KEY) != null) {
channel.pipeline().remove(KEY);
}
}

@RequiredArgsConstructor
public static class PlayerChannelHandler extends ChannelDuplexHandler implements VelocityChatListener {

private final VelocityHuskChat plugin;
private final Player player;

@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
final Optional<String> message = this.extractChatMessage(msg);
if (message.isEmpty()) {
super.write(ctx, msg, promise);
return;
}
this.handleChat(message.get(), (passthrough) -> super.write(ctx, msg, promise));
}

@NotNull
private Optional<String> extractChatMessage(Object msg) {
if (msg instanceof final SessionPlayerChatPacket session) {
// Handle session chat (1.19.4+)
return Optional.of(session.getMessage());
} else if (msg instanceof final KeyedPlayerChatPacket keyed) {
// Handle keyed chat (1.19.2-4)
return Optional.of(keyed.getMessage());
} else if (msg instanceof final LegacyChatPacket legacy) {
// Handle legacy chat (pre-1.19.1)
return Optional.of(legacy.getMessage());
}
return Optional.empty();
}

private void handleChat(@NotNull String message, @NotNull ThrowingConsumer<Void> ifAllowed) {
this.dispatchEvent(message)
.thenApply(event -> event.getResult().isAllowed() && handlePlayerChat(event))
.thenAccept(allowed -> {
if (allowed) {
ifAllowed.accept(null);
}
});
}

@NotNull
private CompletableFuture<PlayerChatEvent> dispatchEvent(@NotNull String message) {
return plugin.getServer().getEventManager().fire(new PlayerChatEvent(player, message));
}

@Override
@NotNull
public HuskChat plugin() {
return plugin;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,48 +19,20 @@

package net.william278.huskchat.listener;

import com.velocitypowered.api.event.PostOrder;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.connection.DisconnectEvent;
import com.velocitypowered.api.event.connection.PostLoginEvent;
import com.velocitypowered.api.event.player.PlayerChatEvent;
import com.velocitypowered.api.event.player.ServerConnectedEvent;
import net.william278.huskchat.HuskChat;
import net.william278.huskchat.channel.Channel;
import net.william278.huskchat.message.ChatMessage;
import net.william278.huskchat.user.VelocityUser;
import org.jetbrains.annotations.NotNull;

import java.util.Optional;
public class VelocityPlayerListener extends PlayerListener {

public class VelocityListener extends PlayerListener {

public VelocityListener(@NotNull HuskChat plugin) {
public VelocityPlayerListener(@NotNull HuskChat plugin) {
super(plugin);
}

@Subscribe(order = PostOrder.LATE)
public void onPlayerChat(PlayerChatEvent e) {
if (!e.getResult().isAllowed()) {
return;
}

// Verify they are in a channel
final VelocityUser player = VelocityUser.adapt(e.getPlayer(), plugin);
final Optional<Channel> channel = plugin.getChannels().getChannel(
plugin.getUserCache().getPlayerChannel(player.getUuid())
);
if (channel.isEmpty()) {
plugin.getLocales().sendMessage(player, "error_no_channel");
return;
}

// Send the chat message, determine if the event should be canceled
if (new ChatMessage(channel.get(), player, e.getMessage(), plugin).dispatch()) {
e.setResult(PlayerChatEvent.ChatResult.denied());
}
}

@Subscribe
public void onPlayerChangeServer(ServerConnectedEvent e) {
final String server = e.getServer().getServerInfo().getName();
Expand Down

This file was deleted.

Loading

0 comments on commit 20b5917

Please sign in to comment.