Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions src/main/java/xyz/earthcow/networkjoinmessages/common/Core.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,18 @@ public Core(CorePlugin plugin, PremiumVanish premiumVanish) {
ReceiverResolver receiverResolver = new ReceiverResolver(plugin, config, sayanVanishHook != null, premiumVanish != null);
MessageHandler messageHandler = new MessageHandler(plugin, config, stateStore, placeholderResolver, receiverResolver);

// Discord webhook builder, early for leave message cache usage
DiscordWebhookBuilder webhookBuilder = new DiscordWebhookBuilder(plugin, configManager.getDiscordConfig());

// Player event helpers
SilenceChecker silenceChecker = new SilenceChecker(plugin, config, stateStore, sayanVanishHook, premiumVanish);
LeaveMessageCache leaveMessageCache = new LeaveMessageCache(plugin, config, messageFormatter, placeholderResolver);
LeaveMessageCache leaveMessageCache = new LeaveMessageCache(
plugin, config, messageFormatter, placeholderResolver, webhookBuilder, configManager.getDiscordConfig());
LeaveJoinBufferManager leaveJoinBuffer = new LeaveJoinBufferManager(plugin, config);

// Discord integration
DiscordWebhookBuilder webhookBuilder = new DiscordWebhookBuilder(plugin, configManager.getDiscordConfig());
DiscordIntegration discordIntegration = new DiscordIntegration(plugin, placeholderResolver, messageFormatter, webhookBuilder, configManager.getDiscordConfig());
DiscordIntegration discordIntegration = new DiscordIntegration(
plugin, placeholderResolver, messageFormatter, webhookBuilder, configManager.getDiscordConfig());

// Spoof
SpoofManager spoofManager = new SpoofManager(plugin, config, messageHandler, messageFormatter, placeholderResolver);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public abstract class CorePlayer implements CoreCommandSender {
@Getter(AccessLevel.NONE) @Setter(AccessLevel.NONE)
private AtomicBoolean disconnecting = new AtomicBoolean(false);
private String cachedLeaveMessage;
private String cachedDiscordLeavePayload;
private Audience audience;
private boolean premiumVanishHidden = false;
private int premiumVanishUseLevel = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import xyz.earthcow.networkjoinmessages.common.events.NetworkJoinEvent;
import xyz.earthcow.networkjoinmessages.common.events.NetworkLeaveEvent;
import xyz.earthcow.networkjoinmessages.common.events.SwapServerEvent;
import xyz.earthcow.networkjoinmessages.common.util.Formatter;
Comment thread
EarthCow marked this conversation as resolved.
import xyz.earthcow.networkjoinmessages.common.util.PlaceholderResolver;

import java.io.FileNotFoundException;
Expand Down Expand Up @@ -71,7 +72,7 @@ public void onSwapServer(SwapServerEvent event) {
DiscordWebhook webhook = webhookBuilder.buildSwapWebhook(webhookUrl);
if (webhook == null) return;

String avatarUrl = resolveAvatarUrl(player);
String avatarUrl = webhookBuilder.resolveAvatarUrl(player);
String preparedJson = messageFormatter.prepareDiscordSwapTemplate(
webhook.getJsonString(), player, event.serverFrom(), event.serverTo(), avatarUrl);
executeWebhook(webhook, preparedJson, player);
Expand All @@ -84,7 +85,7 @@ public void onNetworkJoin(NetworkJoinEvent event) {
DiscordWebhook webhook = webhookBuilder.buildJoinWebhook(webhookUrl, key);
if (webhook == null) return;

String avatarUrl = resolveAvatarUrl(player);
String avatarUrl = webhookBuilder.resolveAvatarUrl(player);
String preparedJson = messageFormatter.prepareDiscordJoinLeaveTemplate(
webhook.getJsonString(), player, false, avatarUrl);
executeWebhook(webhook, preparedJson, player);
Expand All @@ -96,10 +97,13 @@ public void onNetworkLeave(NetworkLeaveEvent event) {
DiscordWebhook webhook = webhookBuilder.buildLeaveWebhook(webhookUrl);
if (webhook == null) return;

String avatarUrl = resolveAvatarUrl(player);
String preparedJson = messageFormatter.prepareDiscordJoinLeaveTemplate(
webhook.getJsonString(), player, true, avatarUrl);
executeWebhook(webhook, preparedJson, player);
String cachedPayload = player.getCachedDiscordLeavePayload();
if (cachedPayload == null) {
plugin.getCoreLogger().warn(
"No cached Discord leave payload for " + player.getName() + ", skipping webhook.");
return;
}
sendWebhook(webhook, cachedPayload);
}

// --- Webhook execution ---
Expand All @@ -109,27 +113,21 @@ public void onNetworkLeave(NetworkLeaveEvent event) {
* string, then executes the webhook asynchronously.
*/
private void executeWebhook(DiscordWebhook webhook, String preparedJson, CorePlayer parseTarget) {
placeholderResolver.resolve(preparedJson, parseTarget, fullyResolved ->
plugin.runTaskAsync(() -> {
try {
webhook.execute(fullyResolved);
} catch (IOException e) {
plugin.getCoreLogger().severe("[DiscordIntegration] " + describeHttpError(e));
plugin.getCoreLogger().debug("Exception: " + e);
plugin.getCoreLogger().debug("Webhook payload: " + preparedJson);
}
})
);
placeholderResolver.resolve(preparedJson, parseTarget, fullyResolved -> sendWebhook(webhook, fullyResolved));
}

/**
* Resolves the avatar URL template from config, substituting {@code %uuid%} and
* {@code %player%} for the given player.
*/
private String resolveAvatarUrl(CorePlayer player) {
return discordConfig.getString("EmbedAvatarUrl")
.replace("%uuid%", player.getUniqueId().toString())
.replace("%player%", player.getName());
/** Sends an already fully-resolved JSON payload to Discord asynchronously. */
private void sendWebhook(DiscordWebhook webhook, String fullyResolved) {
plugin.runTaskAsync(() -> {
String cleanFullyResolved = Formatter.sanitize(fullyResolved);
try {
webhook.execute(cleanFullyResolved);
} catch (IOException e) {
plugin.getCoreLogger().severe("[DiscordIntegration] " + describeHttpError(e));
plugin.getCoreLogger().debug("Exception: " + e);
plugin.getCoreLogger().debug("Webhook payload: " + cleanFullyResolved);
}
});
}

/** Produces a human-readable error description for a failed webhook HTTP request. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import dev.dejvokep.boostedyaml.YamlDocument;
import org.jetbrains.annotations.Nullable;
import xyz.earthcow.discordwebhook.DiscordWebhook;
import xyz.earthcow.networkjoinmessages.common.abstraction.CorePlayer;
import xyz.earthcow.networkjoinmessages.common.abstraction.CorePlugin;

import java.awt.*;
Expand Down Expand Up @@ -54,6 +55,16 @@ public DiscordWebhook buildLeaveWebhook(String webhookUrl) {
return buildWebhook(webhookUrl, "Messages.LeaveNetwork");
}

/**
* Resolves the avatar URL template from config, substituting {@code %uuid%} and
* {@code %player%} for the given player.
*/
public String resolveAvatarUrl(CorePlayer player) {
return discordConfig.getString("EmbedAvatarUrl")
.replace("%uuid%", player.getUniqueId().toString())
.replace("%player%", player.getName());
}

// --- Internal builder ---

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,36 @@
package xyz.earthcow.networkjoinmessages.common.player;

import dev.dejvokep.boostedyaml.YamlDocument;
import xyz.earthcow.discordwebhook.DiscordWebhook;
import xyz.earthcow.networkjoinmessages.common.abstraction.CorePlayer;
import xyz.earthcow.networkjoinmessages.common.abstraction.CorePlugin;
import xyz.earthcow.networkjoinmessages.common.broadcast.MessageFormatter;
import xyz.earthcow.networkjoinmessages.common.config.PluginConfig;
import xyz.earthcow.networkjoinmessages.common.modules.DiscordWebhookBuilder;
import xyz.earthcow.networkjoinmessages.common.util.PlaceholderResolver;

import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
* Pre-computes and caches the formatted leave message for each online player.
* Pre-computes and caches the formatted leave message for each online player, both for the
* in-game leave message and the Discord leave webhook payload.
*
* <p>Because a player's leave message cannot be computed after disconnect (no live
* placeholder data), it is formatted periodically while the player is connected and
* stored on the player object, ready to be sent immediately on disconnect.
* stored on the player object, ready to be sent immediately on disconnect. Both caches
* are refreshed together on the same timer so they can never fall out of sync with
* each other.
*/
public final class LeaveMessageCache {

private final CorePlugin plugin;
private final PluginConfig config;
private final MessageFormatter messageFormatter;
private final PlaceholderResolver placeholderResolver;
private final DiscordWebhookBuilder discordWebhookBuilder;
private final YamlDocument discordConfig;

/** Maps player UUID -> repeating cache-refresh task ID */
private final Map<UUID, Integer> refreshTasks = new ConcurrentHashMap<>();
Expand All @@ -31,12 +39,16 @@ public LeaveMessageCache(
CorePlugin plugin,
PluginConfig config,
MessageFormatter messageFormatter,
PlaceholderResolver placeholderResolver
PlaceholderResolver placeholderResolver,
DiscordWebhookBuilder discordWebhookBuilder,
YamlDocument discordConfig
) {
this.plugin = plugin;
this.config = config;
this.messageFormatter = messageFormatter;
this.placeholderResolver = placeholderResolver;
this.discordWebhookBuilder = discordWebhookBuilder;
this.discordConfig = discordConfig;
}

/** Starts cache-refresh tasks for all currently online players. Called on reload. */
Expand Down Expand Up @@ -67,8 +79,25 @@ public void stopFor(CorePlayer player) {

/** Forces an immediate refresh of the cached leave message for the given player. */
public void refresh(CorePlayer player) {
plugin.getCoreLogger().debug("Refreshing cached leave message for " + player.getName());
String template = messageFormatter.formatLeaveMessage(player);
placeholderResolver.resolve(template, player, player::setCachedLeaveMessage);
refreshDiscordPayload(player);
}

private void refreshDiscordPayload(CorePlayer player) {
if (!discordConfig.getBoolean("Enabled")) {
player.setCachedDiscordLeavePayload(null);
return;
}
String webhookUrl = discordConfig.getString("WebhookUrl");
DiscordWebhook webhook = discordWebhookBuilder.buildLeaveWebhook(webhookUrl);
if (webhook == null) {
player.setCachedDiscordLeavePayload(null);
return;
}
String avatarUrl = discordWebhookBuilder.resolveAvatarUrl(player);
String preparedJson = messageFormatter.prepareDiscordJoinLeaveTemplate(
webhook.getJsonString(), player, true, avatarUrl);
placeholderResolver.resolve(preparedJson, player, player::setCachedDiscordLeavePayload);
}
}
Loading