Compare commits

..

1 Commits

9 changed files with 116 additions and 31 deletions

View File

@@ -1,5 +1,5 @@
# Project # Project
projectVersion=1.0.0 projectVersion=1.0.4
projectGroup=com.nickcore projectGroup=com.nickcore
# Minecraft / Paper # Minecraft / Paper

View File

@@ -302,9 +302,11 @@ public final class NickCorePlugin extends JavaPlugin {
private void registerVelocityMessaging(NickCoreConfig config) { private void registerVelocityMessaging(NickCoreConfig config) {
if (config.velocity().enabled()) { if (config.velocity().enabled()) {
velocityMessaging = new VelocityMessaging(this, profileCache, velocityMessaging = new VelocityMessaging(this, profileCache,
placeholderService, nametagService, scheduler, placeholderService, nametagService, nameOverrideService,
getLogger(), config.velocity().serverName()); scheduler, getLogger(), config.velocity().serverName());
velocityMessaging.register(); velocityMessaging.register();
// Let NickService auto-sync changes to other servers
nickService.setVelocityMessaging(velocityMessaging);
getLogger().info("Velocity messaging enabled (server: " + config.velocity().serverName() + ")"); getLogger().info("Velocity messaging enabled (server: " + config.velocity().serverName() + ")");
} }
} }

View File

@@ -172,13 +172,12 @@ public final class NickCommand implements CommandExecutor, TabCompleter {
sendMessage(player, "<red>You don't have permission for that rank."); sendMessage(player, "<red>You don't have permission for that rank.");
return; return;
} }
NickProfile profile = profileCache.get(player.getUniqueId()) // Save rank to DB and sync to Velocity
.orElse(NickProfile.empty(player.getUniqueId())); nickService.saveRankChange(player.getUniqueId(), rankId).thenRun(() -> {
NickProfile updated = profile.withRank(rankId); sendMessage(player, "<green>Rank set to <white>" +
profileCache.put(updated); rankService.getRank(rankId).map(r -> r.displayName()).orElse(rankId));
sendMessage(player, "<green>Rank set to <white>" + applyDisplay(player);
rankService.getRank(rankId).map(r -> r.displayName()).orElse(rankId)); });
applyDisplay(player);
} }
private void handleSkin(Player player, String[] args) { private void handleSkin(Player player, String[] args) {

View File

@@ -53,8 +53,8 @@ public final class GuiManager implements Listener {
} }
public void openRankMenu(Player player) { public void openRankMenu(Player player) {
RankMenuGui gui = new RankMenuGui(player, this, rankService, profileCache, RankMenuGui gui = new RankMenuGui(player, this, nickService, rankService,
placeholderService, nametagService); profileCache, placeholderService, nametagService);
openGuis.put(player.getUniqueId(), gui); openGuis.put(player.getUniqueId(), gui);
gui.open(); gui.open();
} }

View File

@@ -3,6 +3,7 @@ package com.nickcore.paper.gui;
import com.nickcore.common.model.NickProfile; import com.nickcore.common.model.NickProfile;
import com.nickcore.common.model.RankDefinition; import com.nickcore.common.model.RankDefinition;
import com.nickcore.paper.cache.ProfileCache; import com.nickcore.paper.cache.ProfileCache;
import com.nickcore.paper.service.NickService;
import com.nickcore.paper.service.NametagService; import com.nickcore.paper.service.NametagService;
import com.nickcore.paper.service.PlaceholderService; import com.nickcore.paper.service.PlaceholderService;
import com.nickcore.paper.service.RankService; import com.nickcore.paper.service.RankService;
@@ -21,6 +22,7 @@ import java.util.List;
public final class RankMenuGui extends AbstractGui { public final class RankMenuGui extends AbstractGui {
private final GuiManager guiManager; private final GuiManager guiManager;
private final NickService nickService;
private final RankService rankService; private final RankService rankService;
private final ProfileCache profileCache; private final ProfileCache profileCache;
private final PlaceholderService placeholderService; private final PlaceholderService placeholderService;
@@ -28,11 +30,12 @@ public final class RankMenuGui extends AbstractGui {
private final MiniMessage mm = MiniMessage.miniMessage(); private final MiniMessage mm = MiniMessage.miniMessage();
private int page = 0; private int page = 0;
public RankMenuGui(Player viewer, GuiManager guiManager, RankService rankService, public RankMenuGui(Player viewer, GuiManager guiManager, NickService nickService,
ProfileCache profileCache, PlaceholderService placeholderService, RankService rankService, ProfileCache profileCache,
NametagService nametagService) { PlaceholderService placeholderService, NametagService nametagService) {
super(viewer, 54, Component.text("Select Rank", NamedTextColor.GOLD, TextDecoration.BOLD)); super(viewer, 54, Component.text("Select Rank", NamedTextColor.GOLD, TextDecoration.BOLD));
this.guiManager = guiManager; this.guiManager = guiManager;
this.nickService = nickService;
this.rankService = rankService; this.rankService = rankService;
this.profileCache = profileCache; this.profileCache = profileCache;
this.placeholderService = placeholderService; this.placeholderService = placeholderService;
@@ -57,14 +60,14 @@ public final class RankMenuGui extends AbstractGui {
Component.text("Click to select", NamedTextColor.YELLOW) Component.text("Click to select", NamedTextColor.YELLOW)
)), )),
e -> { e -> {
NickProfile profile = profileCache.get(viewer.getUniqueId()) // Save to DB and sync via Velocity
.orElse(NickProfile.empty(viewer.getUniqueId())); nickService.saveRankChange(viewer.getUniqueId(), rank.id()).thenRun(() -> {
profileCache.put(profile.withRank(rank.id())); viewer.closeInventory();
viewer.closeInventory(); viewer.sendMessage(Component.text("Rank set to ", NamedTextColor.GREEN)
viewer.sendMessage(Component.text("Rank set to ", NamedTextColor.GREEN) .append(mm.deserialize(rank.displayName())));
.append(mm.deserialize(rank.displayName()))); placeholderService.invalidate(viewer.getUniqueId(), viewer.getName());
placeholderService.invalidate(viewer.getUniqueId(), viewer.getName()); nametagService.updateNametag(viewer);
nametagService.updateNametag(viewer); });
}); });
} }

