14 Commits
Author SHA1 Message Date
stijn 2962a605ea Fix typo in CREATE TABLE query within ChatLogQueries to ensure table creation works correctly. 2026-07-19 23:12:36 +02:00
stijn 7492dfa418 Integrate ChatLogHandler across chat modules and add centralized message logging with validation through ChatLogMapper. 2026-07-19 23:00:40 +02:00
stijn 92979e9789 Refactor ChatListener to improve readability and formatting consistency across message handling and ignored players logic. 2026-07-19 01:03:07 +02:00
stijn af6820d1bf Remove custom OffsetDateTime mappings in OpenAPI config and ensure timestamps are converted to UTC in ChatLog. 2026-07-18 21:04:54 +02:00
stijn 8546bda28a Replace hardcoded web server URL with configurable value from Config. 2026-07-18 20:51:32 +02:00
stijn 28894ef245 Improve string formatting consistency in Config.java and add webServerSettings method for web server configuration 2026-07-18 20:51:04 +02:00
stijn 5a508fdf28 Add web-api module with OpenAPI integration, enable chat logs forwarding to web backend, and update dependencies across modules. 2026-07-18 20:21:25 +02:00
stijn dc513ac2f0 Add support for miniMessage serialization in chat logs 2026-07-18 19:11:53 +02:00
stijn c48d4f5c86 Update InventoryGUI to version 1.1.5-SNAPSHOT and refine Nicknames GUI handling logic. 2026-02-02 03:07:45 +01:00
stijn 7f00bc104b Update InventoryGUI to version 1.1.3-SNAPSHOT in build.gradle.kts. 2026-02-01 03:48:31 +01:00
stijn 133cebd1ec Rerender gui when needed 2026-02-01 03:36:06 +01:00
stijn fc20c9a276 Update velocity-api to 3.5.0-SNAPSHOT, adjust event priority annotations, and reconfigure repository URLs in settings.gradle.kts. 2026-01-31 23:45:19 +01:00
stijn ea8e952108 Refactor Nicknames GUI to handle nicknames even if the player is unknown and use InventoryGui library, improving modularity and readability. Update velocity-api version and add InventoryGUI dependency. 2026-01-31 23:25:11 +01:00
stijn 3d010e4139 extract methods for shorter functions and use early returns 2026-01-31 18:52:14 +01:00
27 changed files with 1244 additions and 561 deletions
+7
View File
@@ -3,12 +3,19 @@ plugins {
} }
dependencies { dependencies {
implementation(project(":web-api")) // Web-API
compileOnly("org.projectlombok:lombok:1.18.46")
annotationProcessor("org.projectlombok:lombok:1.18.46")
// Cosmos // Cosmos
compileOnly("com.alttd.cosmos:cosmos-api:1.21.8-R0.1-SNAPSHOT") { compileOnly("com.alttd.cosmos:cosmos-api:1.21.8-R0.1-SNAPSHOT") {
isChanging = true isChanging = true
} }
compileOnly("org.spongepowered:configurate-yaml:4.2.0") // Configurate compileOnly("org.spongepowered:configurate-yaml:4.2.0") // Configurate
compileOnly("net.luckperms:api:5.5") // Luckperms compileOnly("net.luckperms:api:5.5") // Luckperms
//API validation
implementation("org.hibernate.validator:hibernate-validator:9.0.1.Final")
implementation("org.glassfish:jakarta.el:5.0.0-M1")
} }
publishing { publishing {
@@ -183,11 +183,15 @@ public final class Config {
private static void settings() { private static void settings() {
PREFIXGROUPS = getList("settings.prefix-groups", PREFIXGROUPS = getList("settings.prefix-groups",
Lists.newArrayList("discord", "socialmedia", "eventteam", "eventleader", "youtube", "twitch", Lists.newArrayList("discord", "socialmedia", "eventteam", "eventleader", "youtube", "twitch",
"developer")); "developer"
)
);
CONFLICTINGPREFIXGROUPS = getList("settings.prefix-conflicts-groups", CONFLICTINGPREFIXGROUPS = getList("settings.prefix-conflicts-groups",
Lists.newArrayList("eventteam", "eventleader")); Lists.newArrayList("eventteam", "eventleader")
);
STAFFGROUPS = getList("settings.staff-groups", STAFFGROUPS = getList("settings.staff-groups",
Lists.newArrayList("trainee", "moderator", "headmod", "admin", "manager", "owner")); Lists.newArrayList("trainee", "moderator", "headmod", "admin", "manager", "owner")
);
CONSOLENAME = getString("settings.console-name", CONSOLENAME); CONSOLENAME = getString("settings.console-name", CONSOLENAME);
CONSOLEUUID = UUID.fromString(getString("settings.console-uuid", CONSOLEUUID.toString())); CONSOLEUUID = UUID.fromString(getString("settings.console-uuid", CONSOLEUUID.toString()));
MINIMIUMSTAFFRANK = getString("settings.minimum-staff-rank", MINIMIUMSTAFFRANK); MINIMIUMSTAFFRANK = getString("settings.minimum-staff-rank", MINIMIUMSTAFFRANK);
@@ -413,7 +417,8 @@ public final class Config {
ConfigurationNode node = getNode("chat-channels"); ConfigurationNode node = getNode("chat-channels");
if (node.empty()) { if (node.empty()) {
getString("chat-channels.ac.format", getString("chat-channels.ac.format",
"<white><gray><sender></gray> <hover:show_text:on <server>><yellow>to <channel></yellow></hover><gray>: <message>"); "<white><gray><sender></gray> <hover:show_text:on <server>><yellow>to <channel></yellow></hover><gray>: <message>"
);
getList("chat-channels.ac.servers", List.of("lobby")); getList("chat-channels.ac.servers", List.of("lobby"));
getBoolean("chat-channels.ac.proxy", false); getBoolean("chat-channels.ac.proxy", false);
node = getNode("chat-channels"); node = getNode("chat-channels");
@@ -617,11 +622,15 @@ public final class Config {
NICK_WAIT_TIME = getLong("nicknames.wait-time", NICK_WAIT_TIME); NICK_WAIT_TIME = getLong("nicknames.wait-time", NICK_WAIT_TIME);
NICK_ITEM_LORE = getList("nicknames.item-lore", NICK_ITEM_LORE = getList("nicknames.item-lore",
List.of("<aqua>New nick: <newnick>", "<aqua>Old nick: <oldnick>", "<aqua>Last changed: <lastchanged>", List.of("<aqua>New nick: <newnick>", "<aqua>Old nick: <oldnick>", "<aqua>Last changed: <lastchanged>",
"<green>Left click to Accept <light_purple>| <red>Right click to Deny")); "<green>Left click to Accept <light_purple>| <red>Right click to Deny"
)
);
NICK_BLOCKED_COLOR_CODESLIST = getList("nicknames.blocked-color-codes", List.of("&k", "&l", "&n", "&m", "&o")); NICK_BLOCKED_COLOR_CODESLIST = getList("nicknames.blocked-color-codes", List.of("&k", "&l", "&n", "&m", "&o"));
NICK_ALLOWED_COLOR_CODESLIST = getList("nicknames.allowed-color-codes", NICK_ALLOWED_COLOR_CODESLIST = getList("nicknames.allowed-color-codes",
List.of("&0", "&1", "&2", "&3", "&4", "&5", "&6", "&7", "&8", "&9", "&a", "&b", "&c", "&d", "&e", "&f", List.of("&0", "&1", "&2", "&3", "&4", "&5", "&6", "&7", "&8", "&9", "&a", "&b", "&c", "&d", "&e", "&f",
"&r")); "&r"
)
);
NICK_CURRENT = getString("nicknames.messages.nick-current", NICK_CURRENT); NICK_CURRENT = getString("nicknames.messages.nick-current", NICK_CURRENT);
} }
@@ -641,4 +650,10 @@ public final class Config {
CHAT_LOG_DELETE_OLDER_THAN_DAYS = getLong("chat-log.delete-older-than-days", CHAT_LOG_DELETE_OLDER_THAN_DAYS); CHAT_LOG_DELETE_OLDER_THAN_DAYS = getLong("chat-log.delete-older-than-days", CHAT_LOG_DELETE_OLDER_THAN_DAYS);
CHAT_LOG_SAVE_DELAY_MINUTES = getLong("chat-log.save-delay-minutes", CHAT_LOG_SAVE_DELAY_MINUTES); CHAT_LOG_SAVE_DELAY_MINUTES = getLong("chat-log.save-delay-minutes", CHAT_LOG_SAVE_DELAY_MINUTES);
} }
public static String CHAT_WEB_SERVER_BASE_URL = "http://10.0.0.121:8080";
private static void webServerSettings() {
CHAT_WEB_SERVER_BASE_URL = getString("web-server.base-url", CHAT_WEB_SERVER_BASE_URL);
}
} }
@@ -15,15 +15,25 @@ import java.util.concurrent.CompletionException;
public class ChatLogQueries { public class ChatLogQueries {
protected static void createChatLogTable() { protected static void createChatLogTable() {
String nicknamesTableQuery = "CREATE TABLE IF NOT EXISTS chat_log(" String nicknamesTableQuery = """
+ "uuid CHAR(48) NOT NULL," CREATE TABLE IF NOT EXISTS chat_log (
+ "time_stamp TIMESTAMP(6) NOT NULL, " id BIGINT AUTO_INCREMENT PRIMARY KEY,
+ "server VARCHAR(50) NOT NULL, " uuid CHAR(36) NOT NULL,
+ "chat_message VARCHAR(300) NOT NULL, " time_stamp TIMESTAMP(6) NOT NULL,
+ "blocked BIT(1) NOT NULL DEFAULT 0" server VARCHAR(50) NOT NULL,
+ ")"; type VARCHAR(16) NOT NULL DEFAULT 'public',
channel VARCHAR(36) DEFAULT NULL,
receiver CHAR(36) DEFAULT NULL,
chat_message VARCHAR(300) NOT NULL,
mini_message JSON,
blocked BOOLEAN NOT NULL DEFAULT FALSE,
try (PreparedStatement preparedStatement = DatabaseConnection.getConnection().prepareStatement(nicknamesTableQuery)) { INDEX idx_time_stamp (time_stamp)
)
""";
try (PreparedStatement preparedStatement = DatabaseConnection.getConnection().prepareStatement(
nicknamesTableQuery)) {
preparedStatement.executeUpdate(); preparedStatement.executeUpdate();
} catch (Throwable throwable) { } catch (Throwable throwable) {
ALogger.error("Failed to create chat log table", throwable); ALogger.error("Failed to create chat log table", throwable);
@@ -31,7 +41,7 @@ public class ChatLogQueries {
} }
public static @NotNull CompletableFuture<Boolean> storeMessages(HashMap<UUID, List<ChatLog>> chatMessages) { public static @NotNull CompletableFuture<Boolean> storeMessages(HashMap<UUID, List<ChatLog>> chatMessages) {
String insertQuery = "INSERT INTO chat_log (uuid, time_stamp, server, chat_message, blocked) VALUES (?, ?, ?, ?, ?)"; String insertQuery = "INSERT INTO chat_log (uuid, time_stamp, server, type, channel, receiver, chat_message, mini_message, blocked) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
return CompletableFuture.supplyAsync(() -> { return CompletableFuture.supplyAsync(() -> {
try (Connection connection = DatabaseConnection.createTransactionConnection()) { try (Connection connection = DatabaseConnection.createTransactionConnection()) {
PreparedStatement preparedStatement = connection.prepareStatement(insertQuery); PreparedStatement preparedStatement = connection.prepareStatement(insertQuery);
@@ -60,7 +70,7 @@ public class ChatLogQueries {
} }
public static @NotNull CompletableFuture<List<ChatLog>> retrieveMessages(ChatLogHandler chatLogHandler, UUID uuid, Duration duration, String server) { public static @NotNull CompletableFuture<List<ChatLog>> retrieveMessages(ChatLogHandler chatLogHandler, UUID uuid, Duration duration, String server) {
String query = "SELECT * FROM chat_log WHERE uuid = ? AND time_stamp > ? AND server = ?"; String query = "SELECT * FROM chat_log WHERE uuid = ? AND time_stamp > ? AND server = ? AND type = 'public'";
return CompletableFuture.supplyAsync(() -> { return CompletableFuture.supplyAsync(() -> {
try (Connection connection = DatabaseConnection.getConnection()) { try (Connection connection = DatabaseConnection.getConnection()) {
PreparedStatement preparedStatement = connection.prepareStatement(query); PreparedStatement preparedStatement = connection.prepareStatement(query);
@@ -70,13 +80,14 @@ public class ChatLogQueries {
ResultSet resultSet = preparedStatement.executeQuery(); ResultSet resultSet = preparedStatement.executeQuery();
List<ChatLog> chatLogs = new ArrayList<>(); List<ChatLog> chatLogs = new ArrayList<>();
while (resultSet.next()) { while (resultSet.next()) {
ChatLog chatLog = chatLogHandler.loadFromResultSet(resultSet); chatLogHandler.loadFromResultSet(resultSet).ifPresent(chatLogs::add);
chatLogs.add(chatLog);
} }
return chatLogs; return chatLogs;
} catch (SQLException sqlException) { } catch (SQLException sqlException) {
ALogger.error(String.format("Failed to retrieve messages for user %s", uuid), sqlException); ALogger.error(String.format("Failed to retrieve messages for user %s", uuid), sqlException);
throw new CompletionException(String.format("Failed to retrieve messages for user %s", uuid), sqlException); throw new CompletionException(String.format("Failed to retrieve messages for user %s", uuid),
sqlException
);
} }
}); });
} }
@@ -90,8 +101,13 @@ public class ChatLogQueries {
preparedStatement.setTimestamp(1, Timestamp.from(Instant.now().minus(duration))); preparedStatement.setTimestamp(1, Timestamp.from(Instant.now().minus(duration)));
return preparedStatement.execute(); return preparedStatement.execute();
} catch (SQLException sqlException) { } catch (SQLException sqlException) {
ALogger.error(String.format("Failed to delete messages older than %s days", duration.toDays()), sqlException); ALogger.error(String.format("Failed to delete messages older than %s days", duration.toDays()),
throw new CompletionException(String.format("Failed to delete messages older than %s days", duration.toDays()), sqlException); sqlException
);
throw new CompletionException(String.format("Failed to delete messages older than %s days",
duration.toDays()
), sqlException
);
} }
}); });
} }
@@ -1,6 +1,5 @@
package com.alttd.chat.database; package com.alttd.chat.database;
import com.alttd.chat.config.Config; import com.alttd.chat.config.Config;
import java.sql.Connection; import java.sql.Connection;
@@ -27,6 +26,7 @@ public class DatabaseConnection {
/** /**
* Opens the connection if it's not already open. * Opens the connection if it's not already open.
*
* @throws SQLException If it can't create the connection. * @throws SQLException If it can't create the connection.
*/ */
public void openConnection() throws SQLException { public void openConnection() throws SQLException {
@@ -46,13 +46,15 @@ public class DatabaseConnection {
connection = DriverManager.getConnection( connection = DriverManager.getConnection(
"jdbc:mysql://" + Config.IP + ":" + Config.PORT + "/" + Config.DATABASE + "?autoReconnect=true" + "jdbc:mysql://" + Config.IP + ":" + Config.PORT + "/" + Config.DATABASE + "?autoReconnect=true" +
"&useSSL=false", "&useSSL=false&preserveInstants=true",
Config.USERNAME, Config.PASSWORD); Config.USERNAME, Config.PASSWORD
);
} }
} }
/** /**
* Returns the connection for the database * Returns the connection for the database
*
* @return Returns the connection. * @return Returns the connection.
*/ */
public static Connection getConnection() { public static Connection getConnection() {
@@ -69,13 +71,15 @@ public class DatabaseConnection {
* Creates a transactional database connection. * Creates a transactional database connection.
* *
* @return A {@code Connection} object representing the transactional database connection. * @return A {@code Connection} object representing the transactional database connection.
*
* @throws SQLException If there is an error creating the database connection. * @throws SQLException If there is an error creating the database connection.
*/ */
public static Connection createTransactionConnection() throws SQLException { public static Connection createTransactionConnection() throws SQLException {
connection = DriverManager.getConnection( connection = DriverManager.getConnection(
"jdbc:mysql://" + Config.IP + ":" + Config.PORT + "/" + Config.DATABASE + "?autoReconnect=true" + "jdbc:mysql://" + Config.IP + ":" + Config.PORT + "/" + Config.DATABASE + "?autoReconnect=true" +
"&useSSL=false", "&useSSL=false&preserveInstants=true",
Config.USERNAME, Config.PASSWORD); Config.USERNAME, Config.PASSWORD
);
connection.setAutoCommit(false); connection.setAutoCommit(false);
return connection; return connection;
} }
@@ -18,9 +18,10 @@ public abstract class Toggleable {
public static Toggleable getToggleable(UUID uuid) { public static Toggleable getToggleable(UUID uuid) {
for (Toggleable toggleableClass : togglableClasses) { for (Toggleable toggleableClass : togglableClasses) {
if (toggleableClass.isToggled(uuid)) if (toggleableClass.isToggled(uuid)) {
return toggleableClass; return toggleableClass;
} }
}
return null; return null;
} }
@@ -1,6 +1,10 @@
package com.alttd.chat.objects.chat_log; package com.alttd.chat.objects.chat_log;
import com.alttd.chat.objects.BatchInsertable; import com.alttd.chat.objects.BatchInsertable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
@@ -9,44 +13,32 @@ import java.sql.Timestamp;
import java.time.Instant; import java.time.Instant;
import java.util.UUID; import java.util.UUID;
@AllArgsConstructor
@Getter
public class ChatLog implements BatchInsertable { public class ChatLog implements BatchInsertable {
private final UUID uuid; private final UUID uuid;
private final Instant timestamp; private final Instant timestamp;
private final String server; private final String server;
private final ChatLogType type;
private final String channel;
private final String receiver;
private final String message; private final String message;
private final Component miniMessage;
private final boolean blocked; private final boolean blocked;
protected ChatLog(UUID uuid, Instant timestamp, String server, String message, boolean blocked) {
this.uuid = uuid;
this.timestamp = timestamp;
this.server = server;
this.message = message;
this.blocked = blocked;
}
@Override @Override
public void prepareStatement(@NotNull PreparedStatement preparedStatement) throws SQLException { public void prepareStatement(@NotNull PreparedStatement preparedStatement) throws SQLException {
preparedStatement.setString(1, uuid.toString()); preparedStatement.setString(1, uuid.toString());
preparedStatement.setTimestamp(2, Timestamp.from(timestamp)); preparedStatement.setTimestamp(2, Timestamp.from(timestamp));
preparedStatement.setString(3, server); preparedStatement.setString(3, server);
preparedStatement.setString(4, message); preparedStatement.setString(4, ChatLogTypeMapper.toDb(type));
preparedStatement.setInt(5, blocked ? 1 : 0); preparedStatement.setString(5, channel);
} preparedStatement.setString(6, receiver);
preparedStatement.setString(7, message);
public UUID getUuid() { preparedStatement.setString(8,
return uuid; miniMessage == null ? null : GsonComponentSerializer.gson().serialize(miniMessage)
} );
preparedStatement.setInt(9, blocked ? 1 : 0);
public Instant getTimestamp() {
return timestamp;
}
public String getMessage() {
return message;
}
public boolean isBlocked() {
return blocked;
} }
} }
@@ -3,6 +3,9 @@ package com.alttd.chat.objects.chat_log;
import com.alttd.chat.config.Config; import com.alttd.chat.config.Config;
import com.alttd.chat.database.ChatLogQueries; import com.alttd.chat.database.ChatLogQueries;
import com.alttd.chat.util.ALogger; import com.alttd.chat.util.ALogger;
import lombok.extern.slf4j.Slf4j;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.sql.ResultSet; import java.sql.ResultSet;
@@ -12,14 +15,17 @@ import java.time.Instant;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
@Slf4j
public class ChatLogHandler { public class ChatLogHandler {
private final ChatLogWebHandler chatLogWebHandler = new ChatLogWebHandler();
private static ChatLogHandler instance = null; private static ChatLogHandler instance = null;
private ScheduledExecutorService executorService = null; private ScheduledExecutorService executorService = null;
public static ChatLogHandler getInstance(boolean enableLogging) { public static ChatLogHandler getInstance(boolean enableLogging) {
if (instance == null) if (instance == null) {
instance = new ChatLogHandler(enableLogging); instance = new ChatLogHandler(enableLogging);
}
return instance; return instance;
} }
@@ -35,23 +41,30 @@ public class ChatLogHandler {
Duration deleteThreshold = Duration.ofDays(Config.CHAT_LOG_DELETE_OLDER_THAN_DAYS); Duration deleteThreshold = Duration.ofDays(Config.CHAT_LOG_DELETE_OLDER_THAN_DAYS);
ChatLogQueries.deleteOldMessages(deleteThreshold).thenAccept(success -> { ChatLogQueries.deleteOldMessages(deleteThreshold).thenAccept(success -> {
if (success) { if (success) {
ALogger.info(String.format("Deleted all messages older than %s days from chat log database.", deleteThreshold.toDays())); ALogger.info(String.format("Deleted all messages older than %s days from chat log database.",
deleteThreshold.toDays()
));
} else { } else {
ALogger.warn(String.format("Failed to delete all messages older than %s days from chat log database.", deleteThreshold.toDays())); ALogger.warn(String.format("Failed to delete all messages older than %s days from chat log database.",
deleteThreshold.toDays()
));
} }
}); });
executorService = Executors.newSingleThreadScheduledExecutor(); executorService = Executors.newSingleThreadScheduledExecutor();
executorService.scheduleAtFixedRate(() -> { executorService.scheduleAtFixedRate(() -> {
saveToDatabase(false); saveToDatabase(false);
ALogger.info(String.format("Running scheduler to save messages with a %d delay", Config.CHAT_LOG_SAVE_DELAY_MINUTES)); ALogger.info(String.format("Running scheduler to save messages with a %d delay",
Config.CHAT_LOG_SAVE_DELAY_MINUTES
));
}, },
Config.CHAT_LOG_SAVE_DELAY_MINUTES, Config.CHAT_LOG_SAVE_DELAY_MINUTES, TimeUnit.MINUTES); Config.CHAT_LOG_SAVE_DELAY_MINUTES, Config.CHAT_LOG_SAVE_DELAY_MINUTES, TimeUnit.MINUTES
);
ALogger.info("Logging has started!"); ALogger.info("Logging has started!");
} }
/** /**
* Shuts down the executor service and saves the chat logs to the database. * Shuts down the executor service and saves the chat logs to the database. Will throw an error if called on a
* Will throw an error if called on a ChatLogHandler that was started without logging * ChatLogHandler that was started without logging
*/ */
public void shutDown() { public void shutDown() {
executorService.shutdown(); executorService.shutdown();
@@ -66,7 +79,7 @@ public class ChatLogHandler {
return isSaving; return isSaving;
} }
public synchronized void addLog(ChatLog chatLog) { private synchronized void addLog(ChatLog chatLog) {
if (isBlocked()) { if (isBlocked()) {
chatLogQueue.add(chatLog); chatLogQueue.add(chatLog);
} else { } else {
@@ -100,17 +113,50 @@ public class ChatLogHandler {
}); });
} }
public ChatLog loadFromResultSet(@NotNull ResultSet resultSet) throws SQLException { public Optional<ChatLog> loadFromResultSet(@NotNull ResultSet resultSet) throws SQLException {
UUID chatLogUUID = UUID.fromString(resultSet.getString("uuid")); UUID chatLogUUID = UUID.fromString(resultSet.getString("uuid"));
Instant chatTimestamp = resultSet.getTimestamp("time_stamp").toInstant(); Instant chatTimestamp = resultSet.getTimestamp("time_stamp").toInstant();
String server = resultSet.getString("server"); String server = resultSet.getString("server");
String stringType = resultSet.getString("type");
ChatLogType type;
try {
type = ChatLogTypeMapper.fromDb(stringType);
} catch (Exception e) {
log.error("Failed to load chat log from result set: {}", e.getMessage());
return Optional.empty();
}
String channel = resultSet.getString("channel");
String receiver = resultSet.getString("receiver");
String chatMessage = resultSet.getString("chat_message"); String chatMessage = resultSet.getString("chat_message");
String stringMiniMessage = resultSet.getString("mini_message");
Component miniMessage = stringMiniMessage == null ? null : GsonComponentSerializer.gson().deserialize(
stringMiniMessage);
boolean chatMessageBlocked = resultSet.getInt("blocked") == 1; boolean chatMessageBlocked = resultSet.getInt("blocked") == 1;
return new ChatLog(chatLogUUID, chatTimestamp, server, chatMessage, chatMessageBlocked); return Optional.of(new ChatLog(chatLogUUID,
chatTimestamp,
server,
type,
channel,
receiver,
chatMessage,
miniMessage,
chatMessageBlocked
));
} }
public void addChatLog(UUID uuid, String server, String message, boolean blocked) { public void addChatLog(UUID uuid, String server, String message, ChatLogType chatLogType, String channel, String receiver, Component miniMessage, boolean blocked) {
addLog(new ChatLog(uuid, Instant.now(), server, message, blocked)); ChatLog chatLog = new ChatLog(uuid,
Instant.now(),
server,
chatLogType,
channel,
receiver,
message,
miniMessage,
blocked
);
addLog(chatLog);
chatLogWebHandler.forwardChatLogToWeb(chatLog);
} }
public CompletableFuture<List<ChatLog>> retrieveChatLogs(UUID uuid, Duration duration, String server) { public CompletableFuture<List<ChatLog>> retrieveChatLogs(UUID uuid, Duration duration, String server) {
@@ -0,0 +1,50 @@
package com.alttd.chat.objects.chat_log;
import com.alttd.altitudeweb.model.ChatMessageDto;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import java.time.ZoneOffset;
import java.util.Optional;
import java.util.Set;
@Slf4j
@UtilityClass
public class ChatLogMapper {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
public Optional<ChatMessageDto> toDto(ChatLog chatLog) {
ChatMessageDto chatMessageDto = new ChatMessageDto();
if (chatLog.getMiniMessage() == null) {
throw new IllegalArgumentException("MiniMessage cannot be null");
}
chatMessageDto.setUuid(chatLog.getUuid());
chatMessageDto.setTimestamp(chatLog.getTimestamp().atOffset(ZoneOffset.UTC));
chatMessageDto.setServer(chatLog.getServer());
chatMessageDto.setType(ChatLogTypeMapper.toDto(chatLog.getType()));
chatMessageDto.setChannel(chatLog.getChannel());
chatMessageDto.setReceiver(chatLog.getReceiver());
chatMessageDto.setMessage(GsonComponentSerializer.gson().serialize(chatLog.getMiniMessage()));
chatMessageDto.setBlocked(chatLog.isBlocked());
if (checkIsInvalid(chatMessageDto)) {
return Optional.empty();
}
return Optional.of(chatMessageDto);
}
private boolean checkIsInvalid(ChatMessageDto chatMessageDto) {
Set<ConstraintViolation<ChatMessageDto>> violations = validator.validate(chatMessageDto);
if (violations.isEmpty()) {
return false;
}
log.error("Validation failed for chat message: {}, {}", chatMessageDto, violations);
return true;
}
}
@@ -0,0 +1,10 @@
package com.alttd.chat.objects.chat_log;
public enum ChatLogType {
PUBLIC,
GLOBAL,
PARTY,
GAC,
MSG,
CUSTOM;
}
@@ -0,0 +1,43 @@
package com.alttd.chat.objects.chat_log;
import com.alttd.altitudeweb.model.ChatMessageDto;
import lombok.experimental.UtilityClass;
@UtilityClass
public class ChatLogTypeMapper {
public static String toDb(ChatLogType chatLogType) {
return switch (chatLogType) {
case PUBLIC -> "public";
case GLOBAL -> "global";
case PARTY -> "party";
case GAC -> "gac";
case MSG -> "msg";
case CUSTOM -> "custom";
};
}
public ChatMessageDto.TypeEnum toDto(ChatLogType chatLogType) {
return switch (chatLogType) {
case PUBLIC -> ChatMessageDto.TypeEnum.PUBLIC;
case GLOBAL -> ChatMessageDto.TypeEnum.GLOBAL;
case PARTY -> ChatMessageDto.TypeEnum.PARTY;
case GAC -> ChatMessageDto.TypeEnum.GAC;
case MSG -> ChatMessageDto.TypeEnum.MSG;
case CUSTOM -> ChatMessageDto.TypeEnum.CUSTOM;
};
}
public ChatLogType fromDb(String dbType) {
return switch (dbType) {
case "public" -> ChatLogType.PUBLIC;
case "global" -> ChatLogType.GLOBAL;
case "party" -> ChatLogType.PARTY;
case "gac" -> ChatLogType.GAC;
case "msg" -> ChatLogType.MSG;
case "custom" -> ChatLogType.CUSTOM;
default -> throw new IllegalArgumentException("Invalid chat log type: " + dbType);
};
}
}
@@ -0,0 +1,102 @@
package com.alttd.chat.objects.chat_log;
import com.alttd.altitudeweb.api.ChatApi;
import com.alttd.altitudeweb.invoker.ApiClient;
import com.alttd.altitudeweb.invoker.ApiException;
import com.alttd.altitudeweb.model.ChatMessageDto;
import com.alttd.chat.config.Config;
import com.alttd.chat.util.ALogger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ChatLogWebHandler {
private static final int MAX_QUEUE_SIZE = 100;
private static final Logger log = LoggerFactory.getLogger(ChatLogWebHandler.class);
private final Queue<ChatLog> queue = new ConcurrentLinkedQueue<>();
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private final ChatApi chatApi;
public ChatLogWebHandler() {
ApiClient apiClient = new ApiClient();
apiClient.setBasePath(Config.CHAT_WEB_SERVER_BASE_URL);
chatApi = new ChatApi(apiClient);
}
private volatile boolean sending;
public void forwardChatLogToWeb(ChatLog chatLog) {
if (queue.size() >= MAX_QUEUE_SIZE) {
queue.clear();
log.error("Chat log queue overflow, is the web backend still running?");
}
queue.add(chatLog);
triggerSend();
}
private void triggerSend() {
if (sending) {
return;
}
synchronized (this) {
if (sending || queue.isEmpty()) {
return;
}
sending = true;
}
executor.execute(this::sendBatch);
}
private void sendBatch() {
try {
ArrayList<ChatLog> chatLogList = new ArrayList<>();
ChatLog log;
while ((log = queue.poll()) != null) {
if (log.getMiniMessage() == null) {
ALogger.warn("No mini message for message, skipping");
continue;
}
chatLogList.add(log);
}
List<ChatMessageDto> batch = chatLogList.stream()
.map(ChatLogMapper::toDto)
.filter(Optional::isPresent)
.map(Optional::get)
.toList();
if (!chatLogList.isEmpty()) {
try {
chatApi.sendChatMessages(batch);
} catch (ApiException e) {
ALogger.error("Failed to send chat messages to web backend, " +
"adding messages back to queue for another try", e
);
queue.addAll(chatLogList);
}
}
} finally {
synchronized (this) {
sending = false;
}
triggerSend();
}
}
}
+6
View File
@@ -3,14 +3,20 @@ plugins {
id("io.github.goooler.shadow") id("io.github.goooler.shadow")
} }
val nexusUser = providers.gradleProperty("alttdSnapshotUsername").orNull ?: System.getenv("NEXUS_USERNAME")
val nexusPass = providers.gradleProperty("alttdSnapshotPassword").orNull ?: System.getenv("NEXUS_PASSWORD")
dependencies { dependencies {
implementation(project(":api")) // API implementation(project(":api")) // API
compileOnly("com.alttd.cosmos:cosmos-api:1.21.8-R0.1-SNAPSHOT") { compileOnly("com.alttd.cosmos:cosmos-api:1.21.8-R0.1-SNAPSHOT") {
isChanging = true isChanging = true
} }
compileOnly("org.projectlombok:lombok:1.18.46")
annotationProcessor("org.projectlombok:lombok:1.18.46")
compileOnly("com.gitlab.ruany:LiteBansAPI:0.6.1") // move to proxy compileOnly("com.gitlab.ruany:LiteBansAPI:0.6.1") // move to proxy
compileOnly("org.apache.commons:commons-lang3:3.17.0") // needs an alternative, already removed from upstream api and will be removed in server compileOnly("org.apache.commons:commons-lang3:3.17.0") // needs an alternative, already removed from upstream api and will be removed in server
compileOnly("net.luckperms:api:5.5") // Luckperms compileOnly("net.luckperms:api:5.5") // Luckperms
implementation("com.alttd.inventory_gui:InventoryGUI:1.1.5-SNAPSHOT")
} }
tasks { tasks {
@@ -35,11 +35,15 @@ public class ChatPlugin extends JavaPlugin {
instance = this; instance = this;
ALogger.init(getSLF4JLogger()); ALogger.init(getSLF4JLogger());
chatAPI = new ChatImplementation(); chatAPI = new ChatImplementation();
chatHandler = new ChatHandler(); ChatLogHandler chatLogHandler = ChatLogHandler.getInstance(true);
chatHandler = new ChatHandler(chatLogHandler);
DatabaseConnection.initialize(); DatabaseConnection.initialize();
serverConfig = new ServerConfig(ServerName.getServerName()); serverConfig = new ServerConfig(ServerName.getServerName());
ChatLogHandler chatLogHandler = ChatLogHandler.getInstance(true); registerListener(new PlayerListener(serverConfig),
registerListener(new PlayerListener(serverConfig), new ChatListener(chatLogHandler), new BookListener(), new ShutdownListener(chatLogHandler, this)); new ChatListener(chatLogHandler),
new BookListener(),
new ShutdownListener(chatLogHandler, this)
);
if (serverConfig.GLOBALCHAT) { if (serverConfig.GLOBALCHAT) {
registerCommand("globalchat", new GlobalChat()); registerCommand("globalchat", new GlobalChat());
registerCommand("toggleglobalchat", new ToggleGlobalChat()); registerCommand("toggleglobalchat", new ToggleGlobalChat());
@@ -58,12 +62,15 @@ public class ChatPlugin extends JavaPlugin {
if (!(channel instanceof CustomChannel customChannel)) { if (!(channel instanceof CustomChannel customChannel)) {
continue; continue;
} }
this.getServer().getCommandMap().register(channel.getChannelName().toLowerCase(), new ChatChannel(customChannel)); this.getServer()
.getCommandMap()
.register(channel.getChannelName().toLowerCase(), new ChatChannel(customChannel));
} }
String messageChannel = Config.MESSAGECHANNEL; String messageChannel = Config.MESSAGECHANNEL;
getServer().getMessenger().registerOutgoingPluginChannel(this, messageChannel); getServer().getMessenger().registerOutgoingPluginChannel(this, messageChannel);
getServer().getMessenger().registerIncomingPluginChannel(this, messageChannel, new PluginMessage()); getServer().getMessenger()
.registerIncomingPluginChannel(this, messageChannel, new PluginMessage(chatLogHandler));
NicknamesEvents nicknamesEvents = new NicknamesEvents(); NicknamesEvents nicknamesEvents = new NicknamesEvents();
getServer().getMessenger().registerIncomingPluginChannel(this, messageChannel, nicknamesEvents); getServer().getMessenger().registerIncomingPluginChannel(this, messageChannel, nicknamesEvents);
@@ -110,7 +117,9 @@ public class ChatPlugin extends JavaPlugin {
chatAPI.reloadConfig(); chatAPI.reloadConfig();
chatAPI.reloadChatFilters(); chatAPI.reloadChatFilters();
serverConfig = new ServerConfig(ServerName.getServerName()); serverConfig = new ServerConfig(ServerName.getServerName());
Bukkit.broadcast(Utility.parseMiniMessage("Reloaded ChatPlugin Config.").asComponent(), "command.chat.reloadchat"); Bukkit.broadcast(Utility.parseMiniMessage("Reloaded ChatPlugin Config.").asComponent(),
"command.chat.reloadchat"
);
ALogger.info("Reloaded ChatPlugin config."); ALogger.info("Reloaded ChatPlugin config.");
} }
} }
@@ -8,6 +8,8 @@ import com.alttd.chat.objects.ChatFilter;
import com.alttd.chat.objects.ChatUser; import com.alttd.chat.objects.ChatUser;
import com.alttd.chat.objects.ModifiableString; import com.alttd.chat.objects.ModifiableString;
import com.alttd.chat.objects.channels.CustomChannel; import com.alttd.chat.objects.channels.CustomChannel;
import com.alttd.chat.objects.chat_log.ChatLogHandler;
import com.alttd.chat.objects.chat_log.ChatLogType;
import com.alttd.chat.util.ALogger; import com.alttd.chat.util.ALogger;
import com.alttd.chat.util.GalaxyUtility; import com.alttd.chat.util.GalaxyUtility;
import com.alttd.chat.util.ServerName; import com.alttd.chat.util.ServerName;
@@ -28,6 +30,7 @@ import org.bukkit.Material;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jspecify.annotations.Nullable;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@@ -40,8 +43,10 @@ public class ChatHandler {
private final ChatPlugin plugin; private final ChatPlugin plugin;
private final ComponentLike GCNOTENABLED; private final ComponentLike GCNOTENABLED;
private final ChatLogHandler chatLogHandler;
public ChatHandler() { public ChatHandler(ChatLogHandler chatLogHandler) {
this.chatLogHandler = chatLogHandler;
plugin = ChatPlugin.getInstance(); plugin = ChatPlugin.getInstance();
GCNOTENABLED = Utility.parseMiniMessage(Config.GCNOTENABLED); GCNOTENABLED = Utility.parseMiniMessage(Config.GCNOTENABLED);
} }
@@ -64,7 +69,8 @@ public class ChatHandler {
GalaxyUtility.sendBlockedNotification("DM Language", GalaxyUtility.sendBlockedNotification("DM Language",
player, player,
Utility.parseMiniMessage(Utility.parseColors(modifiableString.string())), Utility.parseMiniMessage(Utility.parseColors(modifiableString.string())),
target); target
);
return; // the message was blocked return; // the message was blocked
} }
@@ -73,7 +79,8 @@ public class ChatHandler {
sendPrivateMessage(player, target, "privatemessage", component.asComponent()); sendPrivateMessage(player, target, "privatemessage", component.asComponent());
ComponentLike spymessage = Utility.parseMiniMessage(Config.MESSAGESPY, placeholders); ComponentLike spymessage = Utility.parseMiniMessage(Config.MESSAGESPY, placeholders);
for (Player pl : Bukkit.getOnlinePlayers()) { for (Player pl : Bukkit.getOnlinePlayers()) {
if (pl.hasPermission(Config.SPYPERMISSION) && ChatUserManager.getChatUser(pl.getUniqueId()).isSpy() && !pl.equals(player) && !pl.getName().equalsIgnoreCase(target)) { if (pl.hasPermission(Config.SPYPERMISSION) && ChatUserManager.getChatUser(pl.getUniqueId())
.isSpy() && !pl.equals(player) && !pl.getName().equalsIgnoreCase(target)) {
pl.sendMessage(spymessage); pl.sendMessage(spymessage);
} }
} }
@@ -96,7 +103,8 @@ public class ChatHandler {
GalaxyUtility.sendBlockedNotification("DM Language", GalaxyUtility.sendBlockedNotification("DM Language",
player, player,
Utility.parseMiniMessage(Utility.parseColors(modifiableString.string())), Utility.parseMiniMessage(Utility.parseColors(modifiableString.string())),
target); target
);
return; // the message was blocked return; // the message was blocked
} }
@@ -108,7 +116,8 @@ public class ChatHandler {
sendPrivateMessage(player, target, "privatemessage", messageComponent); sendPrivateMessage(player, target, "privatemessage", messageComponent);
ComponentLike spymessage = Utility.parseMiniMessage(Config.MESSAGESPY, placeholders); ComponentLike spymessage = Utility.parseMiniMessage(Config.MESSAGESPY, placeholders);
for (Player pl : Bukkit.getOnlinePlayers()) { for (Player pl : Bukkit.getOnlinePlayers()) {
if (pl.hasPermission(Config.SPYPERMISSION) && ChatUserManager.getChatUser(pl.getUniqueId()).isSpy() && !pl.equals(player) && !pl.getName().equalsIgnoreCase(target)) { if (pl.hasPermission(Config.SPYPERMISSION) && ChatUserManager.getChatUser(pl.getUniqueId())
.isSpy() && !pl.equals(player) && !pl.getName().equalsIgnoreCase(target)) {
pl.sendMessage(spymessage); pl.sendMessage(spymessage);
} }
} }
@@ -127,7 +136,9 @@ public class ChatHandler {
long timeLeft = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - user.getGcCooldown()); long timeLeft = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - user.getGcCooldown());
if (timeLeft <= Config.GCCOOLDOWN && !player.hasPermission("chat.globalchat.cooldownbypass")) { // player is on cooldown and should wait x seconds if (timeLeft <= Config.GCCOOLDOWN && !player.hasPermission("chat.globalchat.cooldownbypass")) { // player is on cooldown and should wait x seconds
player.sendRichMessage(Config.GCONCOOLDOWN, Placeholder.parsed("cooldown", Config.GCCOOLDOWN - timeLeft + "")); player.sendRichMessage(Config.GCONCOOLDOWN,
Placeholder.parsed("cooldown", Config.GCCOOLDOWN - timeLeft + "")
);
return; return;
} }
@@ -148,11 +159,22 @@ public class ChatHandler {
GalaxyUtility.sendBlockedNotification("GC Language", GalaxyUtility.sendBlockedNotification("GC Language",
player, player,
Utility.parseMiniMessage(Utility.parseColors(modifiableString.string())), Utility.parseMiniMessage(Utility.parseColors(modifiableString.string())),
""); ""
);
return; // the message was blocked return; // the message was blocked
} }
component = modifiableString.component(); component = modifiableString.component();
chatLogHandler.addChatLog(player.getUniqueId(),
Bukkit.getServerName(),
message,
ChatLogType.GLOBAL,
null,
null,
component,
false
);
user.setGcCooldown(System.currentTimeMillis()); user.setGcCooldown(System.currentTimeMillis());
sendPluginMessage(player, "globalchat", component); sendPluginMessage(player, "globalchat", component);
} }
@@ -180,11 +202,16 @@ public class ChatHandler {
Component component = Utility.parseMiniMessage(channel.getFormat(), placeholders).asComponent(); Component component = Utility.parseMiniMessage(channel.getFormat(), placeholders).asComponent();
ModifiableString modifiableString = new ModifiableString(component); ModifiableString modifiableString = new ModifiableString(component);
if (!RegexManager.filterText(player.getName(), player.getUniqueId(), modifiableString, channel.getChannelName())) { if (!RegexManager.filterText(player.getName(),
player.getUniqueId(),
modifiableString,
channel.getChannelName()
)) {
GalaxyUtility.sendBlockedNotification(channel.getChannelName() + " Language", GalaxyUtility.sendBlockedNotification(channel.getChannelName() + " Language",
player, player,
Utility.parseMiniMessage(Utility.parseColors(modifiableString.string())), Utility.parseMiniMessage(Utility.parseColors(modifiableString.string())),
""); ""
);
ALogger.info("Refusing to send blocked chat message"); ALogger.info("Refusing to send blocked chat message");
return; return;
} }
@@ -255,16 +282,20 @@ public class ChatHandler {
if (!chatChannel.getServers().contains(ServerName.getServerName())) { if (!chatChannel.getServers().contains(ServerName.getServerName())) {
player.sendRichMessage("<red>Unable to send messages to <channel> in this server.</red>", player.sendRichMessage("<red>Unable to send messages to <channel> in this server.</red>",
Placeholder.parsed("channel", chatChannel.getChannelName())); Placeholder.parsed("channel", chatChannel.getChannelName())
);
ALogger.info(String.format("Not sending chat message due to [%s] not being in this channels config", ALogger.info(String.format("Not sending chat message due to [%s] not being in this channels config",
ServerName.getServerName())); ServerName.getServerName()
));
return; return;
} }
Stream<? extends Player> stream = Bukkit.getServer().getOnlinePlayers().stream() Stream<? extends Player> stream = Bukkit.getServer().getOnlinePlayers().stream()
.filter(p -> p.hasPermission(chatChannel.getPermission())); .filter(p -> p.hasPermission(chatChannel.getPermission()));
if (!player.hasPermission("chat.ignorebypass")) { if (!player.hasPermission("chat.ignorebypass")) {
stream = stream.filter(receiver -> !ChatUserManager.getChatUser(receiver.getUniqueId()).getIgnoredPlayers().contains(uuid) stream = stream.filter(receiver -> !ChatUserManager.getChatUser(receiver.getUniqueId())
.getIgnoredPlayers()
.contains(uuid)
|| receiver.hasPermission("chat.ignorebypass")); || receiver.hasPermission("chat.ignorebypass"));
} }
if (chatChannel.isLocal()) { if (chatChannel.isLocal()) {
@@ -283,6 +314,16 @@ public class ChatHandler {
List<? extends Player> recipientPlayers = stream.toList(); List<? extends Player> recipientPlayers = stream.toList();
recipientPlayers.forEach(p -> p.sendMessage(component)); recipientPlayers.forEach(p -> p.sendMessage(component));
chatLogHandler.addChatLog(player.getUniqueId(),
Bukkit.getServerName(),
message,
ChatLogType.CUSTOM,
chatChannel.getChannelName(),
getLocationIfLocal(chatChannel, player),
component.asComponent(),
false
);
List<UUID> recipientUUIDs = recipientPlayers.stream().map(Entity::getUniqueId).toList(); List<UUID> recipientUUIDs = recipientPlayers.stream().map(Entity::getUniqueId).toList();
Bukkit.getServer().getOnlinePlayers().stream() Bukkit.getServer().getOnlinePlayers().stream()
.filter(onlinePlayer -> onlinePlayer.hasPermission(Config.SPYPERMISSION)) .filter(onlinePlayer -> onlinePlayer.hasPermission(Config.SPYPERMISSION))
@@ -290,7 +331,22 @@ public class ChatHandler {
.forEach(onlinePlayer -> onlinePlayer.sendRichMessage(Config.CHANNEL_SPY, .forEach(onlinePlayer -> onlinePlayer.sendRichMessage(Config.CHANNEL_SPY,
Placeholder.component("sender", player.name()), Placeholder.component("sender", player.name()),
Placeholder.parsed("channel", chatChannel.getChannelName()), Placeholder.parsed("channel", chatChannel.getChannelName()),
Placeholder.parsed("message", message))); Placeholder.parsed("message", message)
));
}
private static @Nullable String getLocationIfLocal(CustomChannel chatChannel, Player player) {
if (!chatChannel.isLocal()) {
return null;
}
String format = String.format("%s;%s",
player.getLocation().getBlockX(),
player.getLocation().getBlockZ()
);
if (format.length() > 36) {
return null;
}
return format;
} }
private void sendPluginMessage(Player player, String channel, Component component) { private void sendPluginMessage(Player player, String channel, Component component) {
@@ -326,9 +382,14 @@ public class ChatHandler {
if (user == null) { if (user == null) {
return false; return false;
} }
if (user.isMuted() || (ChatPlugin.getInstance().serverMuted() && !player.hasPermission("chat.bypass-server-muted"))) { if (user.isMuted() || (ChatPlugin.getInstance().serverMuted() && !player.hasPermission(
"chat.bypass-server-muted"))) {
// if (Database.get().isPlayerMuted(player.getUniqueId(), null) || (ChatPlugin.getInstance().serverMuted() && !player.hasPermission("chat.bypass-server-muted"))) { // if (Database.get().isPlayerMuted(player.getUniqueId(), null) || (ChatPlugin.getInstance().serverMuted() && !player.hasPermission("chat.bypass-server-muted"))) {
GalaxyUtility.sendBlockedNotification(prefix, player, Utility.parseMiniMessage(Utility.stripTokens(message)), ""); GalaxyUtility.sendBlockedNotification(prefix,
player,
Utility.parseMiniMessage(Utility.stripTokens(message)),
""
);
return true; return true;
} }
return false; return false;
@@ -7,6 +7,7 @@ import com.alttd.chat.managers.ChatUserManager;
import com.alttd.chat.managers.RegexManager; import com.alttd.chat.managers.RegexManager;
import com.alttd.chat.objects.*; import com.alttd.chat.objects.*;
import com.alttd.chat.objects.chat_log.ChatLogHandler; import com.alttd.chat.objects.chat_log.ChatLogHandler;
import com.alttd.chat.objects.chat_log.ChatLogType;
import com.alttd.chat.util.ALogger; import com.alttd.chat.util.ALogger;
import com.alttd.chat.util.GalaxyUtility; import com.alttd.chat.util.GalaxyUtility;
import com.alttd.chat.util.ServerName; import com.alttd.chat.util.ServerName;
@@ -55,9 +56,14 @@ public class ChatListener implements Listener {
} }
Component formatComponent = Component.text("%message%"); Component formatComponent = Component.text("%message%");
ComponentLike message = parseMessageContent(event.player(), plainTextComponentSerializer.serialize(event.originalMessage())); ComponentLike message = parseMessageContent(event.player(),
plainTextComponentSerializer.serialize(event.originalMessage())
);
event.result(formatComponent.replaceText(TextReplacementConfig.builder().match("%message%").replacement(message).build())); event.result(formatComponent.replaceText(TextReplacementConfig.builder()
.match("%message%")
.replacement(message)
.build()));
} }
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
@@ -67,9 +73,14 @@ public class ChatListener implements Listener {
} }
Component formatComponent = Component.text("%message%"); Component formatComponent = Component.text("%message%");
ComponentLike message = parseMessageContent(event.player(), plainTextComponentSerializer.serialize(event.originalMessage())); ComponentLike message = parseMessageContent(event.player(),
plainTextComponentSerializer.serialize(event.originalMessage())
);
event.result(formatComponent.replaceText(TextReplacementConfig.builder().match("%message%").replacement(message).build())); event.result(formatComponent.replaceText(TextReplacementConfig.builder()
.match("%message%")
.replacement(message)
.build()));
} }
private final Component mention = MiniMessage.miniMessage().deserialize(Config.MENTIONPLAYERTAG); private final Component mention = MiniMessage.miniMessage().deserialize(Config.MENTIONPLAYERTAG);
@@ -92,8 +103,9 @@ public class ChatListener implements Listener {
UUID uuid = player.getUniqueId(); UUID uuid = player.getUniqueId();
ComponentLike input = event.message().colorIfAbsent(NamedTextColor.WHITE); ComponentLike input = event.message().colorIfAbsent(NamedTextColor.WHITE);
Component inputComponent = input.asComponent();
ModifiableString modifiableString = new ModifiableString(input.asComponent()); ModifiableString modifiableString = new ModifiableString(inputComponent);
// todo a better way for this // todo a better way for this
if (!RegexManager.filterText(player.getName(), uuid, modifiableString, true, "chat", filterType -> { if (!RegexManager.filterText(player.getName(), uuid, modifiableString, true, "chat", filterType -> {
@@ -107,12 +119,23 @@ public class ChatListener implements Listener {
out.writeUTF(uuid.toString()); out.writeUTF(uuid.toString());
out.writeUTF(modifiableString.string()); out.writeUTF(modifiableString.string());
player.sendPluginMessage(ChatPlugin.getInstance(), Config.MESSAGECHANNEL, out.toByteArray()); player.sendPluginMessage(ChatPlugin.getInstance(), Config.MESSAGECHANNEL, out.toByteArray());
})) { }
)) {
event.setCancelled(true); event.setCancelled(true);
GalaxyUtility.sendBlockedNotification("Language", player, GalaxyUtility.sendBlockedNotification("Language", player,
modifiableString.component(), modifiableString.component(),
""); ""
chatLogHandler.addChatLog(uuid, ServerName.getServerName(), PlainTextComponentSerializer.plainText().serialize(input.asComponent()), true); );
String originalMessage = PlainTextComponentSerializer.plainText().serialize(inputComponent);
chatLogHandler.addChatLog(uuid,
ServerName.getServerName(),
originalMessage,
ChatLogType.PUBLIC,
null,
null,
render(player, inputComponent).asComponent(),
true
);
return; // the message was blocked return; // the message was blocked
} }
@@ -120,8 +143,13 @@ public class ChatListener implements Listener {
.map(audience -> (Player) audience); .map(audience -> (Player) audience);
if (!player.hasPermission("chat.ignorebypass")) { if (!player.hasPermission("chat.ignorebypass")) {
stream = stream.filter(receiver -> !ChatUserManager.getChatUser(receiver.getUniqueId()).getIgnoredPlayers().contains(uuid) stream = stream.filter(receiver -> {
|| receiver.hasPermission("chat.ignorebypass")); boolean isPlayerIgnored = ChatUserManager
.getChatUser(receiver.getUniqueId())
.getIgnoredPlayers()
.contains(uuid);
return !isPlayerIgnored || receiver.hasPermission("chat.ignorebypass");
});
} }
Set<Player> receivers = stream.collect(Collectors.toSet()); Set<Player> receivers = stream.collect(Collectors.toSet());
@@ -135,8 +163,16 @@ public class ChatListener implements Listener {
for (Player pingPlayer : playersToPing) { for (Player pingPlayer : playersToPing) {
pingPlayer.playSound(pingPlayer.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1, 1); pingPlayer.playSound(pingPlayer.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1, 1);
} }
chatLogHandler.addChatLog(uuid, ServerName.getServerName(), modifiableString.string(), false); chatLogHandler.addChatLog(uuid,
ALogger.info(PlainTextComponentSerializer.plainText().serialize(input.asComponent())); ServerName.getServerName(),
modifiableString.string(),
ChatLogType.PUBLIC,
null,
null,
input.asComponent(),
false
);
ALogger.info(PlainTextComponentSerializer.plainText().serialize(inputComponent));
} }
private void pingPlayers(Set<Player> playersToPing, ModifiableString modifiableString, Player player) { private void pingPlayers(Set<Player> playersToPing, ModifiableString modifiableString, Player player) {
@@ -165,7 +201,9 @@ public class ChatListener implements Listener {
// return ; // return ;
// }); // });
if (!ChatUserManager.getChatUser(onlinePlayer.getUniqueId()).getIgnoredPlayers().contains(player.getUniqueId()) if (!ChatUserManager.getChatUser(onlinePlayer.getUniqueId())
.getIgnoredPlayers()
.contains(player.getUniqueId())
|| player.hasPermission("chat.ignorebypass")) { || player.hasPermission("chat.ignorebypass")) {
playersToPing.add(onlinePlayer); playersToPing.add(onlinePlayer);
} }
@@ -175,7 +213,9 @@ public class ChatListener implements Listener {
.match(nickPattern) .match(nickPattern)
.replacement(mention.append(onlinePlayerUser.getDisplayName())) .replacement(mention.append(onlinePlayerUser.getDisplayName()))
.build()); .build());
if (!ChatUserManager.getChatUser(onlinePlayer.getUniqueId()).getIgnoredPlayers().contains(player.getUniqueId()) if (!ChatUserManager.getChatUser(onlinePlayer.getUniqueId())
.getIgnoredPlayers()
.contains(player.getUniqueId())
|| player.hasPermission("chat.ignorebypass")) { || player.hasPermission("chat.ignorebypass")) {
playersToPing.add(onlinePlayer); playersToPing.add(onlinePlayer);
} }
@@ -10,6 +10,8 @@ import com.alttd.chat.objects.Party;
import com.alttd.chat.objects.PartyUser; import com.alttd.chat.objects.PartyUser;
import com.alttd.chat.objects.channels.Channel; import com.alttd.chat.objects.channels.Channel;
import com.alttd.chat.objects.channels.CustomChannel; import com.alttd.chat.objects.channels.CustomChannel;
import com.alttd.chat.objects.chat_log.ChatLogHandler;
import com.alttd.chat.objects.chat_log.ChatLogType;
import com.alttd.chat.util.ALogger; import com.alttd.chat.util.ALogger;
import com.alttd.chat.util.ServerName; import com.alttd.chat.util.ServerName;
import com.alttd.chat.util.Utility; import com.alttd.chat.util.Utility;
@@ -18,6 +20,7 @@ import com.google.common.io.ByteStreams;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike; import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Sound; import org.bukkit.Sound;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@@ -29,6 +32,12 @@ import java.util.UUID;
public class PluginMessage implements PluginMessageListener { public class PluginMessage implements PluginMessageListener {
private final ChatLogHandler chatLogHandler;
public PluginMessage(ChatLogHandler chatLogHandler) {
this.chatLogHandler = chatLogHandler;
}
@Override @Override
public void onPluginMessageReceived(String channel, @NotNull Player ignored, byte[] bytes) { public void onPluginMessageReceived(String channel, @NotNull Player ignored, byte[] bytes) {
if (!channel.equals(Config.MESSAGECHANNEL)) { if (!channel.equals(Config.MESSAGECHANNEL)) {
@@ -48,13 +57,25 @@ public class PluginMessage implements PluginMessageListener {
} }
ChatUser chatUser = ChatUserManager.getChatUser(uuid); ChatUser chatUser = ChatUserManager.getChatUser(uuid);
if (isTargetNotIgnored(chatUser, targetuuid)) { if (isTargetNotIgnored(chatUser, targetuuid)) {
player.sendMessage(GsonComponentSerializer.gson().deserialize(message)); Component component = GsonComponentSerializer.gson().deserialize(message);
player.sendMessage(component);
player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1, player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 1,
1); // todo load this from config 1
); // todo load this from config
ChatUser user = ChatUserManager.getChatUser(uuid); ChatUser user = ChatUserManager.getChatUser(uuid);
if (!user.getReplyContinueTarget().equalsIgnoreCase(target)) { if (!user.getReplyContinueTarget().equalsIgnoreCase(target)) {
user.setReplyTarget(target); user.setReplyTarget(target);
} }
// Handled here since this is after the ignore check and is only send once
chatLogHandler.addChatLog(uuid,
ServerName.getServerName(),
PlainTextComponentSerializer.plainText().serialize(component),
ChatLogType.MSG,
null,
player.getUniqueId().toString(),
component,
false
);
} }
} }
case "privatemessageout" -> { case "privatemessageout" -> {
@@ -69,9 +90,18 @@ public class PluginMessage implements PluginMessageListener {
ChatUser chatUser = ChatUserManager.getChatUser(uuid); ChatUser chatUser = ChatUserManager.getChatUser(uuid);
if (isTargetNotIgnored(chatUser, targetuuid)) { if (isTargetNotIgnored(chatUser, targetuuid)) {
chatUser.setReplyTarget(target); chatUser.setReplyTarget(target);
player.sendMessage(GsonComponentSerializer.gson().deserialize(message)); Component component = GsonComponentSerializer.gson().deserialize(message);
// ChatUser user = ChatUserManager.getChatUser(uuid); player.sendMessage(component);
// user.setReplyTarget(target); // Handled here since this is after the ignore check and is only send once
chatLogHandler.addChatLog(uuid,
ServerName.getServerName(),
PlainTextComponentSerializer.plainText().serialize(component),
ChatLogType.MSG,
null,
player.getUniqueId().toString(),
component,
false
);
} }
} }
case "globalchat" -> { case "globalchat" -> {
@@ -82,10 +112,11 @@ public class PluginMessage implements PluginMessageListener {
UUID uuid = UUID.fromString(in.readUTF()); UUID uuid = UUID.fromString(in.readUTF());
String message = in.readUTF(); String message = in.readUTF();
Component component = GsonComponentSerializer.gson().deserialize(message);
Bukkit.getOnlinePlayers().stream().filter(p -> p.hasPermission(Config.GCPERMISSION)).forEach(p -> { Bukkit.getOnlinePlayers().stream().filter(p -> p.hasPermission(Config.GCPERMISSION)).forEach(p -> {
ChatUser chatUser = ChatUserManager.getChatUser(p.getUniqueId()); ChatUser chatUser = ChatUserManager.getChatUser(p.getUniqueId());
if (isTargetNotIgnored(chatUser, uuid)) { if (isTargetNotIgnored(chatUser, uuid)) {
p.sendMessage(GsonComponentSerializer.gson().deserialize(message)); p.sendMessage(component);
} }
}); });
} }
@@ -107,6 +138,7 @@ public class PluginMessage implements PluginMessageListener {
} }
chatChannel(in); chatChannel(in);
//TODO [Stijn] [2026-07-19]: handle custom channels
} }
case "tmppartyupdate" -> { case "tmppartyupdate" -> {
int id = Integer.parseInt(in.readUTF()); int id = Integer.parseInt(in.readUTF());
@@ -45,13 +45,83 @@ public class Nicknames implements CommandExecutor, TabCompleter {
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, String[] args) {
if (sender instanceof Player player) { if (!(sender instanceof Player player)) {
sender.sendMessage("Console commands are disabled.");
return true;
}
if (args.length == 0) { if (args.length == 0) {
sender.sendRichMessage(helpMessage(sender, HelpType.ALL)); sender.sendRichMessage(helpMessage(sender, HelpType.ALL));
return true; return true;
} }
switch (args[0].toLowerCase()) { switch (args[0].toLowerCase()) {
case "set": case "set" -> setNickname(sender, args, player);
case "review" -> reviewNickname(sender, args, player);
case "request" -> requestNickname(sender, args, player);
case "try" -> tryNickname(sender, args, player);
case "current" -> showCurrentNickname(sender, player);
case "help" ->
sender.sendRichMessage(helpMessage(sender, HelpType.ALL) + "For more info on nicknames and how to use rgb colors go to: <aqua>https://alttd.com/nicknames<white>");
default -> sender.sendRichMessage(helpMessage(sender, HelpType.ALL));
}
return true;
}
private void showCurrentNickname(@NotNull CommandSender sender, Player player) {
if (!hasPermission(sender, "chat.command.nick.current")) {
return;
}
ChatUser chatUser = ChatUserManager.getChatUser(player.getUniqueId());
TagResolver placeholders = TagResolver.resolver(
Placeholder.component("nickname", chatUser.getDisplayName()),
Placeholder.parsed("currentnickname", chatUser.getNickNameString())
);
player.sendRichMessage(Config.NICK_CURRENT, placeholders);
}
private void tryNickname(@NotNull CommandSender sender, String[] args, Player player) {
if (args.length != 2 || !hasPermission(sender, "chat.command.nick.try")) {
sender.sendRichMessage(helpMessage(sender, HelpType.TRY));
return;
}
LuckPerms api = ChatAPI.get().getLuckPerms();
if (api == null) {
sender.sendRichMessage(Config.NICK_NO_LUCKPERMS);
return;
}
if (!NickUtilities.validNick(player, player, args[1])) {
return;
}
sender.sendRichMessage(Config.NICK_TRYOUT,
Placeholder.component("prefix", Utility.applyColor(api.getUserManager().getUser(player.getUniqueId())
.getCachedData().getMetaData().getPrefix())), // TODO pull this from chatuser?
Placeholder.component("nick", Utility.applyColor(args[1])),
Placeholder.unparsed("nickrequest", args[1]));
}
private void requestNickname(@NotNull CommandSender sender, String[] args, Player player) {
if (args.length != 2 || !hasPermission(sender, "chat.command.nick.request")) {
sender.sendRichMessage(helpMessage(sender, HelpType.REQUEST));
return;
}
new BukkitRunnable() {
@Override
public void run() {
handleNickRequest(player, args[1]);
}
}.runTaskAsynchronously(ChatPlugin.getInstance());
}
private void reviewNickname(@NotNull CommandSender sender, String[] args, Player player) {
if (args.length != 1 || !hasPermission(sender, "chat.command.nick.review")) {
sender.sendRichMessage(helpMessage(sender, HelpType.REVIEW));
return;
}
NicknamesGui nicknamesGui = new NicknamesGui(player);
ChatPlugin.getInstance().getServer().getPluginManager().registerEvents(nicknamesGui, ChatPlugin.getInstance());
nicknamesGui.openInventory(player);
}
private void setNickname(@NotNull CommandSender sender, String[] args, Player player) {
if (args.length == 2 && hasPermission(sender, "chat.command.nick.set")) { if (args.length == 2 && hasPermission(sender, "chat.command.nick.set")) {
handleNick(player, player, args[1]); handleNick(player, player, args[1]);
} else if (args.length == 3 && hasPermission(sender, "chat.command.nick.set.others")) { } else if (args.length == 3 && hasPermission(sender, "chat.command.nick.set.others")) {
@@ -65,67 +135,6 @@ public class Nicknames implements CommandExecutor, TabCompleter {
} else if (args.length > 3) { } else if (args.length > 3) {
sender.sendRichMessage(helpMessage(sender, HelpType.SET_SELF, HelpType.SET_OTHERS)); sender.sendRichMessage(helpMessage(sender, HelpType.SET_SELF, HelpType.SET_OTHERS));
} }
break;
case "review":
if (args.length == 1 && hasPermission(sender, "chat.command.nick.review")) {
NicknamesGui nicknamesGui = new NicknamesGui();
ChatPlugin.getInstance().getServer().getPluginManager().registerEvents(nicknamesGui, ChatPlugin.getInstance());
nicknamesGui.openInventory(player);
} else {
sender.sendRichMessage(helpMessage(sender, HelpType.REVIEW));
}
break;
case "request":
if (args.length == 2 && hasPermission(sender, "chat.command.nick.request")) {
new BukkitRunnable() {
@Override
public void run() {
handleNickRequest(player, args[1]);
}
}.runTaskAsynchronously(ChatPlugin.getInstance());
} else {
sender.sendRichMessage(helpMessage(sender, HelpType.REQUEST));
}
break;
case "try":
if (args.length == 2 && hasPermission(sender, "chat.command.nick.try")) {
LuckPerms api = ChatAPI.get().getLuckPerms();
if (api != null) {
if (NickUtilities.validNick(player, player, args[1])) {
sender.sendRichMessage(Config.NICK_TRYOUT,
Placeholder.component("prefix", Utility.applyColor(api.getUserManager().getUser(player.getUniqueId())
.getCachedData().getMetaData().getPrefix())), // TODO pull this from chatuser?
Placeholder.component("nick", Utility.applyColor(args[1])),
Placeholder.unparsed("nickrequest", args[1]));
}
} else {
sender.sendRichMessage(Config.NICK_NO_LUCKPERMS);
}
} else {
sender.sendRichMessage(helpMessage(sender, HelpType.TRY));
}
break;
case "current":
if (hasPermission(sender, "chat.command.nick.current")) {
ChatUser chatUser = ChatUserManager.getChatUser(player.getUniqueId());
TagResolver placeholders = TagResolver.resolver(
Placeholder.component("nickname", chatUser.getDisplayName()),
Placeholder.parsed("currentnickname", chatUser.getNickNameString())
);
player.sendRichMessage(Config.NICK_CURRENT, placeholders);
}
break;
case "help":
sender.sendRichMessage(helpMessage(sender, HelpType.ALL)
+ "For more info on nicknames and how to use rgb colors go to: <aqua>https://alttd.com/nicknames<white>");
break;
default:
sender.sendRichMessage(helpMessage(sender, HelpType.ALL));
}
} else {
sender.sendMessage("Console commands are disabled.");
}
return true;
} }
@Override @Override
@@ -136,6 +145,33 @@ public class Nicknames implements CommandExecutor, TabCompleter {
} }
if (args.length == 1) { if (args.length == 1) {
tabCompleteArgLengthOne(sender, args, completions);
} else if (args.length == 2) {
tabCompleteArgLengthTwo(sender, args, completions);
}
return completions;
}
private static void tabCompleteArgLengthTwo(CommandSender sender, String[] args, List<String> completions) {
if (!args[0].equalsIgnoreCase("set")) {
return;
}
List<String> choices = new ArrayList<>();
List<String> onlinePlayers = new ArrayList<>();
Bukkit.getOnlinePlayers().forEach(a -> onlinePlayers.add(a.getName()));
if (sender.hasPermission("chat.command.nick.set.others")) {
choices.addAll(onlinePlayers);
}
for (String s : choices) {
if (s.startsWith(args[1])) {
completions.add(s);
}
}
}
private static void tabCompleteArgLengthOne(CommandSender sender, String[] args, List<String> completions) {
List<String> choices = new ArrayList<>(); List<String> choices = new ArrayList<>();
if (sender.hasPermission("chat.command.nick.set")) { if (sender.hasPermission("chat.command.nick.set")) {
choices.add("set"); choices.add("set");
@@ -159,24 +195,6 @@ public class Nicknames implements CommandExecutor, TabCompleter {
completions.add(s); completions.add(s);
} }
} }
} else if (args.length == 2) {
if (args[0].equalsIgnoreCase("set")) {
List<String> choices = new ArrayList<>();
List<String> onlinePlayers = new ArrayList<>();
Bukkit.getOnlinePlayers().forEach(a -> onlinePlayers.add(a.getName()));
if (sender.hasPermission("chat.command.nick.set.others")) {
choices.addAll(onlinePlayers);
}
for (String s : choices) {
if (s.startsWith(args[1])) {
completions.add(s);
}
}
}
}
return completions;
} }
private void handleNickRequest(Player player, String nickName) { private void handleNickRequest(Player player, String nickName) {
@@ -261,31 +279,13 @@ public class Nicknames implements CommandExecutor, TabCompleter {
private void handleNick(Player sender, OfflinePlayer target, final String nickName) { private void handleNick(Player sender, OfflinePlayer target, final String nickName) {
if (nickName.equalsIgnoreCase("off")) { if (nickName.equalsIgnoreCase("off")) {
handleNickOff(sender, target);
try {
if (target.isOnline()) {
resetNick(Objects.requireNonNull(target.getPlayer()));
}
Queries.removePlayerFromDataBase(target.getUniqueId());
NickCache.remove(target.getUniqueId());
nickCacheUpdate.add(target.getUniqueId());
} catch (SQLException e) {
ALogger.error("Failed to remove nickname from database", e);
}
if (!sender.equals(target)) {
sender.sendRichMessage(Config.NICK_RESET_OTHERS,
Placeholder.unparsed("player", Objects.requireNonNull(target.getName())));
}
if (target.isOnline() && target.getPlayer() != null) {
target.getPlayer().sendRichMessage(Config.NICK_RESET);
}
NickEvent nickEvent = new NickEvent(sender.getName(), target.getName(), null, NickEvent.NickEventType.RESET);
nickEvent.callEvent();
} else if (NickUtilities.validNick(sender, target, nickName)) { } else if (NickUtilities.validNick(sender, target, nickName)) {
setValidNick(sender, target, nickName);
}
}
private void setValidNick(Player sender, OfflinePlayer target, String nickName) {
if (target.isOnline()) { if (target.isOnline()) {
setNick(target.getPlayer(), nickName); setNick(target.getPlayer(), nickName);
} else { } else {
@@ -322,6 +322,30 @@ public class Nicknames implements CommandExecutor, TabCompleter {
Placeholder.unparsed("nickname", getNick(target.getPlayer()))); Placeholder.unparsed("nickname", getNick(target.getPlayer())));
} }
} }
private void handleNickOff(Player sender, OfflinePlayer target) {
try {
if (target.isOnline()) {
resetNick(Objects.requireNonNull(target.getPlayer()));
}
Queries.removePlayerFromDataBase(target.getUniqueId());
NickCache.remove(target.getUniqueId());
nickCacheUpdate.add(target.getUniqueId());
} catch (SQLException e) {
ALogger.error("Failed to remove nickname from database", e);
}
if (!sender.equals(target)) {
sender.sendRichMessage(Config.NICK_RESET_OTHERS,
Placeholder.unparsed("player", Objects.requireNonNull(target.getName())));
}
if (target.isOnline() && target.getPlayer() != null) {
target.getPlayer().sendRichMessage(Config.NICK_RESET);
}
NickEvent nickEvent = new NickEvent(sender.getName(), target.getName(), null, NickEvent.NickEventType.RESET);
nickEvent.callEvent();
} }
private String helpMessage(final CommandSender sender, final HelpType... helpTypes) { private String helpMessage(final CommandSender sender, final HelpType... helpTypes) {
@@ -5,24 +5,22 @@ import com.alttd.chat.config.Config;
import com.alttd.chat.database.Queries; import com.alttd.chat.database.Queries;
import com.alttd.chat.events.NickEvent; import com.alttd.chat.events.NickEvent;
import com.alttd.chat.objects.Nick; import com.alttd.chat.objects.Nick;
import com.alttd.chat.util.ALogger;
import com.alttd.chat.util.Utility; import com.alttd.chat.util.Utility;
import com.alttd.inventory_gui.click.GuiItem;
import com.alttd.inventory_gui.gui.InventoryGui;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike; import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.SkullMeta; import org.bukkit.inventory.meta.SkullMeta;
@@ -31,39 +29,45 @@ import org.bukkit.scheduler.BukkitRunnable;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class NicknamesGui implements Listener { public class NicknamesGui implements Listener {
private final Inventory inv; public static final String UNKNOWN_PLAYER_NAME = "UNKNOWN PLAYER NAME";
private final int currentPage; private final int currentPage;
private final ChatPlugin plugin = ChatPlugin.getInstance();
private final InventoryGui nicknamesGui;
public NicknamesGui() { public NicknamesGui(Player player) {
// Create a new inventory, with no owner (as this isn't a real inventory) nicknamesGui = InventoryGui.builder()
inv = Bukkit.createInventory(null, 36, Utility.parseMiniMessage("Nicknames GUI").asComponent()); .plugin(plugin)
.title(Component.text("Nicknames GUI"))
// Put the items into the inventory .rows(6)
.build();
currentPage = 1; currentPage = 1;
setItems(currentPage); setItems(currentPage, player);
} }
public void setItems(int currentPage) { public void setItems(int currentPage, Player player) {
new BukkitRunnable() { new BukkitRunnable() {
@Override @Override
public void run() { public void run() {
inv.clear();
NickUtilities.updateCache(); NickUtilities.updateCache();
boolean hasNextPage = false; boolean hasNextPage = false;
int i = (currentPage - 1) * 27; //TODO set to 1 or 2 to test int i = (currentPage - 1) * 27; //TODO set to 1 or 2 to test
int limit = i / 27; int limit = i / 27;
MiniMessage miniMessage = MiniMessage.miniMessage();
for (Nick nick : Nicknames.getInstance().NickCache.values()) { for (Nick nick : Nicknames.getInstance().NickCache.values()) {
if (nick.hasRequest()) { if (nick.hasRequest()) {
if (limit >= i / 27) { if (limit >= i / 27) {
inv.setItem(i % 27, createPlayerSkull(nick, Config.NICK_ITEM_LORE)); ItemStack playerSkull = createPlayerSkull(nick, Config.NICK_ITEM_LORE);
nicknamesGui.getRoot().setItem(i % 27, GuiItem.clickable(playerSkull, inventoryClickEvent ->
handleInventoryClick(nick, inventoryClickEvent, miniMessage, playerSkull)));
ALogger.info("Added nick " + i + " to gui: " + nick.getUuid());
i++; i++;
} else { } else {
ALogger.info("Reached end of nicknames gui page");
hasNextPage = true; hasNextPage = true;
break; break;
} }
@@ -71,20 +75,137 @@ public class NicknamesGui implements Listener {
} }
if (currentPage != 1) { if (currentPage != 1) {
inv.setItem(28, createGuiItem(Material.PAPER, "§bPrevious page", ItemStack itemStack = createGuiItem(Material.PAPER, "§bPrevious page",
"§aCurrent page: %page%".replace("%page%", String.valueOf(currentPage)), "§aCurrent page: %page%".replace("%page%", String.valueOf(currentPage)),
"§aPrevious page: %previousPage%".replace("%previousPage%", String.valueOf(currentPage - 1)))); "§aPrevious page: %previousPage%".replace("%previousPage%", String.valueOf(currentPage - 1)));
GuiItem previousPage = GuiItem.clickable(itemStack, e -> setItems(currentPage - 1, player));
nicknamesGui.getRoot().setItem(28, previousPage);
} }
if (hasNextPage) { if (hasNextPage) {
inv.setItem(36, createGuiItem(Material.PAPER, "§bNext page", ItemStack itemStack = createGuiItem(Material.PAPER, "§bNext page",
"§aCurrent page: %page%".replace("%page%", String.valueOf(currentPage)), "§aCurrent page: %page%".replace("%page%", String.valueOf(currentPage)),
"§aNext page: §b%nextPage%".replace("%nextPage%", String.valueOf(currentPage + 1)))); "§aNext page: §b%nextPage%".replace("%nextPage%", String.valueOf(currentPage + 1)));
GuiItem nextPage = GuiItem.clickable(itemStack, e -> setItems(currentPage + 1, player));
nicknamesGui.getRoot().setItem(36, nextPage);
} }
nicknamesGui.render(player);
} }
}.runTaskAsynchronously(ChatPlugin.getInstance()); }.runTaskAsynchronously(ChatPlugin.getInstance());
} }
private void handleInventoryClick(Nick nick, InventoryClickEvent inventoryClickEvent, MiniMessage miniMessage, ItemStack playerSkull) {
final Player playerWhoClicked = (Player) inventoryClickEvent.getWhoClicked();
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(nick.getUuid());
Component offlinePlayerName = miniMessage.deserialize(getOfflinePlayerName(offlinePlayer));
if (!nick.hasRequest()) {
playerWhoClicked.sendRichMessage(Config.NICK_ALREADY_HANDLED,
Placeholder.component("targetplayer", offlinePlayerName));
return;
}
if (inventoryClickEvent.isLeftClick()) {
handleLeftClickPlayerSkull(nick, inventoryClickEvent, offlinePlayer, playerWhoClicked, offlinePlayerName, playerSkull);
} else if (inventoryClickEvent.isRightClick()) {
handleRightClickPlayerSkull(nick, inventoryClickEvent, offlinePlayer, playerWhoClicked, offlinePlayerName, playerSkull);
}
//TODO what do we do no click?
}
private void handleRightClickPlayerSkull(Nick nick, InventoryClickEvent inventoryClickEvent, OfflinePlayer offlinePlayer, Player playerWhoClicked, Component offlinePlayerName, ItemStack playerSkull) {
Queries.denyNewNickname(nick.getUuid());
String newNick = nick.getNewNick();
new BukkitRunnable() {
@Override
public void run() {
NickEvent nickEvent = new NickEvent(inventoryClickEvent.getWhoClicked().getName(), getOfflinePlayerName(offlinePlayer), newNick, NickEvent.NickEventType.DENIED);
nickEvent.callEvent();
}
}.runTask(ChatPlugin.getInstance());
playerWhoClicked.sendRichMessage(Config.NICK_DENIED,
Placeholder.unparsed("targetplayer", getOfflinePlayerName(offlinePlayer)),
Placeholder.component("newnick", Utility.applyColor(nick.getNewNick())),
Placeholder.component("oldnick", Utility.applyColor(nick.getCurrentNick() == null ? getOfflinePlayerName(offlinePlayer) : nick.getCurrentNick())));
if (Nicknames.getInstance().NickCache.containsKey(nick.getUuid())
&& Nicknames.getInstance().NickCache.get(nick.getUuid()).getCurrentNick() != null) {
nick.setNewNick(null);
nick.setRequestedDate(0);
Nicknames.getInstance().NickCache.put(nick.getUuid(), nick);
} else {
Nicknames.getInstance().NickCache.remove(nick.getUuid());
}
if (offlinePlayer.isOnline() && offlinePlayer.getPlayer() != null) {
Nicknames.getInstance().setNick(offlinePlayer.getPlayer(), nick.getCurrentNick() == null ? getOfflinePlayerName(offlinePlayer) : nick.getCurrentNick());
offlinePlayer.getPlayer().sendRichMessage(Config.NICK_NOT_CHANGED);
}
NickUtilities.bungeeMessageHandled(nick.getUuid(), inventoryClickEvent.getWhoClicked().getServer().getPlayer(inventoryClickEvent.getWhoClicked().getName()), "Denied");
final ComponentLike messageDenied = MiniMessage.miniMessage().deserialize("<red><name>'s nickname was denied!",
Placeholder.unparsed("name", getOfflinePlayerName(offlinePlayer)));
ChatPlugin.getInstance().getServer().getOnlinePlayers().stream()
.filter(player -> player.hasPermission("chat.command.nick.review"))
.forEach(player -> player.sendMessage(messageDenied));
ItemStack completedNickRequestItem = createCompletedNickRequestItem(offlinePlayerName, playerSkull);
inventoryClickEvent.getInventory().setItem(inventoryClickEvent.getSlot(), completedNickRequestItem);
nicknamesGui.render(playerWhoClicked);
}
private void handleLeftClickPlayerSkull(Nick nick, InventoryClickEvent inventoryClickEvent, OfflinePlayer offlinePlayer, Player playerWhoClicked, Component offlinePlayerName, ItemStack playerSkull) {
Queries.acceptNewNickname(nick.getUuid(), nick.getNewNick());
String newNick = nick.getNewNick();
new BukkitRunnable() {
@Override
public void run() {
NickEvent nickEvent = new NickEvent(inventoryClickEvent.getWhoClicked().getName(), getOfflinePlayerName(offlinePlayer), newNick, NickEvent.NickEventType.ACCEPTED);
nickEvent.callEvent();
}
}.runTask(ChatPlugin.getInstance());
playerWhoClicked.sendRichMessage(Config.NICK_ACCEPTED,
Placeholder.component("targetplayer", offlinePlayerName),
Placeholder.component("newnick", Utility.applyColor(nick.getNewNick())),
Placeholder.component("oldnick", nick.getCurrentNick() == null ? offlinePlayerName : Utility.applyColor(nick.getCurrentNick())));
Player affectedPlayer = offlinePlayer.getPlayer();
if (offlinePlayer.isOnline() && affectedPlayer != null) {
Nicknames.getInstance().setNick(affectedPlayer, nick.getNewNick());
}
NickUtilities.bungeeMessageHandled(nick.getUuid(), inventoryClickEvent.getWhoClicked().getServer().getPlayer(inventoryClickEvent.getWhoClicked().getName()), "Accepted");
nick.setCurrentNick(nick.getNewNick());
nick.setLastChangedDate(new Date().getTime());
nick.setNewNick(null);
nick.setRequestedDate(0);
Nicknames.getInstance().NickCache.put(nick.getUuid(), nick);
ItemStack completedNickRequestItem = createCompletedNickRequestItem(offlinePlayerName, playerSkull);
inventoryClickEvent.getInventory().setItem(inventoryClickEvent.getSlot(), completedNickRequestItem);
nicknamesGui.render(playerWhoClicked);
}
private static ItemStack createCompletedNickRequestItem(Component offlinePlayerName, ItemStack playerSkull) {
ItemStack itemStack = new ItemStack(Material.SKELETON_SKULL);
ItemMeta itemMeta = itemStack.getItemMeta();
itemMeta.displayName(offlinePlayerName);
itemMeta.lore(playerSkull.lore());
itemStack.setItemMeta(itemMeta);
return itemStack;
}
private static String getOfflinePlayerName(OfflinePlayer offlinePlayer) {
return offlinePlayer.getName() == null ? UNKNOWN_PLAYER_NAME : offlinePlayer.getName();
}
private ItemStack createPlayerSkull(Nick nick, List<String> lore) { private ItemStack createPlayerSkull(Nick nick, List<String> lore) {
MiniMessage miniMessage = MiniMessage.miniMessage(); MiniMessage miniMessage = MiniMessage.miniMessage();
ItemStack playerHead = new ItemStack(Material.PLAYER_HEAD); ItemStack playerHead = new ItemStack(Material.PLAYER_HEAD);
@@ -94,9 +215,9 @@ public class NicknamesGui implements Listener {
meta.setOwningPlayer(offlinePlayer); meta.setOwningPlayer(offlinePlayer);
String name = offlinePlayer.getName(); String name = offlinePlayer.getName();
if (name == null) { if (name == null) {
meta.displayName(miniMessage.deserialize("UNKNOWN PLAYER NAME")); meta.displayName(miniMessage.deserialize("<red>" + getOfflinePlayerName(offlinePlayer) + "</red>"));
} else { } else {
meta.displayName(miniMessage.deserialize(offlinePlayer.getName())); meta.displayName(miniMessage.deserialize(getOfflinePlayerName(offlinePlayer)));
} }
TagResolver resolver = TagResolver.resolver( TagResolver resolver = TagResolver.resolver(
@@ -128,185 +249,6 @@ public class NicknamesGui implements Listener {
// You can open the inventory with this // You can open the inventory with this
public void openInventory(final HumanEntity ent) {//Possibly with a boolean to show if it should get from cache or update cache public void openInventory(final HumanEntity ent) {//Possibly with a boolean to show if it should get from cache or update cache
ent.openInventory(inv); nicknamesGui.open(ent);
}
// Check for clicks on items
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onInventoryClick(InventoryClickEvent e) {
if (e.getInventory() != inv) {
return;
}
e.setCancelled(true);
final ItemStack clickedItem = e.getCurrentItem();
if (clickedItem == null || clickedItem.getType() == Material.AIR) {
return;
}
final Player p = (Player) e.getWhoClicked();
if (clickedItem.getType().equals(Material.PAPER)) {
Component component = clickedItem.getItemMeta().displayName();
if (component == null) {
throw new IllegalStateException("Nicknames GUI: Item with no display name clicked!");
}
String serialize = PlainTextComponentSerializer.plainText().serialize(component);
if (serialize.equals("Next Page")) {
setItems(currentPage + 1);
}
} else if (clickedItem.getType().equals(Material.PLAYER_HEAD)) {
ItemMeta itemMeta = clickedItem.getItemMeta();
if (itemMeta == null) {
return;
}
SkullMeta meta = (SkullMeta) itemMeta;
if (meta.hasEnchants()) {
return;
}
OfflinePlayer owningPlayer = meta.getOwningPlayer();
if (owningPlayer == null) {
p.sendRichMessage(Config.NICK_USER_NOT_FOUND);
return;
}
new BukkitRunnable() {
@Override
public void run() {
NickUtilities.updateCache();
Nick nick;
UUID uniqueId = owningPlayer.getUniqueId();
if (Nicknames.getInstance().NickCache.containsKey(uniqueId)) {
nick = Nicknames.getInstance().NickCache.get(uniqueId);
} else {
nick = Queries.getNick(uniqueId);
}
Component itemDisplayName = itemMeta.displayName();
if (itemDisplayName == null) {
return;
}
if (nick == null || !nick.hasRequest()) {
p.sendRichMessage(Config.NICK_ALREADY_HANDLED,
Placeholder.component("targetplayer", itemDisplayName))
;
return;
}
if (e.isLeftClick()) {
if (owningPlayer.hasPlayedBefore()) {
Queries.acceptNewNickname(uniqueId, nick.getNewNick());
String newNick = nick.getNewNick();
new BukkitRunnable() {
@Override
public void run() {
NickEvent nickEvent = new NickEvent(e.getWhoClicked().getName(), itemMeta.getDisplayName(), newNick, NickEvent.NickEventType.ACCEPTED);
nickEvent.callEvent();
}
}.runTask(ChatPlugin.getInstance());
p.sendRichMessage(Config.NICK_ACCEPTED,
Placeholder.component("targetplayer", itemDisplayName),
Placeholder.component("newnick", Utility.applyColor(nick.getNewNick())),
Placeholder.component("oldnick", Utility.applyColor(nick.getCurrentNick() == null ? itemMeta.getDisplayName() : nick.getCurrentNick())));
if (owningPlayer.isOnline() && owningPlayer.getPlayer() != null) {
Nicknames.getInstance().setNick(owningPlayer.getPlayer(), nick.getNewNick());
}
NickUtilities.bungeeMessageHandled(uniqueId, e.getWhoClicked().getServer().getPlayer(e.getWhoClicked().getName()), "Accepted");
nick.setCurrentNick(nick.getNewNick());
nick.setLastChangedDate(new Date().getTime());
nick.setNewNick(null);
nick.setRequestedDate(0);
Nicknames.getInstance().NickCache.put(uniqueId, nick);
ItemStack itemStack = new ItemStack(Material.SKELETON_SKULL);
ItemMeta itemMeta = itemStack.getItemMeta();
itemMeta.displayName(itemMeta.displayName());
itemMeta.lore(clickedItem.lore());
itemStack.setItemMeta(itemMeta);
e.getInventory().setItem(e.getSlot(), itemStack);
p.updateInventory();
} else {
p.sendRichMessage(Config.NICK_PLAYER_NOT_ONLINE, Placeholder.component("player", itemDisplayName));
}
} else if (e.isRightClick()) {
if (owningPlayer.hasPlayedBefore()) {
Queries.denyNewNickname(uniqueId);
String newNick = nick.getNewNick();
new BukkitRunnable() {
@Override
public void run() {
NickEvent nickEvent = new NickEvent(e.getWhoClicked().getName(), itemMeta.getDisplayName(), newNick, NickEvent.NickEventType.DENIED);
nickEvent.callEvent();
}
}.runTask(ChatPlugin.getInstance());
p.sendMessage(MiniMessage.miniMessage().deserialize(Config.NICK_DENIED,
Placeholder.unparsed("targetplayer", owningPlayer.getName()),
Placeholder.component("newnick", Utility.applyColor(nick.getNewNick())),
Placeholder.component("oldnick", Utility.applyColor(nick.getCurrentNick() == null ? owningPlayer.getName() : nick.getCurrentNick()))));
if (Nicknames.getInstance().NickCache.containsKey(uniqueId)
&& Nicknames.getInstance().NickCache.get(uniqueId).getCurrentNick() != null) {
nick.setNewNick(null);
nick.setRequestedDate(0);
Nicknames.getInstance().NickCache.put(uniqueId, nick);
} else {
Nicknames.getInstance().NickCache.remove(uniqueId);
}
if (owningPlayer.isOnline() && owningPlayer.getPlayer() != null) {
Nicknames.getInstance().setNick(owningPlayer.getPlayer(), nick.getCurrentNick() == null ? owningPlayer.getName() : nick.getCurrentNick());
owningPlayer.getPlayer().sendRichMessage(Config.NICK_NOT_CHANGED);
}
NickUtilities.bungeeMessageHandled(uniqueId, e.getWhoClicked().getServer().getPlayer(e.getWhoClicked().getName()), "Denied");
final ComponentLike messageDenied = MiniMessage.miniMessage().deserialize("<red><name>'s nickname was denied!",
Placeholder.unparsed("name", owningPlayer.getName()));
ChatPlugin.getInstance().getServer().getOnlinePlayers().forEach(p -> {
if (p.hasPermission("chat.command.nick.review")) {
p.sendMessage(messageDenied);
}
});
ItemStack itemStack = new ItemStack(Material.SKELETON_SKULL);
ItemMeta itemMeta = itemStack.getItemMeta();
itemMeta.displayName(itemDisplayName);
itemMeta.lore(clickedItem.lore());
itemStack.setItemMeta(itemMeta);
e.getInventory().setItem(e.getSlot(), itemStack);
p.updateInventory();
} else {
if (itemDisplayName == null) {
p.sendRichMessage(Config.NICK_PLAYER_NOT_ONLINE, Placeholder.parsed("player", "UNKNOWN PLAYER NAME"));
} else {
p.sendRichMessage(Config.NICK_PLAYER_NOT_ONLINE, Placeholder.component("player", itemDisplayName));
}
}
}
}
}.runTaskAsynchronously(ChatPlugin.getInstance());
}
}
// Cancel dragging in our inventory
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
public void onInventoryClick(InventoryDragEvent e) {
if (e.getInventory() == inv) {
e.setCancelled(true);
}
} }
} }
+4 -3
View File
@@ -9,13 +9,12 @@ val nexusPass = providers.gradleProperty("alttdSnapshotPassword").get()
dependencyResolutionManagement { dependencyResolutionManagement {
repositories { repositories {
// mavenLocal() mavenLocal()
mavenCentral() mavenCentral()
maven("https://repo.alttd.com/snapshots") // Altitude - Galaxy maven("https://repo.alttd.com/snapshots") // Altitude - Galaxy
maven("https://oss.sonatype.org/content/groups/public/") // Adventure maven("https://oss.sonatype.org/content/groups/public/") // Adventure
maven("https://oss.sonatype.org/content/repositories/snapshots/") // Minimessage maven("https://oss.sonatype.org/content/repositories/snapshots/") // Minimessage
maven("https://nexus.velocitypowered.com/repository/") // Velocity maven("https://repo.papermc.io/repository/maven-public/")
maven("https://nexus.velocitypowered.com/repository/maven-public/") // Velocity
maven("https://repo.spongepowered.org/maven") // Configurate maven("https://repo.spongepowered.org/maven") // Configurate
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") // Papi maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") // Papi
maven("https://jitpack.io") maven("https://jitpack.io")
@@ -36,3 +35,5 @@ pluginManagement {
gradlePluginPortal() gradlePluginPortal()
} }
} }
include("web-api")
+4 -2
View File
@@ -5,8 +5,10 @@ plugins {
dependencies { dependencies {
implementation(project(":api")) // API implementation(project(":api")) // API
compileOnly("com.velocitypowered:velocity-api:3.2.0-SNAPSHOT") compileOnly("org.projectlombok:lombok:1.18.46")
annotationProcessor("com.velocitypowered:velocity-api:3.2.0-SNAPSHOT") annotationProcessor("org.projectlombok:lombok:1.18.46")
compileOnly("com.velocitypowered:velocity-api:3.5.0-SNAPSHOT")
annotationProcessor("com.velocitypowered:velocity-api:3.5.0-SNAPSHOT")
implementation("mysql:mysql-connector-java:8.0.33") // mysql implementation("mysql:mysql-connector-java:8.0.33") // mysql
implementation("org.spongepowered", "configurate-yaml", "4.2.0") implementation("org.spongepowered", "configurate-yaml", "4.2.0")
compileOnly("net.kyori:adventure-text-minimessage:4.23.0") compileOnly("net.kyori:adventure-text-minimessage:4.23.0")
@@ -2,20 +2,20 @@ package com.alttd.velocitychat;
import com.alttd.chat.ChatAPI; import com.alttd.chat.ChatAPI;
import com.alttd.chat.ChatImplementation; import com.alttd.chat.ChatImplementation;
import com.alttd.chat.config.Config;
import com.alttd.chat.database.DatabaseConnection;
import com.alttd.chat.managers.ChatUserManager; import com.alttd.chat.managers.ChatUserManager;
import com.alttd.chat.managers.PartyManager; import com.alttd.chat.managers.PartyManager;
import com.alttd.chat.objects.ChatUser; import com.alttd.chat.objects.ChatUser;
import com.alttd.chat.objects.chat_log.ChatLogHandler; import com.alttd.chat.objects.chat_log.ChatLogHandler;
import com.alttd.chat.util.ALogger;
import com.alttd.velocitychat.commands.*; import com.alttd.velocitychat.commands.*;
import com.alttd.chat.config.Config;
import com.alttd.chat.database.DatabaseConnection;
import com.alttd.velocitychat.handlers.ChatHandler; import com.alttd.velocitychat.handlers.ChatHandler;
import com.alttd.velocitychat.handlers.ServerHandler; import com.alttd.velocitychat.handlers.ServerHandler;
import com.alttd.velocitychat.listeners.ChatListener; import com.alttd.velocitychat.listeners.ChatListener;
import com.alttd.velocitychat.listeners.LiteBansListener; import com.alttd.velocitychat.listeners.LiteBansListener;
import com.alttd.velocitychat.listeners.ProxyPlayerListener;
import com.alttd.velocitychat.listeners.PluginMessageListener; import com.alttd.velocitychat.listeners.PluginMessageListener;
import com.alttd.chat.util.ALogger; import com.alttd.velocitychat.listeners.ProxyPlayerListener;
import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import com.google.inject.Inject; import com.google.inject.Inject;
@@ -67,7 +67,8 @@ public class VelocityChat {
PartyManager.initialize(); // load the parties from the db and add the previously loaded users to them PartyManager.initialize(); // load the parties from the db and add the previously loaded users to them
serverHandler = new ServerHandler(); serverHandler = new ServerHandler();
chatHandler = new ChatHandler(); ChatLogHandler chatLogHandler = new ChatLogHandler(true);
chatHandler = new ChatHandler(chatLogHandler);
server.getEventManager().register(this, new ChatListener()); server.getEventManager().register(this, new ChatListener());
server.getEventManager().register(this, new ProxyPlayerListener()); server.getEventManager().register(this, new ProxyPlayerListener());
new LiteBansListener().init(); // init the litebans api listeners new LiteBansListener().init(); // init the litebans api listeners
@@ -89,7 +90,11 @@ public class VelocityChat {
ByteArrayDataOutput buf = ByteStreams.newDataOutput(); ByteArrayDataOutput buf = ByteStreams.newDataOutput();
buf.writeUTF("reloadconfig"); buf.writeUTF("reloadconfig");
ALogger.info("Reloaded ChatPlugin proxy config."); ALogger.info("Reloaded ChatPlugin proxy config.");
getProxy().getAllServers().stream().forEach(registeredServer -> registeredServer.sendPluginMessage(getChannelIdentifier(), buf.toByteArray())); getProxy().getAllServers()
.stream()
.forEach(registeredServer -> registeredServer.sendPluginMessage(getChannelIdentifier(),
buf.toByteArray()
));
} }
public File getDataDirectory() { public File getDataDirectory() {
@@ -6,6 +6,8 @@ import com.alttd.chat.managers.ChatUserManager;
import com.alttd.chat.managers.PartyManager; import com.alttd.chat.managers.PartyManager;
import com.alttd.chat.managers.RegexManager; import com.alttd.chat.managers.RegexManager;
import com.alttd.chat.objects.*; import com.alttd.chat.objects.*;
import com.alttd.chat.objects.chat_log.ChatLogHandler;
import com.alttd.chat.objects.chat_log.ChatLogType;
import com.alttd.chat.util.ALogger; import com.alttd.chat.util.ALogger;
import com.alttd.chat.util.Utility; import com.alttd.chat.util.Utility;
import com.alttd.velocitychat.VelocityChat; import com.alttd.velocitychat.VelocityChat;
@@ -32,6 +34,12 @@ import java.util.UUID;
public class ChatHandler { public class ChatHandler {
private final ChatLogHandler chatLogHandler;
public ChatHandler(ChatLogHandler chatLogHandler) {
this.chatLogHandler = chatLogHandler;
}
public void privateMessage(String sender, String target, String message) { public void privateMessage(String sender, String target, String message) {
UUID uuid = UUID.fromString(sender); UUID uuid = UUID.fromString(sender);
ChatUser senderUser = ChatUserManager.getChatUser(uuid); ChatUser senderUser = ChatUserManager.getChatUser(uuid);
@@ -54,15 +62,23 @@ public class ChatHandler {
Placeholder.component("receiver", targetUser.getDisplayName()), Placeholder.component("receiver", targetUser.getDisplayName()),
Placeholder.unparsed("receivername", player2.getUsername()), Placeholder.unparsed("receivername", player2.getUsername()),
Placeholder.component("message", GsonComponentSerializer.gson().deserialize(message)), Placeholder.component("message", GsonComponentSerializer.gson().deserialize(message)),
Placeholder.unparsed("server", player.getCurrentServer().isPresent() ? player.getCurrentServer().get().getServerInfo().getName() : "Altitude")); Placeholder.unparsed("server",
player.getCurrentServer().isPresent() ? player.getCurrentServer()
.get()
.getServerInfo()
.getName() : "Altitude"
)
);
ServerConnection serverConnection; ServerConnection serverConnection;
//Log handled on receiving server since it is only received on one server and that's where the ignore check happens
if (player.getCurrentServer().isPresent() && player2.getCurrentServer().isPresent()) { if (player.getCurrentServer().isPresent() && player2.getCurrentServer().isPresent()) {
// redirect to the sender // redirect to the sender
serverConnection = player.getCurrentServer().get(); serverConnection = player.getCurrentServer().get();
Component component = Utility.parseMiniMessage(Config.MESSAGESENDER Component component = Utility.parseMiniMessage(Config.MESSAGESENDER
.replaceAll("<sendername>", player.getUsername()) .replaceAll("<sendername>", player.getUsername())
.replaceAll("<receivername>", player2.getUsername()), Placeholders).asComponent(); .replaceAll("<receivername>", player2.getUsername()), Placeholders
).asComponent();
ByteArrayDataOutput buf = ByteStreams.newDataOutput(); ByteArrayDataOutput buf = ByteStreams.newDataOutput();
buf.writeUTF("privatemessageout"); buf.writeUTF("privatemessageout");
buf.writeUTF(player.getUniqueId().toString()); buf.writeUTF(player.getUniqueId().toString());
@@ -75,7 +91,8 @@ public class ChatHandler {
serverConnection = player2.getCurrentServer().get(); serverConnection = player2.getCurrentServer().get();
component = Utility.parseMiniMessage(Config.MESSAGERECIEVER component = Utility.parseMiniMessage(Config.MESSAGERECIEVER
.replaceAll("<sendername>", player.getUsername()) .replaceAll("<sendername>", player.getUsername())
.replaceAll("<receivername>", player2.getUsername()), Placeholders).asComponent(); .replaceAll("<receivername>", player2.getUsername()), Placeholders
).asComponent();
buf = ByteStreams.newDataOutput(); buf = ByteStreams.newDataOutput();
buf.writeUTF("privatemessagein"); buf.writeUTF("privatemessagein");
buf.writeUTF(player2.getUniqueId().toString()); buf.writeUTF(player2.getUniqueId().toString());
@@ -134,7 +151,7 @@ public class ChatHandler {
} }
ComponentLike senderName = user.getDisplayName(); ComponentLike senderName = user.getDisplayName();
TagResolver Placeholders = TagResolver.resolver( TagResolver placeholders = TagResolver.resolver(
Placeholder.component("sender", senderName), Placeholder.component("sender", senderName),
Placeholder.component("sendername", senderName), Placeholder.component("sendername", senderName),
Placeholder.unparsed("partyname", party.getPartyName()), Placeholder.unparsed("partyname", party.getPartyName()),
@@ -142,7 +159,7 @@ public class ChatHandler {
Placeholder.unparsed("server", serverConnection.getServer().getServerInfo().getName()) Placeholder.unparsed("server", serverConnection.getServer().getServerInfo().getName())
); );
Component partyMessage = Utility.parseMiniMessage(Config.PARTY_FORMAT, Placeholders).asComponent() Component partyMessage = Utility.parseMiniMessage(Config.PARTY_FORMAT, placeholders).asComponent()
.replaceText(TextReplacementConfig.builder().once().matchLiteral("[i]").replacement(item).build()); .replaceText(TextReplacementConfig.builder().once().matchLiteral("[i]").replacement(item).build());
ModifiableString modifiableString = new ModifiableString(partyMessage); ModifiableString modifiableString = new ModifiableString(partyMessage);
@@ -155,7 +172,17 @@ public class ChatHandler {
sendPartyMessage(party, partyMessage, user.getIgnoredBy()); sendPartyMessage(party, partyMessage, user.getIgnoredBy());
ComponentLike spyMessage = Utility.parseMiniMessage(Config.PARTY_SPY, Placeholders); chatLogHandler.addChatLog(uuid,
serverConnection.getServer().getServerInfo().getName(),
PlainTextComponentSerializer.plainText().serialize(partyMessage),
ChatLogType.PARTY,
String.valueOf(party.getPartyId()),
null,
partyMessage,
false
);
ComponentLike spyMessage = Utility.parseMiniMessage(Config.PARTY_SPY, placeholders);
for (Player pl : serverConnection.getServer().getPlayersConnected()) { for (Player pl : serverConnection.getServer().getPlayersConnected()) {
if (pl.hasPermission(Config.SPYPERMISSION) && !party.getPartyUsersUuid().contains(pl.getUniqueId())) { if (pl.hasPermission(Config.SPYPERMISSION) && !party.getPartyUsersUuid().contains(pl.getUniqueId())) {
pl.sendMessage(spyMessage); pl.sendMessage(spyMessage);
@@ -171,6 +198,17 @@ public class ChatHandler {
.stream() .stream()
.filter(target -> target.hasPermission("command.chat.globaladminchat")) .filter(target -> target.hasPermission("command.chat.globaladminchat"))
.forEach(target -> target.sendMessage(component)); .forEach(target -> target.sendMessage(component));
chatLogHandler.addChatLog(
new UUID(0, 0), //No origin uuid for GAC
"GAC", //No server for GAC
PlainTextComponentSerializer.plainText().serialize(component),
ChatLogType.GAC,
null,
null,
component,
false
);
} }
public void globalAdminChat(CommandSource commandSource, String message) { public void globalAdminChat(CommandSource commandSource, String message) {
@@ -182,13 +220,17 @@ public class ChatHandler {
return; return;
} }
senderName = user.getDisplayName(); senderName = user.getDisplayName();
serverName = sender.getCurrentServer().isPresent() ? sender.getCurrentServer().get().getServerInfo().getName() : "Altitude"; serverName = sender.getCurrentServer().isPresent() ? sender.getCurrentServer()
.get()
.getServerInfo()
.getName() : "Altitude";
} }
TagResolver Placeholders = TagResolver.resolver( TagResolver Placeholders = TagResolver.resolver(
Placeholder.component("message", parseMessageContent(commandSource, message)), Placeholder.component("message", parseMessageContent(commandSource, message)),
Placeholder.component("sender", senderName), Placeholder.component("sender", senderName),
Placeholder.unparsed("server", serverName)); Placeholder.unparsed("server", serverName)
);
ComponentLike component = Utility.parseMiniMessage(Config.GACFORMAT, Placeholders); ComponentLike component = Utility.parseMiniMessage(Config.GACFORMAT, Placeholders);
@@ -210,7 +252,8 @@ public class ChatHandler {
if (optionalPlayer.isEmpty()) { if (optionalPlayer.isEmpty()) {
targetUUID = ServerHandler.getPlayerUUID(recipient); targetUUID = ServerHandler.getPlayerUUID(recipient);
if (targetUUID == null) { if (targetUUID == null) {
commandSource.sendMessage(Utility.parseMiniMessage("<red>A player with this name hasn't logged in recently.")); // TOOD load from config commandSource.sendMessage(Utility.parseMiniMessage(
"<red>A player with this name hasn't logged in recently.")); // TOOD load from config
return; return;
} }
} else { } else {
@@ -274,7 +317,9 @@ public class ChatHandler {
Placeholder.component("sender", chatUser.getDisplayName()), Placeholder.component("sender", chatUser.getDisplayName()),
Placeholder.component("message", Utility.parseMiniMessage(mail.getMessage())), Placeholder.component("message", Utility.parseMiniMessage(mail.getMessage())),
Placeholder.unparsed("date", date.toString()), Placeholder.unparsed("date", date.toString()),
Placeholder.unparsed("time_ago", getTimeAgo(Duration.between(date.toInstant(), new Date().toInstant()))) Placeholder.unparsed("time_ago",
getTimeAgo(Duration.between(date.toInstant(), new Date().toInstant()))
)
); );
ComponentLike mailMessage = Utility.parseMiniMessage(Config.mailBody, Placeholders); ComponentLike mailMessage = Utility.parseMiniMessage(Config.mailBody, Placeholders);
component = component.append(Component.newline()).append(mailMessage); component = component.append(Component.newline()).append(mailMessage);
@@ -5,7 +5,6 @@ import com.alttd.chat.util.Utility;
import com.alttd.velocitychat.VelocityChat; import com.alttd.velocitychat.VelocityChat;
import com.alttd.velocitychat.events.GlobalAdminChatEvent; import com.alttd.velocitychat.events.GlobalAdminChatEvent;
import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.event.PostOrder;
import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.Player;
import net.kyori.adventure.text.ComponentLike; import net.kyori.adventure.text.ComponentLike;
@@ -19,7 +18,7 @@ public class ChatListener {
plugin = VelocityChat.getPlugin(); plugin = VelocityChat.getPlugin();
} }
@Subscribe(order = PostOrder.FIRST) @Subscribe(priority = 0)
public void onGlobalStaffChat(GlobalAdminChatEvent event) { public void onGlobalStaffChat(GlobalAdminChatEvent event) {
String senderName = Config.CONSOLENAME; String senderName = Config.CONSOLENAME;
String serverName = "Altitude"; String serverName = "Altitude";
@@ -11,7 +11,6 @@ import com.alttd.velocitychat.VelocityChat;
import com.alttd.velocitychat.commands.vote_to_mute.ActiveVoteToMute; import com.alttd.velocitychat.commands.vote_to_mute.ActiveVoteToMute;
import com.alttd.velocitychat.data.ServerWrapper; import com.alttd.velocitychat.data.ServerWrapper;
import com.alttd.velocitychat.handlers.ServerHandler; import com.alttd.velocitychat.handlers.ServerHandler;
import com.velocitypowered.api.event.PostOrder;
import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.connection.DisconnectEvent; import com.velocitypowered.api.event.connection.DisconnectEvent;
import com.velocitypowered.api.event.connection.LoginEvent; import com.velocitypowered.api.event.connection.LoginEvent;
@@ -34,7 +33,7 @@ import java.util.concurrent.TimeUnit;
public class ProxyPlayerListener { public class ProxyPlayerListener {
@Subscribe(order = PostOrder.FIRST) @Subscribe(priority = 0)
public void onPlayerLogin(LoginEvent event) { public void onPlayerLogin(LoginEvent event) {
Player player = event.getPlayer(); Player player = event.getPlayer();
UUID uuid = player.getUniqueId(); UUID uuid = player.getUniqueId();
@@ -57,7 +56,7 @@ public class ProxyPlayerListener {
ServerHandler.addPlayerUUID(player.getUsername(), uuid); ServerHandler.addPlayerUUID(player.getUsername(), uuid);
} }
@Subscribe(order = PostOrder.LAST) @Subscribe(priority = 4)
public void afterPlayerLogin(ServerPostConnectEvent event) { public void afterPlayerLogin(ServerPostConnectEvent event) {
Player player = event.getPlayer(); Player player = event.getPlayer();
RegisteredServer previousServer = event.getPreviousServer(); RegisteredServer previousServer = event.getPreviousServer();
+66
View File
@@ -0,0 +1,66 @@
plugins {
java
id("org.openapi.generator") version "7.12.0"
}
group = "com.alttd.chat"
version = "2.0.0-SNAPSHOT"
dependencies {
implementation("io.swagger.core.v3:swagger-annotations:2.2.28")
implementation("com.squareup.okhttp3:okhttp:4.12.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
implementation("com.google.code.gson:gson:2.12.1")
implementation("io.gsonfire:gson-fire:1.9.0")
implementation("javax.annotation:javax.annotation-api:1.3.2")
implementation("jakarta.annotation:jakarta.annotation-api:2.1.1")
}
sourceSets {
main {
java {
srcDir("${projectDir}/build/generated-sources/client/src/main/java")
}
}
}
tasks.named<org.openapitools.generator.gradle.plugin.tasks.GenerateTask>("openApiGenerate") {
generatorName.set("java")
inputSpec.set("${projectDir}/src/main/resources/chat-api.yml")
configFile.set("${projectDir}/src/main/resources/config_backend.json")
outputDir.set("${projectDir}/build/generated-sources/client")
apiPackage.set("com.alttd.altitudeweb.api")
modelPackage.set("com.alttd.altitudeweb.model")
modelNameSuffix.set("Dto")
additionalProperties.set(
mapOf(
"dateLibrary" to "java8",
"library" to "okhttp-gson"
)
)
generateApiTests.set(false)
generateApiDocumentation.set(false)
generateModelTests.set(false)
generateModelDocumentation.set(false)
generateAliasAsModel.set(true)
}
sourceSets {
main {
java {
srcDir(layout.buildDirectory.dir("generated/openapi/src/main/java"))
}
}
}
tasks.compileJava {
dependsOn("openApiGenerate")
}
+144
View File
@@ -0,0 +1,144 @@
openapi: 3.0.3
info:
title: Minecraft Network API
version: 1.0.0
servers:
- url: http://localhost:8080/api
tags:
- name: chat
description: Data for displaying Chat messages to clients
paths:
/chat/send/chat/message:
post:
tags:
- chat
summary: Sends one or more chat messages
operationId: sendChatMessages
requestBody:
required: true
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/ChatMessage"
responses:
"202":
description: Accepted
/chat/send/servers/state:
post:
tags:
- chat
summary: Update server states
operationId: updateServerStates
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/ServerState"
responses:
"202":
description: Accepted
components:
schemas:
User:
type: object
required:
- uuid
- name
- styledName
# TODO [Stijn] [2026-07-19]: Add who they ignore and who they are ignored by
properties:
uuid:
type: string
format: uuid
name:
type: string
styledName:
type: string
ChatMessage:
type: object
required:
- uuid
- message
- server
- type
- timestamp
- blocked
properties:
uuid:
type: string
format: uuid
message:
type: string
server:
type: string
type:
type: string
enum:
- PUBLIC
- GLOBAL
- PARTY
- GAC
- MSG
- CUSTOM
channel:
type: string
maxLength: 36
receiver:
type: string
maxLength: 36
timestamp:
type: string
format: date-time
blocked:
type: boolean
PrivateMessage:
type: object
required:
- sender
- receiver
- message
- senderServer
- receiverServer
properties:
sender:
type: string
format: uuid
receiver:
type: string
format: uuid
message:
type: string
senderServer:
type: string
receiverServer:
type: string
Server:
type: object
properties:
name:
type: string
players:
type: array
items:
$ref: "#/components/schemas/User"
ServerState:
type: object
properties:
servers:
type: array
items:
$ref: "#/components/schemas/Server"
@@ -0,0 +1,22 @@
{
"library": "okhttp-gson",
"hideGenerationTimestamp": true,
"modelPackage": "com.alttd.altitudeweb.model",
"apiPackage": "com.alttd.altitudeweb.api",
"invokerPackage": "com.alttd.altitudeweb.invoker",
"serializableModel": true,
"openApiNullable": false,
"useTags": true,
"generateApis": true,
"generateApiTests": false,
"generateApiDocumentation": false,
"generateModels": true,
"generateModelTests": false,
"generateSupportingFiles": true,
"modelNameSuffix": "Dto",
"generateTests": false,
"dateLibrary": "java8",
"enumUnknownDefaultCase": true,
"disallowAdditionalPropertiesIfNotPresent": false,
"useJakartaEe": true
}