View File

@@ -3,6 +3,7 @@ package com.nickcore.paper.integration;
import com.nickcore.common.dto.NickSyncMessage; import com.nickcore.common.dto.NickSyncMessage;
import com.nickcore.common.model.NickProfile; import com.nickcore.common.model.NickProfile;
import com.nickcore.paper.cache.ProfileCache; import com.nickcore.paper.cache.ProfileCache;
import com.nickcore.paper.service.NameOverrideService;
import com.nickcore.paper.service.PlaceholderService; import com.nickcore.paper.service.PlaceholderService;
import com.nickcore.paper.service.NametagService; import com.nickcore.paper.service.NametagService;
import com.nickcore.paper.scheduler.SchedulerAdapter; import com.nickcore.paper.scheduler.SchedulerAdapter;
@@ -19,6 +20,10 @@ import java.util.logging.Logger;
/** /**
* Velocity plugin messaging integration. * Velocity plugin messaging integration.
* Sends/receives NickSyncMessages for cross-server nick synchronization. * Sends/receives NickSyncMessages for cross-server nick synchronization.
*
* <p>When a player switches servers (hub → survival), the Velocity proxy forwards
* their profile to the new server. This handler receives that sync and applies
* the full name override so the nick persists across servers.</p>
*/ */
public final class VelocityMessaging implements PluginMessageListener { public final class VelocityMessaging implements PluginMessageListener {
@@ -26,17 +31,20 @@ public final class VelocityMessaging implements PluginMessageListener {
private final ProfileCache profileCache; private final ProfileCache profileCache;
private final PlaceholderService placeholderService; private final PlaceholderService placeholderService;
private final NametagService nametagService; private final NametagService nametagService;
private final NameOverrideService nameOverrideService;
private final SchedulerAdapter scheduler; private final SchedulerAdapter scheduler;
private final Logger logger; private final Logger logger;
private final String serverName; private final String serverName;
public VelocityMessaging(Plugin plugin, ProfileCache profileCache, public VelocityMessaging(Plugin plugin, ProfileCache profileCache,
PlaceholderService placeholderService, NametagService nametagService, PlaceholderService placeholderService, NametagService nametagService,
SchedulerAdapter scheduler, Logger logger, String serverName) { NameOverrideService nameOverrideService, SchedulerAdapter scheduler,
Logger logger, String serverName) {
this.plugin = plugin; this.plugin = plugin;
this.profileCache = profileCache; this.profileCache = profileCache;
this.placeholderService = placeholderService; this.placeholderService = placeholderService;
this.nametagService = nametagService; this.nametagService = nametagService;
this.nameOverrideService = nameOverrideService;
this.scheduler = scheduler; this.scheduler = scheduler;
this.logger = logger; this.logger = logger;
this.serverName = serverName; this.serverName = serverName;
@@ -71,12 +79,34 @@ public final class VelocityMessaging implements PluginMessageListener {
} }
/** /**
* Sends a full profile sync for a player. * Sends a full profile sync to Velocity for cross-server propagation.
* Called every time a nick/rank/skin changes on this server.
*/ */
public void syncProfile(NickProfile profile) { public void syncProfile(NickProfile profile) {
sendSync(NickSyncMessage.fullSync(profile, serverName)); sendSync(NickSyncMessage.fullSync(profile, serverName));
} }
/**
* Sends a nick update to Velocity.
*/
public void syncNickUpdate(java.util.UUID uuid, String nickname) {
sendSync(NickSyncMessage.nickUpdate(uuid, nickname, serverName));
}
/**
* Sends a rank update to Velocity.
*/
public void syncRankUpdate(java.util.UUID uuid, String rankId) {
sendSync(NickSyncMessage.rankUpdate(uuid, rankId, serverName));
}
/**
* Sends a reset to Velocity.
*/
public void syncReset(java.util.UUID uuid) {
sendSync(NickSyncMessage.reset(uuid, serverName));
}
@Override @Override
public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, byte @NotNull [] data) { public void onPluginMessageReceived(@NotNull String channel, @NotNull Player player, byte @NotNull [] data) {
if (!NickSyncMessage.CHANNEL.equals(channel)) return; if (!NickSyncMessage.CHANNEL.equals(channel)) return;
@@ -109,7 +139,11 @@ public final class VelocityMessaging implements PluginMessageListener {
Player player = Bukkit.getPlayer(msg.playerUuid()); Player player = Bukkit.getPlayer(msg.playerUuid());
if (player != null) { if (player != null) {
placeholderService.invalidate(msg.playerUuid(), player.getName()); placeholderService.invalidate(msg.playerUuid(), player.getName());
scheduler.runAtEntity(plugin, player, () -> nametagService.updateNametag(player)); scheduler.runAtEntity(plugin, player, () -> {
// Apply full name override so the nick appears everywhere on this server
nameOverrideService.applyFullOverride(player);
nametagService.updateNametag(player);
});
} }
} }
@@ -137,13 +171,24 @@ public final class VelocityMessaging implements PluginMessageListener {
private void handleCacheInvalidate(NickSyncMessage msg) { private void handleCacheInvalidate(NickSyncMessage msg) {
profileCache.remove(msg.playerUuid()); profileCache.remove(msg.playerUuid());
placeholderService.remove(msg.playerUuid()); placeholderService.remove(msg.playerUuid());
Player player = Bukkit.getPlayer(msg.playerUuid());
if (player != null) {
scheduler.runAtEntity(plugin, player, () -> {
nameOverrideService.clearMapping(player);
nameOverrideService.applyFullOverride(player);
nametagService.updateNametag(player);
});
}
} }
private void refreshPlayer(java.util.UUID uuid) { private void refreshPlayer(java.util.UUID uuid) {
Player player = Bukkit.getPlayer(uuid); Player player = Bukkit.getPlayer(uuid);
if (player != null) { if (player != null) {
placeholderService.invalidate(uuid, player.getName()); placeholderService.invalidate(uuid, player.getName());
scheduler.runAtEntity(plugin, player, () -> nametagService.updateNametag(player)); scheduler.runAtEntity(plugin, player, () -> {
nameOverrideService.applyFullOverride(player);
nametagService.updateNametag(player);
});
} }
} }
} }

View File

@@ -5,6 +5,7 @@ import com.nickcore.common.model.NickProfile;
import com.nickcore.common.validation.InputSanitizer; import com.nickcore.common.validation.InputSanitizer;
import com.nickcore.common.validation.NicknameValidator; import com.nickcore.common.validation.NicknameValidator;
import com.nickcore.paper.cache.ProfileCache; import com.nickcore.paper.cache.ProfileCache;
import com.nickcore.paper.integration.VelocityMessaging;
import com.nickcore.paper.repository.NickRepository; import com.nickcore.paper.repository.NickRepository;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
@@ -45,6 +46,9 @@ public final class NickService {
private volatile boolean allowDuplicateNicks = false; private volatile boolean allowDuplicateNicks = false;
private volatile boolean allowCustomNames = false; private volatile boolean allowCustomNames = false;
// Optional: set when Velocity is enabled for cross-server sync
private volatile VelocityMessaging velocityMessaging;
public NickService(NickRepository repository, ProfileCache cache, public NickService(NickRepository repository, ProfileCache cache,
NicknameValidator validator, Logger logger) { NicknameValidator validator, Logger logger) {
this.repository = Objects.requireNonNull(repository); this.repository = Objects.requireNonNull(repository);
@@ -138,6 +142,11 @@ public final class NickService {
return repository.save(newProfile).thenCompose(v -> { return repository.save(newProfile).thenCompose(v -> {
cache.put(newProfile); cache.put(newProfile);
// Sync to other servers via Velocity
if (velocityMessaging != null) {
velocityMessaging.syncProfile(newProfile);
}
// Record history // Record history
NickHistory history = NickHistory.nickChange( NickHistory history = NickHistory.nickChange(
uuid, oldProfile.nickname(), sanitized, uuid, oldProfile.nickname(), sanitized,
@@ -189,6 +198,11 @@ public final class NickService {
return repository.save(resetProfile).thenCompose(v -> { return repository.save(resetProfile).thenCompose(v -> {
cache.put(resetProfile); cache.put(resetProfile);
// Sync reset to other servers
if (velocityMessaging != null) {
velocityMessaging.syncReset(uuid);
}
NickHistory history = NickHistory.reset( NickHistory history = NickHistory.reset(
uuid, oldProfile.nickname(), oldProfile.rankId(), changedBy uuid, oldProfile.nickname(), oldProfile.rankId(), changedBy
); );
@@ -239,6 +253,28 @@ public final class NickService {
return allowCustomNames; return allowCustomNames;
} }
/**
* Sets the VelocityMessaging instance for cross-server sync.
*/
public void setVelocityMessaging(VelocityMessaging velocityMessaging) {
this.velocityMessaging = velocityMessaging;
}
/**
* Saves a rank change to the database and syncs to Velocity.
*/
public CompletableFuture<Void> saveRankChange(java.util.UUID uuid, String rankId) {
NickProfile profile = cache.get(uuid).orElse(NickProfile.empty(uuid));
NickProfile updated = profile.withRank(rankId);
cache.put(updated);
if (velocityMessaging != null) {
velocityMessaging.syncRankUpdate(uuid, rankId);
}
return repository.save(updated);
}
// ── Result Container ──────────────────────────────────────────────── // ── Result Container ────────────────────────────────────────────────
/** /**

View File

@@ -1,9 +1,9 @@
name: NickCore name: NickCore
version: '1.0.0' version: '1.0.4'
main: com.nickcore.paper.NickCorePlugin main: com.nickcore.paper.NickCorePlugin
api-version: '1.21' api-version: '1.21'
description: Enterprise Minecraft Nickname Plugin description: Enterprise Minecraft Nickname Plugin
author: NickCore Team author: BaileyCodes
website: https://nickcore.com website: https://nickcore.com
folia-supported: true folia-supported: true

View File

@@ -27,8 +27,8 @@ import java.util.concurrent.ConcurrentHashMap;
* NickCore Velocity proxy plugin. * NickCore Velocity proxy plugin.
* Maintains a cross-server nick profile cache and routes sync messages. * Maintains a cross-server nick profile cache and routes sync messages.
*/ */
@Plugin(id = "nickcore", name = "NickCore", version = "1.0.0", @Plugin(id = "nickcore", name = "NickCore", version = "1.0.4",
authors = {"NickCore Team"}, description = "Enterprise nickname system - Velocity module") authors = {"BaileyCodes"}, description = "Enterprise nickname system - Velocity module")
public final class NickCoreVelocity { public final class NickCoreVelocity {
private static final MinecraftChannelIdentifier CHANNEL = private static final MinecraftChannelIdentifier CHANNEL =