ready = new CompletableFuture<>();
private final ProxyServer proxy;
private final ComponentLogger logger;
private final Path dataDirectory;
+ private final DefaultCapabilityRegistry capabilityRegistry = new DefaultCapabilityRegistry();
+ private final DefaultFeatureCatalog featureCatalog = new DefaultFeatureCatalog();
+ private final InternalServiceRegistry internalServiceRegistry = new InternalServiceRegistry();
+ private final CommandOwnershipRegistry commandOwnershipRegistry = new CommandOwnershipRegistry();
+ private final LifecycleCoordinator lifecycleCoordinator = new LifecycleCoordinator();
+ private final DefaultMotdExtensions motdExtensions = new DefaultMotdExtensions();
+ @SuppressWarnings("FieldCanBeLocal")
+ private final nl.hauntedmc.proxyfeatures.framework.service.CapabilityRegistration motdExtensionRegistration =
+ capabilityRegistry.register(FeatureId.of("core"), MotdExtensions.class, motdExtensions);
@Inject
public ProxyFeatures(ProxyServer proxy,
@@ -63,15 +96,8 @@ public ProxyFeatures(ProxyServer proxy,
logger.info("ProxyFeatures is loading...");
}
- /**
- * SYNC
- *
- * This event is fired by the proxy after plugins have been
- * loaded but before the proxy starts accepting connections.
- */
@Subscribe
public void onProxyInitialization(ProxyInitializeEvent event) {
- // General plugin initialization
configService = new ConfigService(this);
mainConfigHandler = new MainConfigHandler(logger, configService);
localizationHandler = new LocalizationHandler(logger, getClass().getClassLoader(), configService);
@@ -80,44 +106,114 @@ public void onProxyInitialization(ProxyInitializeEvent event) {
featureLoadManager = new FeatureLoadManager(this, featureScopeFactory);
registerBaseCommand();
registerCommonListeners();
-
- // Feature specific initialization
- featureLoadManager.initializeFeatures();
+ try {
+ featureLoadManager.initializeFeatures();
+ runtimeState = RuntimeState.READY;
+ ready.complete(null);
+ } catch (Throwable failure) {
+ runtimeState = RuntimeState.DEGRADED;
+ ready.completeExceptionally(failure);
+ throw failure;
+ }
}
/**
- * This event is fired when the proxy is
- * reloaded by the user using /velocity reload.
+ * Validates shared configuration before touching the active graph, then reloads each graph through
+ * the transactional per-feature path so state is captured and failures roll back locally.
*/
@Subscribe
public void onProxyReload(final ProxyReloadEvent event) {
+ lifecycleCoordinator.runExclusive(() -> reloadProxyFeatures(event));
+ }
+
+ private void reloadProxyFeatures(ProxyReloadEvent event) {
if (mainConfigHandler == null || localizationHandler == null || featureLoadManager == null) {
return;
}
- getLogger().info("Reloading ProxyFeatures...");
- featureLoadManager.unloadAllFeatures();
- mainConfigHandler.reloadConfig();
- localizationHandler.reloadLocalization();
- featureLifecycleFactory = new FeatureLifecycleFactory(this);
- featureScopeFactory = new FeatureScopeFactory(this, mainConfigHandler, localizationHandler, featureLifecycleFactory);
- featureLoadManager = new FeatureLoadManager(this, featureScopeFactory);
- featureLoadManager.initializeFeatures();
- getLogger().info("ProxyFeatures reloaded.");
+ runtimeState = RuntimeState.RELOADING;
+ getLogger().info("Reloading ProxyFeatures transactionally...");
+ try {
+ mainConfigHandler.reloadConfig();
+ localizationHandler.reloadLocalization();
+ } catch (Throwable validationFailure) {
+ getLogger().error("ProxyFeatures reload aborted before runtime changes: configuration is invalid.", validationFailure);
+ runtimeState = RuntimeState.DEGRADED;
+ return;
+ }
+
+ playerReferenceResolver = null;
+ Set loadedBefore = new LinkedHashSet<>(
+ featureLoadManager.getFeatureRegistry().getLoadedFeatureNames()
+ );
+ Set processed = new LinkedHashSet<>();
+
+ for (String featureName : loadedBefore) {
+ if (!featureLoadManager.getFeatureRegistry().isFeatureLoaded(featureName)) {
+ continue;
+ }
+ if (!mainConfigHandler.isFeatureEnabled(featureName)) {
+ var response = featureLoadManager.disableFeature(featureName);
+ processed.add(featureName);
+ processed.addAll(response.alsoDisabledDependents());
+ if (!response.success()) {
+ getLogger().error("ProxyFeatures reload stopped: failed disabling feature '{}'.", featureName);
+ runtimeState = RuntimeState.DEGRADED;
+ return;
+ }
+ }
+ }
+
+ for (String featureName : loadedBefore) {
+ if (processed.contains(featureName)
+ || !featureLoadManager.getFeatureRegistry().isFeatureLoaded(featureName)) {
+ continue;
+ }
+ FeatureReloadResponse response = featureLoadManager.reloadFeature(featureName);
+ if (!response.success()) {
+ getLogger().error(
+ "ProxyFeatures reload stopped after transactional rollback of feature graph '{}'.",
+ featureName
+ );
+ runtimeState = RuntimeState.DEGRADED;
+ return;
+ }
+ processed.add(featureName);
+ processed.addAll(response.reloadedDependents());
+ }
+
+ enableConfiguredFeatures();
+ runtimeState = RuntimeState.READY;
+ getLogger().info("ProxyFeatures reloaded transactionally with state preservation.");
+ }
+
+ private void enableConfiguredFeatures() {
+ boolean progress;
+ do {
+ progress = false;
+ for (String featureName : featureLoadManager.getFeatureRegistry().getAvailableFeatures().keySet()) {
+ if (!mainConfigHandler.isFeatureEnabled(featureName)
+ || featureLoadManager.getFeatureRegistry().isFeatureLoaded(featureName)) {
+ continue;
+ }
+ if (featureLoadManager.enableFeature(featureName).success()) {
+ progress = true;
+ }
+ }
+ } while (progress);
}
- /**
- * SYNC
- *
- * This event is fired by the proxy after the proxy has stopped
- * accepting connections but before the proxy process exits
- */
@Subscribe
public void onProxyShutdown(final ProxyShutdownEvent event) {
- if (featureLoadManager != null) {
- featureLoadManager.unloadAllFeatures();
- }
- getLogger().info("proxyfeatures is shutting down...");
+ lifecycleCoordinator.runExclusive(() -> {
+ runtimeState = RuntimeState.STOPPING;
+ if (featureLoadManager != null) {
+ featureLoadManager.unloadAllFeatures();
+ }
+ playerReferenceResolver = null;
+ getLogger().info("proxyfeatures is shutting down...");
+ runtimeState = RuntimeState.STOPPED;
+ });
}
public ComponentLogger getLogger() {
@@ -126,16 +222,12 @@ public ComponentLogger getLogger() {
private void registerBaseCommand() {
CommandManager commandManager = proxy.getCommandManager();
-
- // Build Brigadier tree and register via Velocity's BrigadierCommand wrapper
ProxyFeaturesCommand root = new ProxyFeaturesCommand(this);
com.velocitypowered.api.command.BrigadierCommand brigadier =
new com.velocitypowered.api.command.BrigadierCommand(root.buildTree());
-
CommandMeta meta = commandManager.metaBuilder(brigadier)
.plugin(this)
.build();
-
commandManager.register(meta, brigadier);
}
@@ -166,6 +258,7 @@ public FeatureScopeFactory getFeatureScopeFactory() {
return featureScopeFactory;
}
+ @Override
public Path getDataDirectory() {
return dataDirectory;
}
@@ -198,4 +291,71 @@ public Optional getDataRegistry() {
.map(DataRegistryApiProvider.class::cast)
.map(DataRegistryApiProvider::getDataRegistry);
}
+
+ /** Shared immutable player-reference resolver used by all feature persistence adapters. */
+ public PlayerReferenceResolver getPlayerReferenceResolver() {
+ PlayerReferenceResolver current = playerReferenceResolver;
+ if (current != null) return current;
+ synchronized (this) {
+ current = playerReferenceResolver;
+ if (current == null) {
+ current = new PlayerReferenceResolver(getDataRegistry().orElseThrow(
+ () -> new IllegalStateException("DataRegistryApi is required for player persistence.")));
+ playerReferenceResolver = current;
+ }
+ return current;
+ }
+ }
+
+ @Override
+ public ProxyFeaturesApiVersion version() {
+ String implementationVersion = Optional.ofNullable(getClass().getPackage().getImplementationVersion())
+ .filter(version -> !version.isBlank())
+ .orElse(FALLBACK_IMPLEMENTATION_VERSION);
+ return ProxyFeaturesApiVersion.current(implementationVersion);
+ }
+
+ @Override
+ public RuntimeState state() {
+ return runtimeState;
+ }
+
+ @Override
+ public CompletionStage whenReady() {
+ return ready.minimalCompletionStage();
+ }
+
+ @Override
+ public CapabilityRegistry capabilities() {
+ return capabilityRegistry;
+ }
+
+ @Override
+ public FeatureCatalog features() {
+ return featureCatalog;
+ }
+
+ public DefaultCapabilityRegistry getCapabilityRegistry() {
+ return capabilityRegistry;
+ }
+
+ public DefaultFeatureCatalog getFeatureCatalog() {
+ return featureCatalog;
+ }
+
+ public InternalServiceRegistry getInternalServiceRegistry() {
+ return internalServiceRegistry;
+ }
+
+ public CommandOwnershipRegistry getCommandOwnershipRegistry() {
+ return commandOwnershipRegistry;
+ }
+
+ public LifecycleCoordinator getLifecycleCoordinator() {
+ return lifecycleCoordinator;
+ }
+
+ public DefaultMotdExtensions getMotdExtensions() {
+ return motdExtensions;
+ }
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/FeatureContext.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/FeatureContext.java
deleted file mode 100644
index f093e73e..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/FeatureContext.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features;
-
-import nl.hauntedmc.proxyfeatures.ProxyFeatures;
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-import nl.hauntedmc.proxyfeatures.framework.config.FeatureConfigHandler;
-import nl.hauntedmc.proxyfeatures.framework.lifecycle.FeatureLifecycleManager;
-import nl.hauntedmc.proxyfeatures.framework.localization.LocalizationHandler;
-import nl.hauntedmc.proxyfeatures.framework.log.FeatureLogger;
-
-import java.util.Objects;
-
-public final class FeatureContext {
-
- private final ProxyFeatures plugin;
- private final T meta;
- private final FeatureConfigHandler configHandler;
- private final FeatureLifecycleManager lifecycleManager;
- private final FeatureLogger logger;
- private final LocalizationHandler localizationHandler;
-
- public FeatureContext(
- ProxyFeatures plugin,
- T meta,
- FeatureConfigHandler configHandler,
- FeatureLifecycleManager lifecycleManager,
- FeatureLogger logger,
- LocalizationHandler localizationHandler
- ) {
- this.plugin = Objects.requireNonNull(plugin, "plugin");
- this.meta = Objects.requireNonNull(meta, "meta");
- this.configHandler = Objects.requireNonNull(configHandler, "configHandler");
- this.lifecycleManager = Objects.requireNonNull(lifecycleManager, "lifecycleManager");
- this.logger = Objects.requireNonNull(logger, "logger");
- this.localizationHandler = Objects.requireNonNull(localizationHandler, "localizationHandler");
- }
-
- public ProxyFeatures plugin() {
- return plugin;
- }
-
- public T meta() {
- return meta;
- }
-
- public FeatureConfigHandler configHandler() {
- return configHandler;
- }
-
- public FeatureLifecycleManager lifecycleManager() {
- return lifecycleManager;
- }
-
- public FeatureLogger logger() {
- return logger;
- }
-
- public LocalizationHandler localizationHandler() {
- return localizationHandler;
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/FeatureFactory.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/FeatureFactory.java
deleted file mode 100644
index 7130c00a..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/FeatureFactory.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features;
-
-import nl.hauntedmc.proxyfeatures.ProxyFeatures;
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-public class FeatureFactory {
-
- public static VelocityBaseFeature> createFeature(String featureClassName, FeatureContext extends BaseMeta> context) {
- if (featureClassName == null || featureClassName.isBlank()) {
- context.plugin().getLogger().error("Failed to instantiate feature: missing feature class name.");
- return null;
- }
-
- try {
- ProxyFeatures plugin = context.plugin();
- Class> rawClass = Class.forName(featureClassName, true, plugin.getClass().getClassLoader());
- if (!VelocityBaseFeature.class.isAssignableFrom(rawClass)) {
- plugin.getLogger().error("Feature class does not extend VelocityBaseFeature: {}", featureClassName);
- return null;
- }
-
- @SuppressWarnings("unchecked")
- Class extends VelocityBaseFeature>> featureClass = (Class extends VelocityBaseFeature>>) rawClass;
- var ctor = featureClass.getDeclaredConstructor(FeatureContext.class);
- ctor.setAccessible(true);
- return ctor.newInstance(context);
- } catch (ReflectiveOperationException | LinkageError t) {
- context.plugin().getLogger().error("Failed to instantiate feature class: {}", featureClassName, t);
- return null;
- }
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/announcer/Announcer.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/announcer/Announcer.java
index 29bebfff..7f464816 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/announcer/Announcer.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/announcer/Announcer.java
@@ -1,22 +1,21 @@
package nl.hauntedmc.proxyfeatures.features.announcer;
import nl.hauntedmc.dataprovider.api.orm.ORMContext;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.features.announcer.command.AnnouncerCommand;
import nl.hauntedmc.proxyfeatures.features.announcer.entity.PlayerAnnouncerSettingsEntity;
import nl.hauntedmc.proxyfeatures.features.announcer.internal.AnnouncerHandler;
import nl.hauntedmc.proxyfeatures.features.announcer.internal.AnnouncerSettingsService;
import nl.hauntedmc.proxyfeatures.features.announcer.listener.AnnouncerPlayerListener;
-import nl.hauntedmc.proxyfeatures.features.announcer.meta.Meta;
import nl.hauntedmc.proxyfeatures.framework.persistence.DataRegistryIdentityGate;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReferenceResolver;
import java.util.List;
-public class Announcer extends VelocityBaseFeature {
+public class Announcer extends VelocityBaseFeature {
public static final String ADMIN_PERMISSION = "proxyfeatures.feature.announcer.command";
public static final String TOGGLE_PERMISSION = ADMIN_PERMISSION + ".toggle";
@@ -24,7 +23,7 @@ public class Announcer extends VelocityBaseFeature {
private AnnouncerSettingsService settingsService;
private AnnouncerHandler handler;
- public Announcer(FeatureContext context) {
+ public Announcer(FeatureContext context) {
super(context);
}
@@ -61,10 +60,7 @@ public void initialize() {
PlayerAnnouncerSettingsEntity.class
).orElseThrow();
- PlayerReferenceResolver playerResolver = new PlayerReferenceResolver(
- getPlugin().getDataRegistry()
- .orElseThrow(() -> new IllegalStateException("DataRegistryApi is required for Announcer."))
- );
+ PlayerReferenceResolver playerResolver = getPlugin().getPlayerReferenceResolver();
settingsService = new AnnouncerSettingsService(ormContext, playerResolver);
handler = new AnnouncerHandler(this, settingsService);
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/announcer/command/AnnouncerCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/announcer/command/AnnouncerCommand.java
index 1e67fbe6..11e2d758 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/announcer/command/AnnouncerCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/announcer/command/AnnouncerCommand.java
@@ -10,23 +10,18 @@
import com.velocitypowered.api.proxy.Player;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
-import nl.hauntedmc.proxyfeatures.api.command.brigadier.BrigadierCommand;
import nl.hauntedmc.proxyfeatures.features.announcer.Announcer;
import nl.hauntedmc.proxyfeatures.features.announcer.internal.AnnouncementDefinition;
import nl.hauntedmc.proxyfeatures.features.announcer.internal.AnnouncementEvaluation;
import nl.hauntedmc.proxyfeatures.features.announcer.internal.AnnouncerHandler;
import nl.hauntedmc.proxyfeatures.features.announcer.internal.AnnouncerRegistry;
+import nl.hauntedmc.proxyfeatures.framework.command.brigadier.BrigadierCommand;
import org.jetbrains.annotations.NotNull;
import java.time.DayOfWeek;
import java.time.LocalTime;
import java.time.ZoneId;
-import java.util.Arrays;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Optional;
-import java.util.Set;
+import java.util.*;
import java.util.concurrent.CompletableFuture;
public final class AnnouncerCommand implements BrigadierCommand {
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/announcer/internal/AnnouncerHandler.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/announcer/internal/AnnouncerHandler.java
index 618dbb44..6226eac0 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/announcer/internal/AnnouncerHandler.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/announcer/internal/AnnouncerHandler.java
@@ -7,9 +7,9 @@
import com.velocitypowered.api.scheduler.ScheduledTask;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.text.Component;
-import nl.hauntedmc.proxyfeatures.api.util.text.format.ComponentFormatter;
-import nl.hauntedmc.proxyfeatures.api.util.text.format.TextFormatter;
-import nl.hauntedmc.proxyfeatures.api.util.text.placeholder.MessagePlaceholders;
+import nl.hauntedmc.proxyfeatures.toolkit.text.format.ComponentFormatter;
+import nl.hauntedmc.proxyfeatures.toolkit.text.format.TextFormatter;
+import nl.hauntedmc.proxyfeatures.toolkit.text.placeholder.MessagePlaceholders;
import nl.hauntedmc.proxyfeatures.features.announcer.Announcer;
import nl.hauntedmc.proxyfeatures.framework.lifecycle.FeatureTaskManager;
import nl.hauntedmc.proxyfeatures.framework.log.FeatureLogger;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/announcer/internal/AnnouncerRegistry.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/announcer/internal/AnnouncerRegistry.java
index 91f16f85..f90c0f71 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/announcer/internal/AnnouncerRegistry.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/announcer/internal/AnnouncerRegistry.java
@@ -1,8 +1,8 @@
package nl.hauntedmc.proxyfeatures.features.announcer.internal;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigNode;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigService;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigView;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigNode;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigService;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigView;
import nl.hauntedmc.proxyfeatures.features.announcer.Announcer;
import nl.hauntedmc.proxyfeatures.framework.log.FeatureLogger;
import org.spongepowered.configurate.CommentedConfigurationNode;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/announcer/internal/AnnouncerSettingsService.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/announcer/internal/AnnouncerSettingsService.java
index f5ec32c5..9968acfd 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/announcer/internal/AnnouncerSettingsService.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/announcer/internal/AnnouncerSettingsService.java
@@ -112,6 +112,6 @@ private boolean loadEnabledState(UUID uuid, String username, boolean forceEnable
}
private PlayerReference findPlayer(Session session, UUID uuid, String username) {
- return playerResolver.resolveManaged(session, uuid);
+ return playerResolver.resolveReference(uuid);
}
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/announcer/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/announcer/meta/Meta.java
deleted file mode 100644
index ae02837f..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/announcer/meta/Meta.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.announcer.meta;
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-import java.util.List;
-
-public class Meta implements BaseMeta {
-
- @Override
- public String getFeatureName() {
- return "Announcer";
- }
-
- @Override
- public String getFeatureVersion() {
- return "2.0.0";
- }
-
- @Override
- public List getPluginDependencies() {
- return List.of(DATA_PROVIDER, DATA_REGISTRY);
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antibot/AntiBot.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antibot/AntiBot.java
index f57cef9c..11262454 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antibot/AntiBot.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antibot/AntiBot.java
@@ -1,10 +1,10 @@
package nl.hauntedmc.proxyfeatures.features.antibot;
import nl.hauntedmc.dataprovider.api.orm.ORMContext;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.features.antibot.audit.AntiBotAuditLogService;
import nl.hauntedmc.proxyfeatures.features.antibot.audit.PlayerAntiBotLogEntity;
import nl.hauntedmc.proxyfeatures.features.antibot.command.AntiBotCommand;
@@ -13,14 +13,13 @@
import nl.hauntedmc.proxyfeatures.features.antibot.internal.AntiBotService;
import nl.hauntedmc.proxyfeatures.features.antibot.internal.KnownPlayerStore;
import nl.hauntedmc.proxyfeatures.features.antibot.listener.AntiBotListener;
-import nl.hauntedmc.proxyfeatures.features.antibot.meta.Meta;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReferenceResolver;
import java.nio.file.Path;
import java.time.Duration;
import java.util.List;
-public final class AntiBot extends VelocityBaseFeature {
+public final class AntiBot extends VelocityBaseFeature {
public static final String BYPASS_PERMISSION = "proxyfeatures.feature.antibot.bypass";
public static final String NOTIFY_PERMISSION = "proxyfeatures.feature.antibot.notify";
@@ -29,7 +28,7 @@ public final class AntiBot extends VelocityBaseFeature {
private AntiBotService service;
private AntiBotAuditLogService auditLogService;
- public AntiBot(FeatureContext context) {
+ public AntiBot(FeatureContext context) {
super(context);
}
@@ -196,10 +195,7 @@ public void initialize() {
if (orm == null) {
getLogger().warn("AntiBot database audit logging is disabled because the ORM context is unavailable.");
}
- PlayerReferenceResolver playerResolver = new PlayerReferenceResolver(
- getPlugin().getDataRegistry()
- .orElseThrow(() -> new IllegalStateException("DataRegistryApi is required for AntiBot."))
- );
+ PlayerReferenceResolver playerResolver = getPlugin().getPlayerReferenceResolver();
this.auditLogService = new AntiBotAuditLogService(getLogger(), orm, playerResolver);
Path knownPlayersFile = getLifecycleManager()
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antibot/command/AntiBotCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antibot/command/AntiBotCommand.java
index 6c9aca44..ecdb63fd 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antibot/command/AntiBotCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antibot/command/AntiBotCommand.java
@@ -8,7 +8,7 @@
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import com.mojang.brigadier.tree.LiteralCommandNode;
import com.velocitypowered.api.command.CommandSource;
-import nl.hauntedmc.proxyfeatures.api.command.brigadier.BrigadierCommand;
+import nl.hauntedmc.proxyfeatures.framework.command.brigadier.BrigadierCommand;
import nl.hauntedmc.proxyfeatures.features.antibot.AntiBot;
import nl.hauntedmc.proxyfeatures.features.antibot.internal.AddressWhitelist;
import nl.hauntedmc.proxyfeatures.features.antibot.internal.AntiBotConfig;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antibot/internal/AddressWhitelist.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antibot/internal/AddressWhitelist.java
index 89cb4fc6..1239981d 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antibot/internal/AddressWhitelist.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antibot/internal/AddressWhitelist.java
@@ -1,5 +1,7 @@
package nl.hauntedmc.proxyfeatures.features.antibot.internal;
+import nl.hauntedmc.proxyfeatures.framework.network.IpAddressUtil;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antibot/internal/AntiBotService.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antibot/internal/AntiBotService.java
index 961bd348..62770cc2 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antibot/internal/AntiBotService.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antibot/internal/AntiBotService.java
@@ -1,5 +1,7 @@
package nl.hauntedmc.proxyfeatures.features.antibot.internal;
+import nl.hauntedmc.proxyfeatures.framework.network.IpAddressUtil;
+
import nl.hauntedmc.proxyfeatures.features.antibot.AntiBot;
import nl.hauntedmc.proxyfeatures.features.antibot.audit.AntiBotAuditLogService;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antibot/listener/AntiBotLoginPolicy.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antibot/listener/AntiBotLoginPolicy.java
index 08c786ab..358989e5 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antibot/listener/AntiBotLoginPolicy.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antibot/listener/AntiBotLoginPolicy.java
@@ -1,7 +1,7 @@
package nl.hauntedmc.proxyfeatures.features.antibot.listener;
import com.velocitypowered.api.proxy.Player;
-import nl.hauntedmc.proxyfeatures.features.antibot.internal.IpAddressUtil;
+import nl.hauntedmc.proxyfeatures.framework.network.IpAddressUtil;
import java.net.InetSocketAddress;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antibot/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antibot/meta/Meta.java
deleted file mode 100644
index 6d1cdae2..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antibot/meta/Meta.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.antibot.meta;
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-public class Meta implements BaseMeta {
-
- @Override
- public String getFeatureName() {
- return "AntiBot";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.0.0";
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antivpn/AntiVPN.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antivpn/AntiVPN.java
index 68bbf0c3..8d0dda0f 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antivpn/AntiVPN.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antivpn/AntiVPN.java
@@ -1,14 +1,14 @@
package nl.hauntedmc.proxyfeatures.features.antivpn;
import nl.hauntedmc.dataprovider.api.orm.ORMContext;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.api.io.cache.CacheDirectory;
-import nl.hauntedmc.proxyfeatures.api.io.cache.CacheType;
-import nl.hauntedmc.proxyfeatures.api.io.cache.FileCacheStore;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
-import nl.hauntedmc.proxyfeatures.features.antivpn.api.CountryAPI;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.toolkit.io.cache.CacheDirectory;
+import nl.hauntedmc.proxyfeatures.toolkit.io.cache.CacheType;
+import nl.hauntedmc.proxyfeatures.toolkit.io.cache.FileCacheStore;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.api.capability.player.NetworkLocationApi;
import nl.hauntedmc.proxyfeatures.features.antivpn.audit.AntiVpnAuditLogService;
import nl.hauntedmc.proxyfeatures.features.antivpn.audit.PlayerAntiVpnLogEntity;
import nl.hauntedmc.proxyfeatures.features.antivpn.command.AntiVPNCommand;
@@ -20,13 +20,12 @@
import nl.hauntedmc.proxyfeatures.features.antivpn.internal.provider.ProviderChain;
import nl.hauntedmc.proxyfeatures.features.antivpn.internal.provider.ProviderRegistry;
import nl.hauntedmc.proxyfeatures.features.antivpn.listener.AntiVPNListener;
-import nl.hauntedmc.proxyfeatures.features.antivpn.meta.Meta;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReferenceResolver;
import java.time.Duration;
import java.util.List;
-public class AntiVPN extends VelocityBaseFeature {
+public class AntiVPN extends VelocityBaseFeature {
private CountryService countryService;
private MetricsCollector metrics;
@@ -34,7 +33,7 @@ public class AntiVPN extends VelocityBaseFeature {
private AntiVPNService service;
private AntiVpnAuditLogService auditLogService;
- public AntiVPN(FeatureContext context) {
+ public AntiVPN(FeatureContext context) {
super(context);
}
@@ -151,10 +150,7 @@ public void initialize() {
if (orm == null) {
getLogger().warn("AntiVPN database audit logging is disabled because the ORM context is unavailable.");
}
- PlayerReferenceResolver playerResolver = new PlayerReferenceResolver(
- getPlugin().getDataRegistry()
- .orElseThrow(() -> new IllegalStateException("DataRegistryApi is required for AntiVPN."))
- );
+ PlayerReferenceResolver playerResolver = getPlugin().getPlayerReferenceResolver();
this.auditLogService = new AntiVpnAuditLogService(getLogger(), orm, playerResolver);
this.metrics = new MetricsCollector();
@@ -177,7 +173,7 @@ public void initialize() {
this.service = new AntiVPNService(this, cache, providerChain, notifications, metrics);
// API
- getLifecycleManager().getApiManager().registerService(CountryAPI.class, countryService);
+ getLifecycleManager().getApiManager().registerService(NetworkLocationApi.class, countryService);
// Listener
getLifecycleManager().getListenerManager().registerListener(
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antivpn/api/CountryAPI.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antivpn/api/CountryAPI.java
deleted file mode 100644
index abecb25b..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antivpn/api/CountryAPI.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.antivpn.api;
-
-import java.util.Optional;
-import java.util.UUID;
-
-/**
- * Simple API to get the player's ISO country code (e.g., "NL", "US").
- */
-public interface CountryAPI {
- /**
- * Returns the country code for the given player UUID, if known.
- */
- Optional getCountry(UUID uuid);
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antivpn/command/AntiVPNCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antivpn/command/AntiVPNCommand.java
index c6e9937e..766d9fe3 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antivpn/command/AntiVPNCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antivpn/command/AntiVPNCommand.java
@@ -12,7 +12,7 @@
import net.kyori.adventure.text.Component;
import nl.hauntedmc.dataregistry.api.player.PlayerData;
import nl.hauntedmc.dataregistry.api.player.PlayerIdentity;
-import nl.hauntedmc.proxyfeatures.api.command.brigadier.BrigadierCommand;
+import nl.hauntedmc.proxyfeatures.framework.command.brigadier.BrigadierCommand;
import nl.hauntedmc.proxyfeatures.features.antivpn.AntiVPN;
import nl.hauntedmc.proxyfeatures.features.antivpn.internal.AntiVPNService;
import nl.hauntedmc.proxyfeatures.features.antivpn.internal.IpWhitelist;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antivpn/internal/CountryService.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antivpn/internal/CountryService.java
index 0c051d45..e40d4257 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antivpn/internal/CountryService.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antivpn/internal/CountryService.java
@@ -2,7 +2,8 @@
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
-import nl.hauntedmc.proxyfeatures.features.antivpn.api.CountryAPI;
+import nl.hauntedmc.proxyfeatures.api.capability.player.NetworkLocationApi;
+import nl.hauntedmc.proxyfeatures.api.model.CountryCode;
import java.time.Duration;
import java.util.Locale;
@@ -13,7 +14,7 @@
/**
* Thread-safe storage of country codes keyed by UUID, with optional temporary username staging.
*/
-public final class CountryService implements CountryAPI {
+public final class CountryService implements NetworkLocationApi {
private final ConcurrentHashMap byUuid = new ConcurrentHashMap<>();
private final Cache stagedByUsernameLower;
@@ -26,9 +27,9 @@ public CountryService(Duration usernameTtl) {
}
@Override
- public Optional getCountry(UUID uuid) {
+ public Optional countryCode(UUID uuid) {
String v = byUuid.get(uuid);
- return (v == null || v.isBlank()) ? Optional.empty() : Optional.of(v);
+ return CountryCode.optional(v);
}
public void put(UUID uuid, String countryCode) {
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antivpn/internal/PersistentIpCache.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antivpn/internal/PersistentIpCache.java
index 78deef72..ca7bb2dd 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antivpn/internal/PersistentIpCache.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antivpn/internal/PersistentIpCache.java
@@ -2,8 +2,8 @@
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
-import nl.hauntedmc.proxyfeatures.api.io.cache.CacheValue;
-import nl.hauntedmc.proxyfeatures.api.io.cache.FileCacheStore;
+import nl.hauntedmc.proxyfeatures.toolkit.io.cache.CacheValue;
+import nl.hauntedmc.proxyfeatures.toolkit.io.cache.FileCacheStore;
import nl.hauntedmc.proxyfeatures.features.antivpn.AntiVPN;
import java.time.Duration;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antivpn/listener/AntiVPNLoginPolicy.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antivpn/listener/AntiVPNLoginPolicy.java
index 69edf712..ea6838e5 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antivpn/listener/AntiVPNLoginPolicy.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antivpn/listener/AntiVPNLoginPolicy.java
@@ -1,7 +1,7 @@
package nl.hauntedmc.proxyfeatures.features.antivpn.listener;
import com.velocitypowered.api.proxy.Player;
-import nl.hauntedmc.proxyfeatures.features.antibot.internal.IpAddressUtil;
+import nl.hauntedmc.proxyfeatures.framework.network.IpAddressUtil;
import java.net.InetAddress;
import java.net.InetSocketAddress;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antivpn/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antivpn/meta/Meta.java
deleted file mode 100644
index 9fb06a1b..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/antivpn/meta/Meta.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.antivpn.meta;
-
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-import java.util.List;
-
-public class Meta implements BaseMeta {
-
- @Override
- public String getFeatureName() {
- return "AntiVPN";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.1.0";
- }
-
- @Override
- public List getPluginDependencies() {
- return List.of(DATA_PROVIDER, DATA_REGISTRY);
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/broadcast/Broadcast.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/broadcast/Broadcast.java
index aea046c5..2ba84017 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/broadcast/Broadcast.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/broadcast/Broadcast.java
@@ -1,15 +1,14 @@
package nl.hauntedmc.proxyfeatures.features.broadcast;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.features.broadcast.command.BroadcastProxyCommand;
-import nl.hauntedmc.proxyfeatures.features.broadcast.meta.Meta;
-public class Broadcast extends VelocityBaseFeature {
+public class Broadcast extends VelocityBaseFeature {
- public Broadcast(FeatureContext context) {
+ public Broadcast(FeatureContext context) {
super(context);
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/broadcast/command/BroadcastProxyCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/broadcast/command/BroadcastProxyCommand.java
index 37ced13d..005e34f9 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/broadcast/command/BroadcastProxyCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/broadcast/command/BroadcastProxyCommand.java
@@ -11,9 +11,9 @@
import com.velocitypowered.api.proxy.ProxyServer;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.title.Title;
-import nl.hauntedmc.proxyfeatures.api.command.brigadier.BrigadierCommand;
-import nl.hauntedmc.proxyfeatures.api.util.text.format.ComponentFormatter;
-import nl.hauntedmc.proxyfeatures.api.util.text.format.TextFormatter;
+import nl.hauntedmc.proxyfeatures.framework.command.brigadier.BrigadierCommand;
+import nl.hauntedmc.proxyfeatures.toolkit.text.format.ComponentFormatter;
+import nl.hauntedmc.proxyfeatures.toolkit.text.format.TextFormatter;
import nl.hauntedmc.proxyfeatures.features.broadcast.Broadcast;
import org.jetbrains.annotations.NotNull;
@@ -32,18 +32,9 @@ public final class BroadcastProxyCommand implements BrigadierCommand {
private final Broadcast feature;
private final ProxyServer proxy;
- /**
- * Cached title timings (computed once on init).
- * Note: if you change config and run /proxyfeatures softreload, the framework reloads the YAML in memory,
- * but it will NOT re-create this command. If you want these values to update on softreload too,
- * you need a small hook to call {@link #reloadTitleTimesCache()} after config reload.
- */
- private volatile Title.Times cachedTitleTimes;
-
public BroadcastProxyCommand(Broadcast feature) {
this.feature = feature;
this.proxy = feature.getPlugin().getProxy();
- reloadTitleTimesCache();
}
@Override
@@ -148,27 +139,23 @@ private void broadcastTitle(String msg, CommandSource src) {
.features(ComponentFormatter.ALL_DEFAULTS())
.toComponent();
- Title.Times times = this.cachedTitleTimes; // volatile read
- Title title = Title.title(titleComp, subComp, times);
+ Title title = Title.title(titleComp, subComp, titleTimes());
proxy.getAllPlayers().forEach(p -> p.showTitle(title));
acknowledge(src);
}
- /* ============================ Cache ============================ */
+ /* ============================ Configuration ============================ */
- /**
- * Recomputes the cached title timings from config.
- * Call this if you ever add a softreload hook for this feature/command.
- */
- public void reloadTitleTimesCache() {
+ /** Returns a point-in-time timing snapshot from the current feature configuration. */
+ private Title.Times titleTimes() {
var root = feature.getConfigHandler().node();
int fadeInTicks = clampNonNegative(root.get("title_fade_in").as(Integer.class, 20));
int stayTicks = clampNonNegative(root.get("title_stay").as(Integer.class, 100));
int fadeOutTicks = clampNonNegative(root.get("title_fade_out").as(Integer.class, 20));
- this.cachedTitleTimes = Title.Times.times(
+ return Title.Times.times(
Duration.ofMillis(fadeInTicks * TICK_MILLIS),
Duration.ofMillis(stayTicks * TICK_MILLIS),
Duration.ofMillis(fadeOutTicks * TICK_MILLIS)
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/broadcast/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/broadcast/meta/Meta.java
deleted file mode 100644
index ded703fb..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/broadcast/meta/Meta.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.broadcast.meta;
-
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-public class Meta implements BaseMeta {
-
- @Override
- public String getFeatureName() {
- return "Broadcast";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.0.0";
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/Capacity.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/Capacity.java
index 862042a8..add329a3 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/Capacity.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/Capacity.java
@@ -2,20 +2,21 @@
import com.velocitypowered.api.scheduler.ScheduledTask;
import nl.hauntedmc.dataprovider.database.messaging.MessagingDataAccess;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityAPI;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.framework.admission.CapacityAPI;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.features.capacity.command.CapacityCommand;
import nl.hauntedmc.proxyfeatures.features.capacity.config.CapacityConfig;
import nl.hauntedmc.proxyfeatures.features.capacity.config.CapacityConfigStore;
import nl.hauntedmc.proxyfeatures.features.capacity.internal.CapacityControlPlane;
import nl.hauntedmc.proxyfeatures.features.capacity.internal.CapacityIntegrationSynchronizer;
import nl.hauntedmc.proxyfeatures.features.capacity.internal.CapacityService;
+import nl.hauntedmc.proxyfeatures.features.capacity.internal.AdmissionCapability;
+import nl.hauntedmc.proxyfeatures.api.capability.admission.AdmissionApi;
import nl.hauntedmc.proxyfeatures.features.capacity.internal.CapacitySnapshotPublisher;
import nl.hauntedmc.proxyfeatures.features.capacity.listener.CapacityListener;
-import nl.hauntedmc.proxyfeatures.features.capacity.meta.Meta;
import java.time.Duration;
import java.util.LinkedHashMap;
@@ -23,7 +24,7 @@
import java.util.Map;
import java.util.Optional;
-public final class Capacity extends VelocityBaseFeature {
+public final class Capacity extends VelocityBaseFeature {
private CapacityService service;
private CapacityIntegrationSynchronizer integrationSynchronizer;
private CapacityControlPlane controlPlane;
@@ -34,7 +35,7 @@ public final class Capacity extends VelocityBaseFeature {
private String snapshotPublisherId;
private int snapshotIntervalSeconds;
- public Capacity(FeatureContext context) {
+ public Capacity(FeatureContext context) {
super(context);
}
@@ -195,7 +196,11 @@ public void initialize() {
integrationSynchronizer.start();
service.start();
- getLifecycleManager().getApiManager().registerService(CapacityAPI.class, service);
+ getLifecycleManager().getApiManager().registerInternalService(CapacityAPI.class, service);
+ getLifecycleManager().getApiManager().registerService(
+ AdmissionApi.class,
+ new AdmissionCapability(this, service)
+ );
getLifecycleManager().getListenerManager().registerListener(new CapacityListener(this, service));
getLifecycleManager().getCommandManager().registerFeatureCommand(
new CapacityCommand(this, service, controlPlane)
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/command/CapacityCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/command/CapacityCommand.java
index c500617a..30416cb7 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/command/CapacityCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/command/CapacityCommand.java
@@ -2,24 +2,20 @@
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityScopeSnapshot;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacitySnapshot;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityState;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
import nl.hauntedmc.proxyfeatures.features.capacity.Capacity;
import nl.hauntedmc.proxyfeatures.features.capacity.config.CapacityConfig;
import nl.hauntedmc.proxyfeatures.features.capacity.config.CapacityConfigEditor;
import nl.hauntedmc.proxyfeatures.features.capacity.internal.CapacityControlPlane;
import nl.hauntedmc.proxyfeatures.features.capacity.internal.CapacityService;
+import nl.hauntedmc.proxyfeatures.framework.admission.CapacityScopeSnapshot;
+import nl.hauntedmc.proxyfeatures.framework.admission.CapacitySnapshot;
+import nl.hauntedmc.proxyfeatures.framework.admission.CapacityState;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
import java.time.DateTimeException;
import java.time.Duration;
import java.time.Instant;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-import java.util.Set;
-import java.util.TreeSet;
+import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.function.Consumer;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/config/CapacityConfig.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/config/CapacityConfig.java
index 24a4ded4..52fd179a 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/config/CapacityConfig.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/config/CapacityConfig.java
@@ -1,21 +1,12 @@
package nl.hauntedmc.proxyfeatures.features.capacity.config;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityState;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigNode;
import nl.hauntedmc.proxyfeatures.features.capacity.Capacity;
+import nl.hauntedmc.proxyfeatures.framework.admission.CapacityState;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigNode;
import java.math.BigDecimal;
import java.time.Duration;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
+import java.util.*;
/** Immutable validated Capacity configuration. A zero capacity disables that scope. */
public record CapacityConfig(
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/config/CapacityConfigStore.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/config/CapacityConfigStore.java
index 3504812f..63cf24ef 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/config/CapacityConfigStore.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/config/CapacityConfigStore.java
@@ -1,8 +1,8 @@
package nl.hauntedmc.proxyfeatures.features.capacity.config;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityState;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigNode;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigView;
+import nl.hauntedmc.proxyfeatures.framework.admission.CapacityState;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigNode;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigView;
import nl.hauntedmc.proxyfeatures.features.capacity.Capacity;
import org.spongepowered.configurate.serialize.SerializationException;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/internal/AdmissionCapability.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/internal/AdmissionCapability.java
new file mode 100644
index 00000000..5354673b
--- /dev/null
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/internal/AdmissionCapability.java
@@ -0,0 +1,193 @@
+package nl.hauntedmc.proxyfeatures.features.capacity.internal;
+
+import nl.hauntedmc.proxyfeatures.api.capability.admission.AdmissionApi;
+import nl.hauntedmc.proxyfeatures.api.capability.admission.AdmissionDecision;
+import nl.hauntedmc.proxyfeatures.api.capability.admission.AdmissionDenialReason;
+import nl.hauntedmc.proxyfeatures.api.capability.admission.AdmissionIntent;
+import nl.hauntedmc.proxyfeatures.api.capability.admission.AdmissionLease;
+import nl.hauntedmc.proxyfeatures.api.capability.admission.LeaseState;
+import nl.hauntedmc.proxyfeatures.api.capability.admission.LeaseTerminalResult;
+import nl.hauntedmc.proxyfeatures.api.capability.admission.AdmissionRequest;
+import nl.hauntedmc.proxyfeatures.api.capability.admission.AdmissionScopeSnapshot;
+import nl.hauntedmc.proxyfeatures.api.capability.admission.AdmissionSnapshot;
+import nl.hauntedmc.proxyfeatures.api.capability.admission.AdmissionState;
+import nl.hauntedmc.proxyfeatures.api.model.ServerId;
+import nl.hauntedmc.proxyfeatures.features.capacity.Capacity;
+import nl.hauntedmc.proxyfeatures.framework.admission.CapacityDecision;
+import nl.hauntedmc.proxyfeatures.framework.admission.CapacityScopeSnapshot;
+import nl.hauntedmc.proxyfeatures.framework.service.CapabilityProviderGenerationAware;
+
+import java.time.Instant;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/** Public, permission-safe projection of the internal admission engine. */
+public final class AdmissionCapability implements AdmissionApi, CapabilityProviderGenerationAware {
+ private final Capacity feature;
+ private final CapacityService service;
+ private volatile long providerGeneration;
+
+ public AdmissionCapability(Capacity feature, CapacityService service) {
+ this.feature = Objects.requireNonNull(feature, "feature");
+ this.service = Objects.requireNonNull(service, "service");
+ }
+
+ @Override
+ public void providerGeneration(long generation) {
+ if (generation <= 0L) {
+ throw new IllegalArgumentException("providerGeneration must be positive");
+ }
+ providerGeneration = generation;
+ }
+
+ @Override
+ public AdmissionDecision tryAcquire(AdmissionRequest request) {
+ Objects.requireNonNull(request, "request");
+ var player = feature.getPlugin().getProxy().getPlayer(request.playerId());
+ if (player.isEmpty()) {
+ return AdmissionDecision.deny(AdmissionDenialReason.PLAYER_OFFLINE, "player");
+ }
+ var trusted = service.createRequest(
+ player.get(),
+ request.previousServer().map(ServerId::value).orElse(""),
+ request.targetServer().value(),
+ toInternalIntent(request.intent())
+ );
+ return toPublicDecision(service.tryAcquire(trusted));
+ }
+
+ @Override
+ public AdmissionSnapshot snapshot() {
+ var source = service.snapshot();
+ Map groups = new LinkedHashMap<>();
+ source.groups().forEach((name, scope) -> groups.put(name, toPublicScope(scope)));
+ Map servers = new LinkedHashMap<>();
+ source.servers().forEach((name, scope) -> servers.put(ServerId.of(name), toPublicScope(scope)));
+ return new AdmissionSnapshot(
+ toPublicScope(source.proxy()),
+ toPublicScope(source.gameplay()),
+ groups,
+ servers,
+ source.activeLeases(),
+ Instant.now()
+ );
+ }
+
+ private AdmissionDecision toPublicDecision(CapacityDecision decision) {
+ if (decision.allowed()) {
+ return AdmissionDecision.allow(new LeaseAdapter(decision.lease(), providerGeneration()));
+ }
+ return AdmissionDecision.deny(switch (decision.reason()) {
+ case NONE -> throw new IllegalStateException("Denied capacity decision has no reason");
+ case FULL -> AdmissionDenialReason.CAPACITY;
+ case SERVER_STATE -> AdmissionDenialReason.SERVER_STATE;
+ case PROXY_HARD_LIMIT -> AdmissionDenialReason.PROXY_HARD_LIMIT;
+ case UNKNOWN_TARGET -> AdmissionDenialReason.UNKNOWN_TARGET;
+ case INVALID_REQUEST -> AdmissionDenialReason.INVALID_REQUEST;
+ }, decision.blockingScope());
+ }
+
+ private long providerGeneration() {
+ long generation = providerGeneration;
+ if (generation <= 0L) {
+ throw new IllegalStateException("Admission capability has not been published");
+ }
+ return generation;
+ }
+
+ private static AdmissionScopeSnapshot toPublicScope(CapacityScopeSnapshot source) {
+ return new AdmissionScopeSnapshot(
+ source.name(),
+ source.capacity(),
+ source.reservedSlots(),
+ source.occupied(),
+ source.pending(),
+ source.restorationReserved(),
+ AdmissionState.valueOf(source.state().name())
+ );
+ }
+
+ private static nl.hauntedmc.proxyfeatures.framework.admission.AdmissionIntent toInternalIntent(
+ AdmissionIntent intent
+ ) {
+ return switch (intent) {
+ case NORMAL -> nl.hauntedmc.proxyfeatures.framework.admission.AdmissionIntent.NORMAL;
+ case QUEUE_ADVANCE -> nl.hauntedmc.proxyfeatures.framework.admission.AdmissionIntent.QUEUE_ADVANCE;
+ case RESTART_RETURN -> nl.hauntedmc.proxyfeatures.framework.admission.AdmissionIntent.RESTART_RETURN;
+ case MAINTENANCE_EVACUATION ->
+ nl.hauntedmc.proxyfeatures.framework.admission.AdmissionIntent.MAINTENANCE_EVACUATION;
+ case SECURITY_ROUTE -> nl.hauntedmc.proxyfeatures.framework.admission.AdmissionIntent.SECURITY_ROUTE;
+ case PLUGIN -> nl.hauntedmc.proxyfeatures.framework.admission.AdmissionIntent.INTERNAL;
+ };
+ }
+
+ static final class LeaseAdapter implements AdmissionLease {
+ private final nl.hauntedmc.proxyfeatures.framework.admission.CapacityLease delegate;
+ private final long providerGeneration;
+
+ LeaseAdapter(nl.hauntedmc.proxyfeatures.framework.admission.CapacityLease delegate, long providerGeneration) {
+ this.delegate = Objects.requireNonNull(delegate, "delegate");
+ if (providerGeneration < 0) {
+ throw new IllegalArgumentException("providerGeneration must be non-negative");
+ }
+ this.providerGeneration = providerGeneration;
+ }
+
+ @Override
+ public java.util.UUID id() {
+ return delegate.id();
+ }
+
+ @Override
+ public java.util.UUID playerId() {
+ return delegate.playerId();
+ }
+
+ @Override
+ public ServerId targetServer() {
+ return ServerId.of(delegate.targetServer());
+ }
+
+ @Override
+ public AdmissionIntent intent() {
+ return AdmissionIntent.valueOf(delegate.intent().name().replace("INTERNAL", "PLUGIN"));
+ }
+
+ @Override
+ public Instant expiresAt() {
+ return delegate.expiresAt();
+ }
+
+ @Override
+ public boolean isActive() {
+ return state() == LeaseState.ACTIVE;
+ }
+
+ @Override
+ public LeaseState state() {
+ return LeaseState.valueOf(delegate.state().name());
+ }
+
+ @Override
+ public long providerGeneration() {
+ return providerGeneration;
+ }
+
+ @Override
+ public LeaseTerminalResult commit() {
+ if (state() == LeaseState.ACTIVE) {
+ delegate.commit();
+ }
+ return new LeaseTerminalResult(state(), providerGeneration());
+ }
+
+ @Override
+ public LeaseTerminalResult release() {
+ if (state() == LeaseState.ACTIVE) {
+ delegate.release();
+ }
+ return new LeaseTerminalResult(state(), providerGeneration());
+ }
+ }
+}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/internal/CapacityControlPlane.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/internal/CapacityControlPlane.java
index d30a3b77..eee7f105 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/internal/CapacityControlPlane.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/internal/CapacityControlPlane.java
@@ -1,7 +1,7 @@
package nl.hauntedmc.proxyfeatures.features.capacity.internal;
import com.velocitypowered.api.scheduler.ScheduledTask;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityState;
+import nl.hauntedmc.proxyfeatures.framework.admission.CapacityState;
import nl.hauntedmc.proxyfeatures.features.capacity.Capacity;
import nl.hauntedmc.proxyfeatures.features.capacity.config.CapacityConfig;
import nl.hauntedmc.proxyfeatures.features.capacity.config.CapacityConfigStore;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/internal/CapacityIntegrationSynchronizer.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/internal/CapacityIntegrationSynchronizer.java
index 049629e9..04e35251 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/internal/CapacityIntegrationSynchronizer.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/internal/CapacityIntegrationSynchronizer.java
@@ -1,14 +1,13 @@
package nl.hauntedmc.proxyfeatures.features.capacity.internal;
import com.velocitypowered.api.scheduler.ScheduledTask;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityState;
-import nl.hauntedmc.proxyfeatures.features.maintenance.Maintenance;
+import nl.hauntedmc.proxyfeatures.api.capability.operations.MaintenanceApi;
import nl.hauntedmc.proxyfeatures.features.capacity.Capacity;
-import nl.hauntedmc.proxyfeatures.features.restart.Restart;
+import nl.hauntedmc.proxyfeatures.framework.admission.CapacityState;
+import nl.hauntedmc.proxyfeatures.framework.admission.RestartCoordinationPort;
import java.time.Duration;
import java.util.HashSet;
-import java.util.Locale;
import java.util.Set;
/** Mirrors independently owned operational restrictions into Capacity state claims. */
@@ -19,7 +18,7 @@ public final class CapacityIntegrationSynchronizer {
private final CapacityService service;
private final Set maintainedServers = new HashSet<>();
private ScheduledTask task;
- private Restart attachedRestart;
+ private RestartCoordinationPort attachedRestart;
private boolean closed;
public CapacityIntegrationSynchronizer(Capacity feature, CapacityService service) {
@@ -83,14 +82,12 @@ private synchronized void sync() {
private void syncMaintenance() {
Set active = new HashSet<>();
- var loaded = feature.getPlugin().getFeatureLoadManager().getFeatureRegistry()
- .getLoadedFeature("Maintenance");
- if (loaded instanceof Maintenance maintenance && maintenance.getHandler() != null) {
- maintenance.getHandler().getActiveGamemodes().stream()
- .map(CapacityIntegrationSynchronizer::normalize)
- .filter(server -> !server.isBlank())
- .forEach(active::add);
- }
+ feature.findCapability(MaintenanceApi.class)
+ .map(MaintenanceApi::snapshot)
+ .stream()
+ .flatMap(snapshot -> snapshot.servers().stream())
+ .map(server -> server.value())
+ .forEach(active::add);
Set all = new HashSet<>(maintainedServers);
all.addAll(active);
@@ -104,25 +101,22 @@ private void syncMaintenance() {
}
private void syncRestart() {
- var loaded = feature.getPlugin().getFeatureLoadManager().getFeatureRegistry()
- .getLoadedFeature("Restart");
- Restart current = loaded instanceof Restart restart ? restart : null;
+ RestartCoordinationPort current = feature.findInternalService(RestartCoordinationPort.class)
+ .orElse(null);
if (attachedRestart != null && attachedRestart != current) {
- attachedRestart.detachCapacity(service);
+ attachedRestart.detachAdmission(service);
attachedRestart = null;
}
- if (current != null && current.attachCapacity(service)) {
+ if (current != null) {
+ current.attachAdmission(service);
attachedRestart = current;
}
}
private void detachRestart() {
if (attachedRestart == null) return;
- attachedRestart.detachCapacity(service);
+ attachedRestart.detachAdmission(service);
attachedRestart = null;
}
- private static String normalize(String value) {
- return value == null ? "" : value.trim().toLowerCase(Locale.ROOT);
- }
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/internal/CapacityService.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/internal/CapacityService.java
index 8c7faa16..461c5163 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/internal/CapacityService.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/internal/CapacityService.java
@@ -3,34 +3,16 @@
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.scheduler.ScheduledTask;
-import nl.hauntedmc.proxyfeatures.api.capacity.AdmissionIntent;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityAPI;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityDecision;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityDenialReason;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityLease;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityRequest;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityScopeSnapshot;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacitySnapshot;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityState;
-import nl.hauntedmc.proxyfeatures.api.queue.QueueAdmissionAPI;
+import nl.hauntedmc.proxyfeatures.api.capability.operations.MaintenanceApi;
+import nl.hauntedmc.proxyfeatures.api.capability.operations.MaintenanceScope;
+import nl.hauntedmc.proxyfeatures.api.model.ServerId;
import nl.hauntedmc.proxyfeatures.features.capacity.Capacity;
import nl.hauntedmc.proxyfeatures.features.capacity.config.CapacityConfig;
-import nl.hauntedmc.proxyfeatures.framework.service.FeatureServices;
+import nl.hauntedmc.proxyfeatures.framework.admission.*;
import java.time.Duration;
import java.time.Instant;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.UUID;
+import java.util.*;
/**
* Local, atomic admission coordinator. All mutable admission state is guarded by this instance.
@@ -110,6 +92,33 @@ public CapacityConfig config() {
return config;
}
+ @Override
+ public CapacityRequest createRequest(
+ Player player,
+ String previousServer,
+ String targetServer,
+ AdmissionIntent intent
+ ) {
+ Objects.requireNonNull(player, "player");
+ String target = normalize(targetServer);
+ boolean maintenanceBypass = ServerId.optional(target).flatMap(server ->
+ feature.findCapability(MaintenanceApi.class)
+ .map(api -> api.mayBypass(
+ player.getUniqueId(),
+ MaintenanceScope.server(server)
+ ))).orElse(false);
+ CapacityConfig current = config;
+ return new CapacityRequest(
+ player.getUniqueId(),
+ previousServer,
+ target,
+ intent,
+ player.hasPermission(current.reservedPermission()),
+ player.hasPermission(current.capacityBypassPermission()),
+ player.hasPermission(current.stateBypassPermission()) || maintenanceBypass
+ );
+ }
+
public synchronized void shutdown() {
if (closed) return;
closed = true;
@@ -118,7 +127,7 @@ public synchronized void shutdown() {
reconciliationTask = null;
}
for (LeaseImpl lease : new ArrayList<>(leasesById.values())) {
- releaseLease(lease, false);
+ releaseLease(lease, CapacityLeaseState.INVALIDATED, false);
}
leasesById.clear();
pendingByPlayer.clear();
@@ -199,13 +208,13 @@ public synchronized CapacityDecision tryAcquire(CapacityRequest request) {
|| existing.configGeneration != configGeneration)) {
// Prepared connections are checked again by the final Velocity event. Reacquire whenever
// origin, intent, permission-derived flags or the validated configuration changed.
- releaseLease(existing, true);
+ releaseLease(existing, CapacityLeaseState.INVALIDATED, true);
existing = null;
}
CapacityState state = effectiveState(request.targetServer());
if (!request.stateBypass() && !state.acceptsNormalAdmissions()) {
- if (existing != null) releaseLease(existing, true);
+ if (existing != null) releaseLease(existing, CapacityLeaseState.INVALIDATED, true);
return CapacityDecision.deny(
CapacityDenialReason.SERVER_STATE,
"server:" + request.targetServer()
@@ -297,21 +306,21 @@ public synchronized void connected(UUID playerId, String serverName) {
if (lease.targetServer().equals(normalize(serverName))) {
commitLease(lease);
} else {
- releaseLease(lease, true);
+ releaseLease(lease, CapacityLeaseState.RELEASED, true);
}
}
public synchronized void connectionFailed(UUID playerId, String targetServer) {
LeaseImpl lease = pendingByPlayer.get(playerId);
if (lease != null && lease.targetServer().equals(normalize(targetServer))) {
- releaseLease(lease, true);
+ releaseLease(lease, CapacityLeaseState.RELEASED, true);
}
}
public synchronized void disconnected(UUID playerId) {
releaseLogin(playerId);
LeaseImpl lease = pendingByPlayer.get(playerId);
- if (lease != null) releaseLease(lease, true);
+ if (lease != null) releaseLease(lease, CapacityLeaseState.RELEASED, true);
}
public boolean isQueueable(String serverName) {
@@ -338,7 +347,8 @@ public synchronized void setServerState(String serverName, CapacityState state,
if (!effective.acceptsNormalAdmissions()) {
revokeBlockedLeases(server);
} else if (previous != CapacityState.OPEN) {
- FeatureServices.find(feature, QueueAdmissionAPI.class).ifPresent(api -> api.wake(server));
+ feature.findInternalService(QueueAdmissionPort.class)
+ .ifPresent(api -> api.capacityChanged(ServerId.of(server)));
}
}
@@ -575,7 +585,7 @@ private void revokeBlockedLeases(String targetServer) {
if (targetServer != null && !lease.targetServer().equals(targetServer)) continue;
if (!lease.request.stateBypass()
&& !effectiveState(lease.targetServer()).acceptsNormalAdmissions()) {
- releaseLease(lease, true);
+ releaseLease(lease, CapacityLeaseState.INVALIDATED, true);
}
}
}
@@ -607,7 +617,7 @@ private void restoreRestoration(LeaseImpl lease) {
private void cleanupExpired(long now) {
loginReservations.entrySet().removeIf(entry -> entry.getValue().expiresAtMillis <= now);
for (LeaseImpl lease : new ArrayList<>(leasesById.values())) {
- if (!lease.isActiveAt(now)) releaseLease(lease, true);
+ if (!lease.isActiveAt(now)) releaseLease(lease, CapacityLeaseState.EXPIRED, true);
}
restorationsByServer.values().forEach(map ->
map.entrySet().removeIf(entry -> entry.getValue().expiresAtMillis <= now));
@@ -639,20 +649,22 @@ private Set configuredServerNames() {
}
private void wakeQueues() {
- FeatureServices.find(feature, QueueAdmissionAPI.class).ifPresent(api ->
- configuredServerNames().forEach(api::wake));
+ feature.findInternalService(QueueAdmissionPort.class).ifPresent(api ->
+ configuredServerNames().forEach(server -> api.capacityChanged(ServerId.of(server))));
}
private void commitLease(LeaseImpl lease) {
if (lease == null || !lease.active) return;
lease.active = false;
+ lease.state = CapacityLeaseState.COMMITTED;
leasesById.remove(lease.id());
pendingByPlayer.remove(lease.playerId(), lease);
}
- private void releaseLease(LeaseImpl lease, boolean restoreClaim) {
+ private void releaseLease(LeaseImpl lease, CapacityLeaseState state, boolean restoreClaim) {
if (lease == null || !lease.active) return;
lease.active = false;
+ lease.state = state;
leasesById.remove(lease.id());
pendingByPlayer.remove(lease.playerId(), lease);
if (restoreClaim) restoreRestoration(lease);
@@ -706,6 +718,7 @@ private final class LeaseImpl implements CapacityLease {
private final Restoration claimedRestoration;
private final long configGeneration;
private boolean active = true;
+ private CapacityLeaseState state = CapacityLeaseState.ACTIVE;
private LeaseImpl(
UUID id,
@@ -748,7 +761,17 @@ public Instant expiresAt() {
@Override
public boolean isActive() {
synchronized (CapacityService.this) {
- return isActiveAt(System.currentTimeMillis());
+ return state() == CapacityLeaseState.ACTIVE;
+ }
+ }
+
+ @Override
+ public CapacityLeaseState state() {
+ synchronized (CapacityService.this) {
+ if (active && !isActiveAt(System.currentTimeMillis())) {
+ releaseLease(this, CapacityLeaseState.EXPIRED, true);
+ }
+ return state;
}
}
@@ -757,16 +780,23 @@ private boolean isActiveAt(long now) {
}
@Override
- public void commit() {
+ public boolean commit() {
synchronized (CapacityService.this) {
+ if (!isActiveAt(System.currentTimeMillis())) {
+ releaseLease(this, CapacityLeaseState.EXPIRED, true);
+ return false;
+ }
commitLease(this);
+ return true;
}
}
@Override
- public void release() {
+ public boolean release() {
synchronized (CapacityService.this) {
- releaseLease(this, true);
+ if (!active) return false;
+ releaseLease(this, CapacityLeaseState.RELEASED, true);
+ return true;
}
}
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/internal/CapacitySnapshotPublisher.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/internal/CapacitySnapshotPublisher.java
index 8b726fc2..324c8e27 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/internal/CapacitySnapshotPublisher.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/internal/CapacitySnapshotPublisher.java
@@ -1,8 +1,8 @@
package nl.hauntedmc.proxyfeatures.features.capacity.internal;
import nl.hauntedmc.dataprovider.database.messaging.MessagingDataAccess;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityScopeSnapshot;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacitySnapshot;
+import nl.hauntedmc.proxyfeatures.framework.admission.CapacityScopeSnapshot;
+import nl.hauntedmc.proxyfeatures.framework.admission.CapacitySnapshot;
import nl.hauntedmc.proxyfeatures.contracts.messaging.CapacitySnapshotMessage;
import nl.hauntedmc.proxyfeatures.features.capacity.Capacity;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/listener/CapacityListener.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/listener/CapacityListener.java
index 3f1f45e3..d8ae8340 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/listener/CapacityListener.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/listener/CapacityListener.java
@@ -10,19 +10,20 @@
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import net.kyori.adventure.text.Component;
-import nl.hauntedmc.proxyfeatures.api.queue.QueueAdmissionAPI;
-import nl.hauntedmc.proxyfeatures.api.capacity.AdmissionIntent;
-import nl.hauntedmc.proxyfeatures.api.capacity.RestartAdmissionAPI;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityDecision;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityDenialReason;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityRequest;
-import nl.hauntedmc.proxyfeatures.features.maintenance.Maintenance;
+import nl.hauntedmc.proxyfeatures.api.capability.operations.MaintenanceApi;
+import nl.hauntedmc.proxyfeatures.api.capability.operations.MaintenanceScope;
+import nl.hauntedmc.proxyfeatures.api.capability.operations.RestartApi;
+import nl.hauntedmc.proxyfeatures.api.capability.operations.TwoFactorApi;
+import nl.hauntedmc.proxyfeatures.api.model.ServerId;
+import nl.hauntedmc.proxyfeatures.framework.admission.AdmissionIntent;
+import nl.hauntedmc.proxyfeatures.framework.admission.CapacityDecision;
+import nl.hauntedmc.proxyfeatures.framework.admission.CapacityDenialReason;
+import nl.hauntedmc.proxyfeatures.framework.admission.CapacityRequest;
import nl.hauntedmc.proxyfeatures.features.capacity.Capacity;
import nl.hauntedmc.proxyfeatures.features.capacity.config.CapacityConfig;
import nl.hauntedmc.proxyfeatures.features.capacity.internal.CapacityService;
-import nl.hauntedmc.proxyfeatures.features.twofactor.TwoFactor;
+import nl.hauntedmc.proxyfeatures.framework.admission.QueueAdmissionPort;
import nl.hauntedmc.proxyfeatures.framework.log.ConnectionLogHelper;
-import nl.hauntedmc.proxyfeatures.framework.service.FeatureServices;
import java.util.Locale;
@@ -93,16 +94,7 @@ public void onPreConnect(ServerPreConnectEvent event) {
.filter(prepared -> prepared == AdmissionIntent.QUEUE_ADVANCE
|| prepared == AdmissionIntent.RESTART_RETURN)
.orElseGet(() -> resolveIntent(player, previous, original, targetName));
- boolean maintenanceBypass = hasMaintenanceBypass(player, targetName);
- request = new CapacityRequest(
- player.getUniqueId(),
- previous,
- targetName,
- intent,
- player.hasPermission(config.reservedPermission()),
- player.hasPermission(config.capacityBypassPermission()),
- player.hasPermission(config.stateBypassPermission()) || maintenanceBypass
- );
+ request = service.createRequest(player, previous, targetName, intent);
decision = service.tryAcquire(request);
} catch (Throwable error) {
@@ -159,62 +151,43 @@ public void onDisconnect(DisconnectEvent event) {
}
private AdmissionIntent resolveIntent(Player player, String previous, String original, String target) {
- Maintenance maintenance = maintenance();
- if (maintenance != null && maintenance.getHandler() != null) {
+ MaintenanceApi maintenance = feature.findCapability(MaintenanceApi.class).orElse(null);
+ if (maintenance != null) {
boolean redirectedFromMaintained = !original.isBlank()
&& !original.equals(target)
- && maintenance.getHandler().isGamemodeActive(original);
+ && maintenance.isActive(MaintenanceScope.server(ServerId.of(original)));
boolean evacuatingMaintained = !previous.isBlank()
&& !previous.equals(target)
- && maintenance.getHandler().isGamemodeActive(previous);
+ && maintenance.isActive(MaintenanceScope.server(ServerId.of(previous)));
if (redirectedFromMaintained || evacuatingMaintained) {
return AdmissionIntent.MAINTENANCE_EVACUATION;
}
}
- boolean restartReturn = FeatureServices.find(feature, RestartAdmissionAPI.class)
- .map(api -> api.isRestartReturn(player.getUniqueId(), target))
+ boolean restartReturn = feature.findCapability(RestartApi.class)
+ .map(api -> api.isExpectedReturn(player.getUniqueId(), ServerId.of(target)))
.orElse(false);
if (restartReturn) return AdmissionIntent.RESTART_RETURN;
- TwoFactor twoFactor = twoFactor();
+ TwoFactorApi twoFactor = feature.findCapability(TwoFactorApi.class).orElse(null);
boolean twoFactorRoute = previous.isBlank()
&& !original.isBlank()
&& !original.equals(target)
&& twoFactor != null
- && twoFactor.getService() != null
- && twoFactor.getService().isLocked(player)
- && twoFactor.isLockServer(target);
+ && twoFactor.isLocked(player.getUniqueId())
+ && twoFactor.isAuthenticationServer(ServerId.of(target));
return twoFactorRoute ? AdmissionIntent.SECURITY_ROUTE : AdmissionIntent.NORMAL;
}
- private boolean hasMaintenanceBypass(Player player, String target) {
- Maintenance maintenance = maintenance();
- return maintenance != null && maintenance.getHandler() != null
- && maintenance.getHandler().hasGamemodeBypass(player, target);
- }
-
- private Maintenance maintenance() {
- var loaded = feature.getPlugin().getFeatureLoadManager().getFeatureRegistry()
- .getLoadedFeature("Maintenance");
- return loaded instanceof Maintenance maintenance ? maintenance : null;
- }
-
- private TwoFactor twoFactor() {
- var loaded = feature.getPlugin().getFeatureLoadManager().getFeatureRegistry()
- .getLoadedFeature("TwoFactor");
- return loaded instanceof TwoFactor twoFactor ? twoFactor : null;
- }
-
private boolean enqueue(Player player, String serverName, CapacityDenialReason reason, CapacityRequest request) {
- return FeatureServices.find(feature, QueueAdmissionAPI.class)
- .filter(api -> api.isQueueEnabled(serverName))
- .map(api -> api.enqueue(player, serverName, reason, request))
+ ServerId server = ServerId.of(serverName);
+ return feature.findInternalService(QueueAdmissionPort.class)
+ .map(api -> api.enqueueDenied(player, server, reason, request))
.orElse(false);
}
private boolean consumeCancelledQueueAdvance(Player player, String serverName) {
- return FeatureServices.find(feature, QueueAdmissionAPI.class)
- .map(api -> api.consumeCancelledAdvance(player.getUniqueId(), serverName))
+ return feature.findInternalService(QueueAdmissionPort.class)
+ .map(api -> api.consumeCancelledAdvance(player.getUniqueId(), ServerId.of(serverName)))
.orElse(false);
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/meta/Meta.java
deleted file mode 100644
index bac9c0ee..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/capacity/meta/Meta.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.capacity.meta;
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-public final class Meta implements BaseMeta {
- @Override
- public String getFeatureName() {
- return "Capacity";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.4.0";
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/clientinfo/ClientInfo.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/clientinfo/ClientInfo.java
index ecf596b1..649519c8 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/clientinfo/ClientInfo.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/clientinfo/ClientInfo.java
@@ -1,33 +1,28 @@
package nl.hauntedmc.proxyfeatures.features.clientinfo;
import nl.hauntedmc.dataprovider.api.orm.ORMContext;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.features.clientinfo.command.ClientInfoBrigadierCommand;
import nl.hauntedmc.proxyfeatures.features.clientinfo.entity.PlayerClientInfoChannelEntity;
import nl.hauntedmc.proxyfeatures.features.clientinfo.entity.PlayerClientInfoEntity;
import nl.hauntedmc.proxyfeatures.features.clientinfo.entity.PlayerClientInfoModEntity;
import nl.hauntedmc.proxyfeatures.features.clientinfo.entity.PlayerClientInfoSettingsEntity;
-import nl.hauntedmc.proxyfeatures.features.clientinfo.internal.ClientInfoAdvisor;
-import nl.hauntedmc.proxyfeatures.features.clientinfo.internal.ClientInfoConfig;
-import nl.hauntedmc.proxyfeatures.features.clientinfo.internal.ClientInfoPersistenceService;
-import nl.hauntedmc.proxyfeatures.features.clientinfo.internal.ClientInfoSettingsService;
-import nl.hauntedmc.proxyfeatures.features.clientinfo.internal.ClientTelemetryService;
+import nl.hauntedmc.proxyfeatures.features.clientinfo.internal.*;
import nl.hauntedmc.proxyfeatures.features.clientinfo.listener.PlayerListener;
-import nl.hauntedmc.proxyfeatures.features.clientinfo.meta.Meta;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReferenceResolver;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
import java.util.Map;
-public class ClientInfo extends VelocityBaseFeature {
+public class ClientInfo extends VelocityBaseFeature {
public static final String STAFF_DETAILS_PERMISSION = "proxyfeatures.feature.clientinfo.command.staff";
private ClientInfoAdvisor advisor;
- public ClientInfo(FeatureContext context) {
+ public ClientInfo(FeatureContext context) {
super(context);
}
@@ -180,10 +175,7 @@ public void initialize() {
PlayerClientInfoChannelEntity.class)
.orElseThrow();
- PlayerReferenceResolver playerResolver = new PlayerReferenceResolver(
- getPlugin().getDataRegistry()
- .orElseThrow(() -> new IllegalStateException("DataRegistryApi is required for ClientInfo."))
- );
+ PlayerReferenceResolver playerResolver = getPlugin().getPlayerReferenceResolver();
ClientInfoSettingsService settingsService = new ClientInfoSettingsService(ormContext, playerResolver);
ClientTelemetryService telemetryService = new ClientTelemetryService();
ClientInfoPersistenceService persistenceService = new ClientInfoPersistenceService(
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/clientinfo/command/ClientInfoBrigadierCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/clientinfo/command/ClientInfoBrigadierCommand.java
index 28a89b10..b0b197d4 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/clientinfo/command/ClientInfoBrigadierCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/clientinfo/command/ClientInfoBrigadierCommand.java
@@ -9,7 +9,7 @@
import com.mojang.brigadier.tree.LiteralCommandNode;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
-import nl.hauntedmc.proxyfeatures.api.command.brigadier.BrigadierCommand;
+import nl.hauntedmc.proxyfeatures.framework.command.brigadier.BrigadierCommand;
import nl.hauntedmc.proxyfeatures.features.clientinfo.ClientInfo;
import nl.hauntedmc.proxyfeatures.features.clientinfo.internal.ClientInfoAdvisor;
import org.jetbrains.annotations.NotNull;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/clientinfo/internal/ClientInfoConfig.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/clientinfo/internal/ClientInfoConfig.java
index 705351d3..b02609e5 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/clientinfo/internal/ClientInfoConfig.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/clientinfo/internal/ClientInfoConfig.java
@@ -1,8 +1,8 @@
package nl.hauntedmc.proxyfeatures.features.clientinfo.internal;
import com.velocitypowered.api.proxy.player.PlayerSettings;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigNode;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigView;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigNode;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigView;
import java.util.ArrayList;
import java.util.List;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/clientinfo/internal/ClientInfoPersistenceService.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/clientinfo/internal/ClientInfoPersistenceService.java
index 6988c2a1..bf6ecae0 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/clientinfo/internal/ClientInfoPersistenceService.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/clientinfo/internal/ClientInfoPersistenceService.java
@@ -74,7 +74,7 @@ private void runPersist(UUID uuid, long generation) {
private void persistSnapshot(ClientTelemetryService.SessionSnapshot snapshot) {
try {
orm.runInTransaction(session -> {
- PlayerReference player = playerResolver.resolveManaged(session, snapshot.uuid());
+ PlayerReference player = playerResolver.resolveReference(snapshot.uuid());
if (player == null) {
return null;
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/clientinfo/internal/ClientInfoSettingsService.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/clientinfo/internal/ClientInfoSettingsService.java
index 5290cdde..92a30fbf 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/clientinfo/internal/ClientInfoSettingsService.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/clientinfo/internal/ClientInfoSettingsService.java
@@ -61,7 +61,7 @@ private PlayerClientInfoSettingsEntity loadSettings(UUID uuid, String username)
}
private PlayerReference findPlayer(org.hibernate.Session session, UUID uuid, String username) {
- return playerResolver.resolveManaged(session, uuid);
+ return playerResolver.resolveReference(uuid);
}
public Optional getPlayerReference(UUID uuid) {
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/clientinfo/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/clientinfo/meta/Meta.java
deleted file mode 100644
index aa07c193..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/clientinfo/meta/Meta.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.clientinfo.meta;
-
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-import java.util.List;
-
-public class Meta implements BaseMeta {
-
- @Override
- public String getFeatureName() {
- return "ClientInfo";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.1.0";
- }
-
- @Override
- public List getPluginDependencies() {
- return List.of(DATA_PROVIDER, DATA_REGISTRY);
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandhider/CommandHider.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandhider/CommandHider.java
index aabfc2e2..802407dc 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandhider/CommandHider.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandhider/CommandHider.java
@@ -1,21 +1,20 @@
package nl.hauntedmc.proxyfeatures.features.commandhider;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.features.commandhider.command.CommandHiderCommand;
import nl.hauntedmc.proxyfeatures.features.commandhider.internal.HiderHandler;
import nl.hauntedmc.proxyfeatures.features.commandhider.listener.AvailableCommandListener;
-import nl.hauntedmc.proxyfeatures.features.commandhider.meta.Meta;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
import java.util.List;
-public final class CommandHider extends VelocityBaseFeature {
+public final class CommandHider extends VelocityBaseFeature {
private HiderHandler hiderHandler;
- public CommandHider(FeatureContext context) {
+ public CommandHider(FeatureContext context) {
super(context);
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandhider/command/CommandHiderCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandhider/command/CommandHiderCommand.java
index aa6afbbe..423311c7 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandhider/command/CommandHiderCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandhider/command/CommandHiderCommand.java
@@ -7,7 +7,7 @@
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import com.mojang.brigadier.tree.LiteralCommandNode;
import com.velocitypowered.api.command.CommandSource;
-import nl.hauntedmc.proxyfeatures.api.command.brigadier.BrigadierCommand;
+import nl.hauntedmc.proxyfeatures.framework.command.brigadier.BrigadierCommand;
import nl.hauntedmc.proxyfeatures.features.commandhider.CommandHider;
import nl.hauntedmc.proxyfeatures.features.commandhider.internal.HiderHandler;
import org.jetbrains.annotations.NotNull;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandhider/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandhider/meta/Meta.java
deleted file mode 100644
index c903d0c5..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandhider/meta/Meta.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.commandhider.meta;
-
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-public class Meta implements BaseMeta {
-
- @Override
- public String getFeatureName() {
- return "CommandHider";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.1.0";
- }
-
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandlogger/CommandLogger.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandlogger/CommandLogger.java
index 2f3aac62..feda5a61 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandlogger/CommandLogger.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandlogger/CommandLogger.java
@@ -1,23 +1,22 @@
package nl.hauntedmc.proxyfeatures.features.commandlogger;
import nl.hauntedmc.dataprovider.api.orm.ORMContext;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.features.commandlogger.entity.CommandExecutionEntity;
import nl.hauntedmc.proxyfeatures.features.commandlogger.internal.LogHandler;
import nl.hauntedmc.proxyfeatures.features.commandlogger.listener.CommandListener;
-import nl.hauntedmc.proxyfeatures.features.commandlogger.meta.Meta;
import nl.hauntedmc.proxyfeatures.features.commandlogger.service.CommandLogService;
-public class CommandLogger extends VelocityBaseFeature {
+public class CommandLogger extends VelocityBaseFeature {
private LogHandler logHandler;
private CommandLogService commandLogService;
private ORMContext ormContext;
- public CommandLogger(FeatureContext context) {
+ public CommandLogger(FeatureContext context) {
super(context);
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandlogger/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandlogger/meta/Meta.java
deleted file mode 100644
index ff69fbb8..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandlogger/meta/Meta.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.commandlogger.meta;
-
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-import java.util.List;
-
-public class Meta implements BaseMeta {
-
- @Override
- public String getFeatureName() {
- return "CommandLogger";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.0.0";
- }
-
- @Override
- public List getPluginDependencies() {
- return List.of(DATA_PROVIDER, DATA_REGISTRY);
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandlogger/service/CommandLogService.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandlogger/service/CommandLogService.java
index 2d2b4447..6745dfbf 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandlogger/service/CommandLogService.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandlogger/service/CommandLogService.java
@@ -2,12 +2,10 @@
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
-import nl.hauntedmc.dataregistry.api.DataRegistryApi;
-import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReference;
-import nl.hauntedmc.dataregistry.api.player.PlayerDirectory;
import nl.hauntedmc.proxyfeatures.features.commandlogger.CommandLogger;
-import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReferenceResolver;
import nl.hauntedmc.proxyfeatures.features.commandlogger.entity.CommandExecutionEntity;
+import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReference;
+import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReferenceResolver;
import java.util.Locale;
@@ -19,18 +17,12 @@ public class CommandLogService {
private final PlayerReferenceResolver playerResolver;
public CommandLogService(CommandLogger feature) {
- this(feature, feature.getPlugin().getDataRegistry()
- .orElseThrow(() -> new IllegalStateException("DataRegistryApi is required for CommandLogger.")));
- }
-
- CommandLogService(CommandLogger feature, DataRegistryApi dataRegistry) {
- this.feature = feature;
- this.playerResolver = new PlayerReferenceResolver(dataRegistry);
+ this(feature, feature.getPlugin().getPlayerReferenceResolver());
}
- CommandLogService(CommandLogger feature, PlayerDirectory playerDirectory) {
+ CommandLogService(CommandLogger feature, PlayerReferenceResolver playerResolver) {
this.feature = feature;
- this.playerResolver = new PlayerReferenceResolver(playerDirectory);
+ this.playerResolver = playerResolver;
}
/**
@@ -74,7 +66,7 @@ private void schedulePersist(java.util.UUID playerUuid, String sourceLabel, Stri
private void persist(java.util.UUID playerUuid, String sourceLabel, String fullCommand, long timestamp) {
feature.getOrmContext().runInTransaction(session -> {
- PlayerReference playerEntity = playerUuid == null ? null : playerResolver.resolveManaged(session, playerUuid);
+ PlayerReference playerEntity = playerUuid == null ? null : playerResolver.resolveReference(playerUuid);
if (playerUuid != null && playerEntity == null) {
return null;
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandrelay/CommandRelay.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandrelay/CommandRelay.java
index 8a9167e1..8f03fa35 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandrelay/CommandRelay.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandrelay/CommandRelay.java
@@ -6,18 +6,17 @@
import nl.hauntedmc.dataprovider.database.messaging.durable.DurableMessagingDataAccess;
import nl.hauntedmc.proxyfeatures.features.commandrelay.audit.CommandRelayAuditLogEntity;
import nl.hauntedmc.proxyfeatures.features.commandrelay.audit.CommandRelayAuditLogService;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.features.commandrelay.internal.EventBusHandler;
-import nl.hauntedmc.proxyfeatures.features.commandrelay.meta.Meta;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
-public class CommandRelay extends VelocityBaseFeature {
+public class CommandRelay extends VelocityBaseFeature {
private static final String STREAM = "proxy.commandrelay.command";
private static final String DEFAULT_CONSUMER_GROUP = "proxyfeatures.commandrelay.proxy";
@@ -26,7 +25,7 @@ public class CommandRelay extends VelocityBaseFeature {
private EventBusHandler eventBusHandler;
private CommandRelayAuditLogService auditLogService;
- public CommandRelay(FeatureContext context) {
+ public CommandRelay(FeatureContext context) {
super(context);
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandrelay/internal/EventBusHandler.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandrelay/internal/EventBusHandler.java
index 9379b1f2..8c278d09 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandrelay/internal/EventBusHandler.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandrelay/internal/EventBusHandler.java
@@ -8,10 +8,9 @@
import nl.hauntedmc.dataprovider.database.messaging.durable.DurableMessagingDataAccess;
import nl.hauntedmc.dataprovider.database.messaging.durable.DurableSubscription;
import nl.hauntedmc.dataprovider.database.messaging.durable.PublishedDurableEvent;
-import nl.hauntedmc.proxyfeatures.api.io.cache.CacheDirectory;
-import nl.hauntedmc.proxyfeatures.api.io.cache.CacheType;
-import nl.hauntedmc.proxyfeatures.api.io.cache.FileCacheStore;
-import nl.hauntedmc.proxyfeatures.api.util.type.CastUtils;
+import nl.hauntedmc.proxyfeatures.toolkit.io.cache.CacheDirectory;
+import nl.hauntedmc.proxyfeatures.toolkit.io.cache.CacheType;
+import nl.hauntedmc.proxyfeatures.toolkit.io.cache.FileCacheStore;
import nl.hauntedmc.proxyfeatures.contracts.messaging.CommandRelayMessage;
import nl.hauntedmc.proxyfeatures.features.commandrelay.CommandRelay;
import nl.hauntedmc.proxyfeatures.features.commandrelay.audit.CommandRelayAuditLogService;
@@ -142,9 +141,10 @@ private void handleIncoming(String stream, DurableDelivery
? full.substring(0, full.indexOf(' '))
: full;
- List whitelist = CastUtils.safeCastToList(
- feature.getConfigHandler().get("command_whitelist"),
- String.class
+ List whitelist = feature.getConfigHandler().getList(
+ "command_whitelist",
+ String.class,
+ List.of()
);
Set allowed = whitelist.stream()
.filter(value -> value != null && !value.isBlank())
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandrelay/internal/ProcessedCommandLedger.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandrelay/internal/ProcessedCommandLedger.java
index 330f91a7..bb12a717 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandrelay/internal/ProcessedCommandLedger.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandrelay/internal/ProcessedCommandLedger.java
@@ -1,7 +1,7 @@
package nl.hauntedmc.proxyfeatures.features.commandrelay.internal;
-import nl.hauntedmc.proxyfeatures.api.io.cache.CacheValue;
-import nl.hauntedmc.proxyfeatures.api.io.cache.FileCacheStore;
+import nl.hauntedmc.proxyfeatures.toolkit.io.cache.CacheValue;
+import nl.hauntedmc.proxyfeatures.toolkit.io.cache.FileCacheStore;
import java.util.Objects;
import java.util.Set;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandrelay/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandrelay/meta/Meta.java
deleted file mode 100644
index 1f749e34..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/commandrelay/meta/Meta.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.commandrelay.meta;
-
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-import java.util.List;
-
-public class Meta implements BaseMeta {
-
- @Override
- public String getFeatureName() {
- return "CommandRelay";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.0.0";
- }
-
- @Override
- public List getPluginDependencies() {
- return List.of(DATA_PROVIDER);
- }
-
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/connectioninfo/ConnectionInfo.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/connectioninfo/ConnectionInfo.java
index dd06c54d..1019cc34 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/connectioninfo/ConnectionInfo.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/connectioninfo/ConnectionInfo.java
@@ -1,20 +1,19 @@
package nl.hauntedmc.proxyfeatures.features.connectioninfo;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.features.connectioninfo.command.ConnectionInfoCommand;
import nl.hauntedmc.proxyfeatures.features.connectioninfo.command.PingCommand;
import nl.hauntedmc.proxyfeatures.features.connectioninfo.internal.SessionHandler;
import nl.hauntedmc.proxyfeatures.features.connectioninfo.listener.PlayerListener;
-import nl.hauntedmc.proxyfeatures.features.connectioninfo.meta.Meta;
-public class ConnectionInfo extends VelocityBaseFeature {
+public class ConnectionInfo extends VelocityBaseFeature {
private SessionHandler sessionHandler;
- public ConnectionInfo(FeatureContext context) {
+ public ConnectionInfo(FeatureContext context) {
super(context);
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/connectioninfo/command/ConnectionInfoCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/connectioninfo/command/ConnectionInfoCommand.java
index b6c8c6e0..a99b534d 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/connectioninfo/command/ConnectionInfoCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/connectioninfo/command/ConnectionInfoCommand.java
@@ -3,7 +3,7 @@
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.proxy.Player;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
import nl.hauntedmc.proxyfeatures.features.connectioninfo.ConnectionInfo;
import java.net.InetSocketAddress;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/connectioninfo/command/PingCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/connectioninfo/command/PingCommand.java
index 0541154e..14cdd413 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/connectioninfo/command/PingCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/connectioninfo/command/PingCommand.java
@@ -2,8 +2,8 @@
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
import nl.hauntedmc.proxyfeatures.features.connectioninfo.ConnectionInfo;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
import java.util.List;
import java.util.Optional;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/connectioninfo/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/connectioninfo/meta/Meta.java
deleted file mode 100644
index ad361232..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/connectioninfo/meta/Meta.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.connectioninfo.meta;
-
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-public class Meta implements BaseMeta {
-
- @Override
- public String getFeatureName() {
- return "ConnectionInfo";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.0.0";
- }
-
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/Friends.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/Friends.java
index 6aed32b3..cedcdf8e 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/Friends.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/Friends.java
@@ -1,28 +1,27 @@
package nl.hauntedmc.proxyfeatures.features.friends;
import nl.hauntedmc.dataprovider.api.orm.ORMContext;
-import nl.hauntedmc.proxyfeatures.api.friends.FriendshipApi;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.api.capability.social.FriendshipApi;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.features.friends.api.FriendshipApiImpl;
import nl.hauntedmc.proxyfeatures.features.friends.command.AsyncFriendCommand;
import nl.hauntedmc.proxyfeatures.features.friends.entity.FriendRelationEntity;
import nl.hauntedmc.proxyfeatures.features.friends.entity.FriendSettingsEntity;
import nl.hauntedmc.proxyfeatures.features.friends.entity.FriendsService;
import nl.hauntedmc.proxyfeatures.features.friends.listener.FriendActivityListener;
-import nl.hauntedmc.proxyfeatures.features.friends.meta.Meta;
import nl.hauntedmc.proxyfeatures.features.friends.support.FriendsCache;
-public class Friends extends VelocityBaseFeature {
+public class Friends extends VelocityBaseFeature {
private ORMContext orm;
private FriendsService service;
private FriendsCache cache;
private FriendshipApiImpl friendshipApi;
- public Friends(FeatureContext context) {
+ public Friends(FeatureContext context) {
super(context);
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/api/FriendshipApiImpl.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/api/FriendshipApiImpl.java
index e6c4d391..2f0fee52 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/api/FriendshipApiImpl.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/api/FriendshipApiImpl.java
@@ -1,6 +1,8 @@
package nl.hauntedmc.proxyfeatures.features.friends.api;
-import nl.hauntedmc.proxyfeatures.api.friends.FriendshipApi;
+import nl.hauntedmc.proxyfeatures.api.capability.social.FriendshipApi;
+import nl.hauntedmc.proxyfeatures.api.ApiFailureCode;
+import nl.hauntedmc.proxyfeatures.api.ApiOperationException;
import nl.hauntedmc.proxyfeatures.features.friends.Friends;
import nl.hauntedmc.proxyfeatures.features.friends.entity.FriendStatus;
import nl.hauntedmc.proxyfeatures.features.friends.entity.FriendsService;
@@ -110,7 +112,10 @@ private static CompletableFuture unavailableFuture() {
return CompletableFuture.failedFuture(unavailableFailure());
}
- private static IllegalStateException unavailableFailure() {
- return new IllegalStateException("Friends feature service is no longer available.");
+ private static ApiOperationException unavailableFailure() {
+ return new ApiOperationException(
+ ApiFailureCode.PROVIDER_RELOADED,
+ "Friends capability provider is no longer available."
+ );
}
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/command/AsyncFriendCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/command/AsyncFriendCommand.java
index 8dd56431..5a8abd75 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/command/AsyncFriendCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/command/AsyncFriendCommand.java
@@ -3,7 +3,7 @@
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.command.SimpleCommand;
import com.velocitypowered.api.proxy.Player;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
import nl.hauntedmc.proxyfeatures.features.friends.Friends;
import java.util.List;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/command/FriendCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/command/FriendCommand.java
index 7d744641..f0f74ada 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/command/FriendCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/command/FriendCommand.java
@@ -5,15 +5,14 @@
import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import net.kyori.adventure.text.Component;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
-import nl.hauntedmc.proxyfeatures.api.util.text.placeholder.MessagePlaceholders;
-import nl.hauntedmc.proxyfeatures.api.util.tools.Paginator;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.toolkit.text.placeholder.MessagePlaceholders;
+import nl.hauntedmc.proxyfeatures.toolkit.pagination.Paginator;
import nl.hauntedmc.proxyfeatures.features.friends.Friends;
import nl.hauntedmc.proxyfeatures.features.friends.entity.FriendStatus;
import nl.hauntedmc.proxyfeatures.features.friends.entity.FriendsService;
import nl.hauntedmc.proxyfeatures.features.friends.entity.PlayerRef;
-import nl.hauntedmc.proxyfeatures.features.vanish.internal.VanishAPI;
-import nl.hauntedmc.proxyfeatures.framework.service.FeatureServices;
+import nl.hauntedmc.proxyfeatures.api.capability.presence.PresenceApi;
import java.util.*;
import java.util.concurrent.CompletableFuture;
@@ -643,8 +642,8 @@ private PlayerRef resolvePlayerRef(String name) {
}
private boolean notVanished(Player pl) {
- return FeatureServices.find(feature, VanishAPI.class)
- .map(api -> !api.isVanished(pl.getUniqueId()))
+ return feature.findCapability(PresenceApi.class)
+ .map(api -> !api.isHidden(pl.getUniqueId()))
.orElse(true);
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/entity/FriendsService.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/entity/FriendsService.java
index a28f7979..a475c5b9 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/entity/FriendsService.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/entity/FriendsService.java
@@ -1,9 +1,7 @@
package nl.hauntedmc.proxyfeatures.features.friends.entity;
import jakarta.persistence.PersistenceException;
-import nl.hauntedmc.dataregistry.api.DataRegistryApi;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReference;
-import nl.hauntedmc.dataregistry.api.player.PlayerDirectory;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReferenceResolver;
import nl.hauntedmc.proxyfeatures.features.friends.Friends;
import nl.hauntedmc.proxyfeatures.features.friends.support.FriendsCache;
@@ -20,20 +18,13 @@ public class FriendsService {
private final PlayerReferenceResolver playerResolver;
public FriendsService(Friends feature, FriendsCache cache) {
- this(feature, cache, feature.getPlugin().getDataRegistry()
- .orElseThrow(() -> new IllegalStateException("DataRegistryApi is required for Friends.")));
+ this(feature, cache, feature.getPlugin().getPlayerReferenceResolver());
}
- FriendsService(Friends feature, FriendsCache cache, DataRegistryApi dataRegistry) {
+ FriendsService(Friends feature, FriendsCache cache, PlayerReferenceResolver playerResolver) {
this.feature = feature;
this.cache = cache;
- this.playerResolver = new PlayerReferenceResolver(dataRegistry);
- }
-
- FriendsService(Friends feature, FriendsCache cache, PlayerDirectory playerDirectory) {
- this.feature = feature;
- this.cache = cache;
- this.playerResolver = new PlayerReferenceResolver(playerDirectory);
+ this.playerResolver = playerResolver;
}
// -------- Player lookups (cached, lightweight) --------
@@ -560,7 +551,7 @@ private String displayName(Long playerId) {
if (playerId == null) {
return "-";
}
- return playerResolver.findActiveIdentityById(playerId)
+ return playerResolver.findIdentityById(playerId)
.map(identity -> identity.username())
.orElse("#" + playerId);
}
@@ -569,7 +560,7 @@ private FriendSnapshot snapshot(Long playerId) {
if (playerId == null) {
return new FriendSnapshot(null, null, "-");
}
- return playerResolver.findActiveIdentityById(playerId)
+ return playerResolver.findIdentityById(playerId)
.map(identity -> new FriendSnapshot(
identity.playerId(), identity.uuid().toString(), identity.username()))
.orElseGet(() -> new FriendSnapshot(playerId, null, "#" + playerId));
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/listener/FriendActivityListener.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/listener/FriendActivityListener.java
index b1bd53da..0863fb89 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/listener/FriendActivityListener.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/listener/FriendActivityListener.java
@@ -9,9 +9,8 @@
import nl.hauntedmc.proxyfeatures.features.friends.entity.FriendSnapshot;
import nl.hauntedmc.proxyfeatures.features.friends.entity.FriendsService;
import nl.hauntedmc.proxyfeatures.features.friends.entity.PlayerRef;
-import nl.hauntedmc.proxyfeatures.features.vanish.event.VanishStateChangeEvent;
-import nl.hauntedmc.proxyfeatures.features.vanish.internal.VanishAPI;
-import nl.hauntedmc.proxyfeatures.framework.service.FeatureServices;
+import nl.hauntedmc.proxyfeatures.api.capability.presence.PresenceApi;
+import nl.hauntedmc.proxyfeatures.api.capability.presence.PresenceChangedEvent;
import java.time.Duration;
import java.util.ArrayList;
@@ -114,13 +113,13 @@ public void onDisconnect(DisconnectEvent event) {
}
@Subscribe
- public void onVanishStateChange(VanishStateChangeEvent event) {
- Optional currentVanishState = vanishState(event.playerUuid());
- if (currentVanishState.isEmpty() || currentVanishState.get() != event.vanished()) {
+ public void onPresenceChanged(PresenceChangedEvent event) {
+ Optional currentVanishState = vanishState(event.playerId());
+ if (currentVanishState.isEmpty() || currentVanishState.get() != event.hidden()) {
return;
}
- Optional subjectOptional = feature.getPlugin().getProxy().getPlayer(event.playerUuid());
+ Optional subjectOptional = feature.getPlugin().getProxy().getPlayer(event.playerId());
if (subjectOptional.isEmpty()) {
return;
}
@@ -130,8 +129,8 @@ public void onVanishStateChange(VanishStateChangeEvent event) {
.map(current -> current.getServerInfo().getName())
.orElse(null);
FriendPresenceTracker.Plan plan = presenceTracker.applyVanishState(
- event.playerUuid(),
- event.vanished(),
+ event.playerId(),
+ event.hidden(),
currentServer
);
if (!announceVanishStateChanges) {
@@ -143,11 +142,11 @@ public void onVanishStateChange(VanishStateChangeEvent event) {
: event.playerName();
if (plan.type() == FriendPresenceTracker.NotificationType.VANISH_OFFLINE) {
feature.getLifecycleManager().getTaskManager().scheduleTask(() ->
- notifyFriendsOffline(event.playerUuid(), username, plan.generation()));
+ notifyFriendsOffline(event.playerId(), username, plan.generation()));
} else if (plan.type() == FriendPresenceTracker.NotificationType.VANISH_ONLINE) {
feature.getLifecycleManager().getTaskManager().scheduleTask(() ->
notifyFriendsOnline(
- event.playerUuid(),
+ event.playerId(),
username,
plan.to(),
plan.generation()
@@ -288,8 +287,8 @@ private boolean isVanished(Player player) {
private Optional vanishState(UUID playerUuid) {
try {
- return FeatureServices.find(feature, VanishAPI.class)
- .map(api -> api.isVanished(playerUuid));
+ return feature.findCapability(PresenceApi.class)
+ .map(api -> api.isHidden(playerUuid));
} catch (RuntimeException ignored) {
return Optional.empty();
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/meta/Meta.java
deleted file mode 100644
index 25eb6091..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/friends/meta/Meta.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.friends.meta;
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-import java.util.List;
-
-public class Meta implements BaseMeta {
-
- @Override
- public String getFeatureName() {
- return "Friends";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.4.0";
- }
-
- @Override
- public List getDependencies() {
- return List.of();
- }
-
- @Override
- public List getPluginDependencies() {
- return List.of(DATA_PROVIDER, DATA_REGISTRY);
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hlink/HLink.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hlink/HLink.java
index ec89140b..5475b195 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hlink/HLink.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hlink/HLink.java
@@ -1,22 +1,21 @@
package nl.hauntedmc.proxyfeatures.features.hlink;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.features.hlink.command.HLinkCommand;
import nl.hauntedmc.proxyfeatures.features.hlink.command.LinkCommand;
import nl.hauntedmc.proxyfeatures.features.hlink.command.RegisterCommand;
import nl.hauntedmc.proxyfeatures.features.hlink.internal.HLinkHandler;
import nl.hauntedmc.proxyfeatures.features.hlink.internal.hook.LuckPermsHook;
-import nl.hauntedmc.proxyfeatures.features.hlink.meta.Meta;
-public class HLink extends VelocityBaseFeature {
+public class HLink extends VelocityBaseFeature {
private HLinkHandler hlinkHandler;
private LuckPermsHook luckPermsHook;
- public HLink(FeatureContext context) {
+ public HLink(FeatureContext context) {
super(context);
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hlink/command/HLinkCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hlink/command/HLinkCommand.java
index 83a6e0fd..3ee4254a 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hlink/command/HLinkCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hlink/command/HLinkCommand.java
@@ -2,7 +2,7 @@
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
import nl.hauntedmc.proxyfeatures.features.hlink.HLink;
import nl.hauntedmc.proxyfeatures.features.hlink.internal.HLinkHandler;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hlink/command/LinkCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hlink/command/LinkCommand.java
index c4cc6b6c..38ec03ec 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hlink/command/LinkCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hlink/command/LinkCommand.java
@@ -4,7 +4,7 @@
import com.velocitypowered.api.proxy.Player;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
import nl.hauntedmc.proxyfeatures.features.hlink.HLink;
import nl.hauntedmc.proxyfeatures.features.hlink.internal.HLinkHandler;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hlink/command/RegisterCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hlink/command/RegisterCommand.java
index 158ece6b..aa93b379 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hlink/command/RegisterCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hlink/command/RegisterCommand.java
@@ -4,7 +4,7 @@
import com.velocitypowered.api.proxy.Player;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
import nl.hauntedmc.proxyfeatures.features.hlink.HLink;
import nl.hauntedmc.proxyfeatures.features.hlink.internal.HLinkHandler;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hlink/internal/HLinkHandler.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hlink/internal/HLinkHandler.java
index 6964069f..e3ffcd66 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hlink/internal/HLinkHandler.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hlink/internal/HLinkHandler.java
@@ -10,8 +10,8 @@
import net.luckperms.api.node.types.InheritanceNode;
import net.luckperms.api.track.Track;
import net.luckperms.api.track.TrackManager;
-import nl.hauntedmc.proxyfeatures.api.util.http.SimpleHttpClient;
-import nl.hauntedmc.proxyfeatures.api.util.http.SimpleHttpClient.FormParameter;
+import nl.hauntedmc.proxyfeatures.toolkit.http.HttpTransport;
+import nl.hauntedmc.proxyfeatures.toolkit.http.HttpTransport.FormParameter;
import nl.hauntedmc.proxyfeatures.features.hlink.HLink;
import nl.hauntedmc.proxyfeatures.features.hlink.internal.api.AccountRequest;
import nl.hauntedmc.proxyfeatures.features.hlink.internal.api.LinkRequest;
@@ -164,7 +164,7 @@ private boolean updatePlayerData(UUID uuid, String username) {
args.add(new FormParameter("groups", primaryGroup));
try {
- SimpleHttpClient.postHttps(apiUrl + "/updatePlayerCache", args);
+ HttpTransport.postHttps(apiUrl + "/updatePlayerCache", args);
updateCache.put(uuid, new CachedPlayerData(username, primaryGroup));
return true;
} catch (InterruptedException e) {
@@ -238,7 +238,7 @@ public String doesKeyExist(String uuid, int keyType) {
args.add(new FormParameter("uuid", uuid));
args.add(new FormParameter("key_type", String.valueOf(keyType)));
try {
- String response = SimpleHttpClient.postHttps(apiUrl + "/checkForExistingLink", args);
+ String response = HttpTransport.postHttps(apiUrl + "/checkForExistingLink", args);
LinkRequest request = gson.fromJson(response, LinkRequest.class);
if (request != null && request.getResults() != null && !request.getResults().isEmpty()) {
return request.getResults();
@@ -259,7 +259,7 @@ public boolean alreadyRegistered(String uuid) {
args.add(new FormParameter("api_key", apiKey));
args.add(new FormParameter("uuid", uuid));
try {
- String response = SimpleHttpClient.postHttps(apiUrl + "/checkUserAccountExists", args);
+ String response = HttpTransport.postHttps(apiUrl + "/checkUserAccountExists", args);
AccountRequest request = gson.fromJson(response, AccountRequest.class);
return request != null && request.getExists();
} catch (InterruptedException e) {
@@ -346,8 +346,8 @@ private LinkResult addNewKey(UUID playerId, String username, int keyType) {
argsUpdate.add(new FormParameter("groups", groups));
try {
- SimpleHttpClient.postHttps(apiUrl + "/createLinkKey", argsCreate);
- SimpleHttpClient.postHttps(apiUrl + "/updatePlayerCache", argsUpdate);
+ HttpTransport.postHttps(apiUrl + "/createLinkKey", argsCreate);
+ HttpTransport.postHttps(apiUrl + "/updatePlayerCache", argsUpdate);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
feature.getLogger().warn(Component.text("Link-key creation interrupted for " + username));
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hlink/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hlink/meta/Meta.java
deleted file mode 100644
index 4402cd5d..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hlink/meta/Meta.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.hlink.meta;
-
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-import java.util.List;
-
-public class Meta implements BaseMeta {
-
- @Override
- public String getFeatureName() {
- return "HLink";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.1.0";
- }
-
- @Override
- public List getPluginDependencies() {
- return List.of("luckperms");
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hub/Hub.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hub/Hub.java
index b39daf2b..0074ee24 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hub/Hub.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hub/Hub.java
@@ -1,15 +1,14 @@
package nl.hauntedmc.proxyfeatures.features.hub;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.features.hub.command.HubCommand;
-import nl.hauntedmc.proxyfeatures.features.hub.meta.Meta;
-public class Hub extends VelocityBaseFeature {
+public class Hub extends VelocityBaseFeature {
- public Hub(FeatureContext context) {
+ public Hub(FeatureContext context) {
super(context);
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hub/command/HubCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hub/command/HubCommand.java
index d763f37a..ca785242 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hub/command/HubCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hub/command/HubCommand.java
@@ -3,7 +3,7 @@
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.server.RegisteredServer;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
import nl.hauntedmc.proxyfeatures.features.hub.Hub;
import java.util.List;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hub/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hub/meta/Meta.java
deleted file mode 100644
index e6bbd310..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/hub/meta/Meta.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.hub.meta;
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-public class Meta implements BaseMeta {
-
- @Override
- public String getFeatureName() {
- return "Hub";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.0.0";
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/maintenance/Maintenance.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/maintenance/Maintenance.java
index 6b4e64ed..89a4d650 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/maintenance/Maintenance.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/maintenance/Maintenance.java
@@ -1,25 +1,28 @@
package nl.hauntedmc.proxyfeatures.features.maintenance;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.api.capability.operations.MaintenanceApi;
+import nl.hauntedmc.proxyfeatures.api.extension.ExtensionRegistration;
+import nl.hauntedmc.proxyfeatures.api.extension.MotdContribution;
+import nl.hauntedmc.proxyfeatures.api.extension.MotdExtensions;
import nl.hauntedmc.proxyfeatures.features.maintenance.command.MaintenanceCommand;
import nl.hauntedmc.proxyfeatures.features.maintenance.internal.MaintenanceHandler;
import nl.hauntedmc.proxyfeatures.features.maintenance.listener.MaintenanceConnectionListener;
-import nl.hauntedmc.proxyfeatures.features.maintenance.meta.Meta;
-import nl.hauntedmc.proxyfeatures.features.motd.internal.MotdLine2OverrideRegistry;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
import java.util.ArrayList;
import java.util.List;
-public class Maintenance extends VelocityBaseFeature {
+public class Maintenance extends VelocityBaseFeature {
private static final String MOTD_OVERRIDE_KEY = "maintenance";
private MaintenanceHandler handler;
+ private ExtensionRegistration motdRegistration;
- public Maintenance(FeatureContext context) {
+ public Maintenance(FeatureContext context) {
super(context);
}
@@ -112,14 +115,23 @@ public MessageMap getDefaultMessages() {
@Override
public void initialize() {
this.handler = new MaintenanceHandler(this);
- MotdLine2OverrideRegistry.register(MOTD_OVERRIDE_KEY, 100, handler::resolveMotdLine2Override);
+ getLifecycleManager().getApiManager().registerService(MaintenanceApi.class, handler);
+ motdRegistration = requireCapability(MotdExtensions.class).register(
+ MOTD_OVERRIDE_KEY,
+ 100,
+ context -> java.util.Optional.ofNullable(handler.resolveMotdLine2Override())
+ .map(MotdContribution::secondLine)
+ );
getLifecycleManager().getCommandManager().registerFeatureCommand(new MaintenanceCommand(this));
getLifecycleManager().getListenerManager().registerListener(new MaintenanceConnectionListener(this));
}
@Override
public void disable() {
- MotdLine2OverrideRegistry.unregister(MOTD_OVERRIDE_KEY);
+ if (motdRegistration != null) {
+ motdRegistration.close();
+ motdRegistration = null;
+ }
if (handler != null) {
handler.shutdown();
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/maintenance/command/MaintenanceCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/maintenance/command/MaintenanceCommand.java
index 06897472..c102e58b 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/maintenance/command/MaintenanceCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/maintenance/command/MaintenanceCommand.java
@@ -1,7 +1,7 @@
package nl.hauntedmc.proxyfeatures.features.maintenance.command;
import com.velocitypowered.api.command.CommandSource;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
import nl.hauntedmc.proxyfeatures.features.maintenance.Maintenance;
import nl.hauntedmc.proxyfeatures.features.maintenance.internal.MaintenanceHandler;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/maintenance/internal/MaintenanceHandler.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/maintenance/internal/MaintenanceHandler.java
index f9553a39..c574d48e 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/maintenance/internal/MaintenanceHandler.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/maintenance/internal/MaintenanceHandler.java
@@ -5,6 +5,10 @@
import com.velocitypowered.api.proxy.server.RegisteredServer;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.title.Title;
+import nl.hauntedmc.proxyfeatures.api.capability.operations.MaintenanceApi;
+import nl.hauntedmc.proxyfeatures.api.capability.operations.MaintenanceScope;
+import nl.hauntedmc.proxyfeatures.api.capability.operations.MaintenanceSnapshot;
+import nl.hauntedmc.proxyfeatures.api.model.ServerId;
import nl.hauntedmc.proxyfeatures.features.maintenance.Maintenance;
import nl.hauntedmc.proxyfeatures.framework.log.ConnectionLogHelper;
import nl.hauntedmc.proxyfeatures.framework.lifecycle.FeatureTaskManager;
@@ -18,11 +22,12 @@
import java.util.Locale;
import java.util.Optional;
import java.util.Set;
+import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
-public final class MaintenanceHandler {
+public final class MaintenanceHandler implements MaintenanceApi {
public enum ToggleResult {
ENABLED,
@@ -158,6 +163,32 @@ public boolean isGlobalActive() {
return globalActive.get();
}
+ @Override
+ public boolean isActive(MaintenanceScope scope) {
+ if (scope.isGlobal()) {
+ return isGlobalActive();
+ }
+ return scope.server().map(server -> isGamemodeActive(server.value())).orElse(false);
+ }
+
+ @Override
+ public boolean mayBypass(UUID playerId, MaintenanceScope scope) {
+ if (playerId == null) {
+ return false;
+ }
+ return proxy.getPlayer(playerId).map(player -> scope.server()
+ .map(server -> hasGamemodeBypass(player, server.value()))
+ .orElseGet(() -> hasGlobalBypass(player))).orElse(false);
+ }
+
+ @Override
+ public MaintenanceSnapshot snapshot() {
+ Set servers = activeGamemodes.stream()
+ .map(ServerId::of)
+ .collect(java.util.stream.Collectors.toUnmodifiableSet());
+ return new MaintenanceSnapshot(isGlobalActive(), servers, java.time.Instant.now());
+ }
+
public boolean isGlobalCountdownRunning() {
return globalCountdownRunning.get();
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/maintenance/listener/MaintenanceConnectionListener.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/maintenance/listener/MaintenanceConnectionListener.java
index 018249b6..ee466076 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/maintenance/listener/MaintenanceConnectionListener.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/maintenance/listener/MaintenanceConnectionListener.java
@@ -6,8 +6,9 @@
import com.velocitypowered.api.event.player.ServerPreConnectEvent;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.server.RegisteredServer;
+import nl.hauntedmc.proxyfeatures.api.capability.operations.TwoFactorApi;
+import nl.hauntedmc.proxyfeatures.api.model.ServerId;
import nl.hauntedmc.proxyfeatures.features.maintenance.Maintenance;
-import nl.hauntedmc.proxyfeatures.features.twofactor.TwoFactor;
import nl.hauntedmc.proxyfeatures.framework.log.ConnectionLogHelper;
public class MaintenanceConnectionListener {
@@ -105,15 +106,10 @@ private boolean isSecurityRoute(ServerPreConnectEvent event, RegisteredServer ta
return false;
}
- var loaded = feature.getPlugin().getFeatureLoadManager().getFeatureRegistry()
- .getLoadedFeature("TwoFactor");
- if (!(loaded instanceof TwoFactor twoFactor)) {
- return false;
- }
- var service = twoFactor.getService();
- return service != null
- && service.isLocked(event.getPlayer())
- && twoFactor.isLockServer(targetName);
+ return feature.findCapability(TwoFactorApi.class)
+ .map(api -> api.isLocked(event.getPlayer().getUniqueId())
+ && api.isAuthenticationServer(ServerId.of(targetName)))
+ .orElse(false);
}
@Subscribe
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/maintenance/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/maintenance/meta/Meta.java
deleted file mode 100644
index b93c11d2..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/maintenance/meta/Meta.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.maintenance.meta;
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-public class Meta implements BaseMeta {
-
- @Override
- public String getFeatureName() {
- return "Maintenance";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.0.0";
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/Messenger.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/Messenger.java
index d9e4ff62..e2697b53 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/Messenger.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/Messenger.java
@@ -1,11 +1,11 @@
package nl.hauntedmc.proxyfeatures.features.messager;
import nl.hauntedmc.dataprovider.api.orm.ORMContext;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.api.messaging.MessageMode;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
+import nl.hauntedmc.proxyfeatures.features.messager.model.MessageMode;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.features.messager.command.MessagingCommand;
import nl.hauntedmc.proxyfeatures.features.messager.command.ReplyCommand;
import nl.hauntedmc.proxyfeatures.features.messager.entity.PlayerMessageSettingsEntity;
@@ -13,17 +13,16 @@
import nl.hauntedmc.proxyfeatures.features.messager.history.PlayerMessageLogEntity;
import nl.hauntedmc.proxyfeatures.features.messager.internal.MessagingHandler;
import nl.hauntedmc.proxyfeatures.features.messager.listener.PlayerListener;
-import nl.hauntedmc.proxyfeatures.features.messager.meta.Meta;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReferenceResolver;
-public class Messenger extends VelocityBaseFeature {
+public class Messenger extends VelocityBaseFeature {
private MessagingHandler handler;
private ORMContext ormContext;
private PlayerMessageHistoryLogService messageHistoryLogService;
private MessageMode defaultMessageMode = MessageMode.FRIENDS;
- public Messenger(FeatureContext context) {
+ public Messenger(FeatureContext context) {
super(context);
}
@@ -76,10 +75,7 @@ public void initialize() {
PlayerMessageSettingsEntity.class,
PlayerMessageLogEntity.class
).orElseThrow();
- PlayerReferenceResolver playerResolver = new PlayerReferenceResolver(
- getPlugin().getDataRegistry()
- .orElseThrow(() -> new IllegalStateException("DataRegistryApi is required for Messenger."))
- );
+ PlayerReferenceResolver playerResolver = getPlugin().getPlayerReferenceResolver();
this.messageHistoryLogService = new PlayerMessageHistoryLogService(getLogger(), ormContext, playerResolver);
this.handler = new MessagingHandler(this);
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/command/MessagingCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/command/MessagingCommand.java
index 913ad914..d7fcc6a0 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/command/MessagingCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/command/MessagingCommand.java
@@ -3,11 +3,11 @@
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
-import nl.hauntedmc.proxyfeatures.api.messaging.MessageMode;
import nl.hauntedmc.proxyfeatures.features.messager.Messenger;
import nl.hauntedmc.proxyfeatures.features.messager.internal.MessagingHandler;
import nl.hauntedmc.proxyfeatures.features.messager.internal.MessengerTargetVisibility;
+import nl.hauntedmc.proxyfeatures.features.messager.model.MessageMode;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReference;
import java.util.*;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/command/ReplyCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/command/ReplyCommand.java
index 865960a9..9f340699 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/command/ReplyCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/command/ReplyCommand.java
@@ -2,7 +2,7 @@
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
import nl.hauntedmc.proxyfeatures.features.messager.Messenger;
import nl.hauntedmc.proxyfeatures.features.messager.internal.MessagingHandler;
import nl.hauntedmc.proxyfeatures.features.messager.internal.MessengerTargetVisibility;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/entity/MessageModeConverter.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/entity/MessageModeConverter.java
index 54837357..02686f3c 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/entity/MessageModeConverter.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/entity/MessageModeConverter.java
@@ -2,7 +2,7 @@
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
-import nl.hauntedmc.proxyfeatures.api.messaging.MessageMode;
+import nl.hauntedmc.proxyfeatures.features.messager.model.MessageMode;
/**
* Persists message modes by stable name.
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/entity/PlayerMessageSettingsEntity.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/entity/PlayerMessageSettingsEntity.java
index caf7e8c9..cfe58dc1 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/entity/PlayerMessageSettingsEntity.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/entity/PlayerMessageSettingsEntity.java
@@ -1,12 +1,11 @@
package nl.hauntedmc.proxyfeatures.features.messager.entity;
import jakarta.persistence.*;
-import nl.hauntedmc.proxyfeatures.api.messaging.MessageMode;
+import nl.hauntedmc.proxyfeatures.features.messager.model.MessageMode;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReference;
import java.util.HashSet;
import java.util.Objects;
-import java.util.Optional;
import java.util.Set;
@Entity
@@ -28,8 +27,8 @@ public class PlayerMessageSettingsEntity {
private boolean msgSpy = false;
@Convert(converter = MessageModeConverter.class)
- @Column(name = "message_mode", length = 32)
- private MessageMode messageMode;
+ @Column(name = "message_mode", length = 32, nullable = false)
+ private MessageMode messageMode = MessageMode.FRIENDS;
@ElementCollection(fetch = FetchType.LAZY)
@CollectionTable(
@@ -63,12 +62,8 @@ public void setMsgSpy(boolean msgSpy) {
this.msgSpy = msgSpy;
}
- public Optional getStoredMessageMode() {
- return Optional.ofNullable(messageMode);
- }
-
- public MessageMode getMessageMode(MessageMode fallback) {
- return messageMode == null ? Objects.requireNonNull(fallback, "fallback") : messageMode;
+ public MessageMode getMessageMode() {
+ return messageMode;
}
public void setMessageMode(MessageMode messageMode) {
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/internal/MessagePrivacyPolicy.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/internal/MessagePrivacyPolicy.java
index b3703e80..2a3c5ab7 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/internal/MessagePrivacyPolicy.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/internal/MessagePrivacyPolicy.java
@@ -1,6 +1,6 @@
package nl.hauntedmc.proxyfeatures.features.messager.internal;
-import nl.hauntedmc.proxyfeatures.api.messaging.MessageMode;
+import nl.hauntedmc.proxyfeatures.features.messager.model.MessageMode;
import java.util.Objects;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/internal/MessagingHandler.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/internal/MessagingHandler.java
index 57a5aea4..911883d6 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/internal/MessagingHandler.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/internal/MessagingHandler.java
@@ -1,14 +1,13 @@
package nl.hauntedmc.proxyfeatures.features.messager.internal;
import com.velocitypowered.api.proxy.Player;
-import nl.hauntedmc.proxyfeatures.api.friends.FriendshipApi;
-import nl.hauntedmc.proxyfeatures.api.messaging.MessageMode;
-import nl.hauntedmc.proxyfeatures.api.util.text.format.TextFormatter;
+import nl.hauntedmc.proxyfeatures.api.capability.social.FriendshipApi;
+import nl.hauntedmc.proxyfeatures.features.messager.model.MessageMode;
+import nl.hauntedmc.proxyfeatures.toolkit.text.format.TextFormatter;
import nl.hauntedmc.proxyfeatures.features.messager.Messenger;
import nl.hauntedmc.proxyfeatures.features.messager.entity.PlayerMessageSettingsEntity;
import nl.hauntedmc.proxyfeatures.features.messager.history.PlayerMessageHistoryLogService;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReference;
-import nl.hauntedmc.proxyfeatures.framework.service.FeatureServices;
import java.util.*;
import java.util.concurrent.CompletableFuture;
@@ -35,7 +34,7 @@ public final class MessagingHandler {
private final Map lastMessageFrom = new ConcurrentHashMap<>();
public MessagingHandler(Messenger feature) {
- this(feature, () -> FeatureServices.find(feature, FriendshipApi.class));
+ this(feature, () -> feature.findCapability(FriendshipApi.class));
}
MessagingHandler(Messenger feature, FriendshipApi friendshipApi) {
@@ -66,14 +65,14 @@ public boolean loadPlayerSettings(Player player) {
if (loadedSettings.contains(id)) return true;
try {
- PlayerMessageSettingsEntity s = settings.loadSettings(id, player.getUsername());
+ PlayerMessageSettingsEntity s = settings.loadSettings(id);
// messaging toggle
if (!s.isMsgToggle()) toggledOff.add(id);
else toggledOff.remove(id);
// privacy mode
- messageModes.put(id, s.getMessageMode(feature.getDefaultMessageMode()));
+ messageModes.put(id, s.getMessageMode());
// spy mode
if (s.isMsgSpy()) spies.add(id);
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/internal/MessagingSettingsService.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/internal/MessagingSettingsService.java
index fed8adc1..42ec5b90 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/internal/MessagingSettingsService.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/internal/MessagingSettingsService.java
@@ -1,10 +1,8 @@
package nl.hauntedmc.proxyfeatures.features.messager.internal;
-import nl.hauntedmc.dataregistry.api.DataRegistryApi;
-import nl.hauntedmc.dataregistry.api.player.PlayerDirectory;
-import nl.hauntedmc.proxyfeatures.api.messaging.MessageMode;
import nl.hauntedmc.proxyfeatures.features.messager.Messenger;
import nl.hauntedmc.proxyfeatures.features.messager.entity.PlayerMessageSettingsEntity;
+import nl.hauntedmc.proxyfeatures.features.messager.model.MessageMode;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReference;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReferenceResolver;
@@ -19,28 +17,23 @@ public class MessagingSettingsService {
private final PlayerReferenceResolver playerResolver;
public MessagingSettingsService(Messenger feature) {
- this(feature, feature.getPlugin().getDataRegistry()
- .orElseThrow(() -> new IllegalStateException("DataRegistryApi is required for Messenger.")));
- }
-
- MessagingSettingsService(Messenger feature, DataRegistryApi dataRegistry) {
this.feature = feature;
- this.playerResolver = new PlayerReferenceResolver(dataRegistry);
+ this.playerResolver = feature.getPlugin().getPlayerReferenceResolver();
}
- MessagingSettingsService(Messenger feature, PlayerDirectory playerDirectory) {
+ MessagingSettingsService(Messenger feature, PlayerReferenceResolver playerResolver) {
this.feature = feature;
- this.playerResolver = new PlayerReferenceResolver(playerDirectory);
+ this.playerResolver = playerResolver;
}
/**
* Loads MessageSettings for an existing DataRegistryApi player.
* If the player row is not available yet, returns transient configured defaults without writing PlayerReference.
*/
- public PlayerMessageSettingsEntity loadSettings(UUID uuid, String username) {
+ public PlayerMessageSettingsEntity loadSettings(UUID uuid) {
return feature.getOrmContext().runInTransaction(session -> {
MessageMode defaultMode = defaultMessageMode();
- PlayerReference playerEnt = findPlayer(session, uuid, username);
+ PlayerReference playerEnt = playerResolver.resolveReference(uuid);
if (playerEnt == null) {
PlayerMessageSettingsEntity transientSettings = new PlayerMessageSettingsEntity();
transientSettings.setMessageMode(defaultMode);
@@ -56,10 +49,6 @@ public PlayerMessageSettingsEntity loadSettings(UUID uuid, String username) {
PlayerMessageSettingsEntity settings;
if (existing.isPresent()) {
settings = existing.get();
- if (settings.getStoredMessageMode().isEmpty()) {
- settings.setMessageMode(defaultMode);
- session.merge(settings);
- }
} else {
settings = new PlayerMessageSettingsEntity(playerEnt);
settings.setMessageMode(defaultMode);
@@ -72,10 +61,6 @@ public PlayerMessageSettingsEntity loadSettings(UUID uuid, String username) {
});
}
- private PlayerReference findPlayer(org.hibernate.Session session, UUID uuid, String username) {
- return playerResolver.resolveManaged(session, uuid);
- }
-
public void setToggle(PlayerReference player, boolean enabled) {
feature.getOrmContext().runInTransaction(session -> {
PlayerMessageSettingsEntity s = session.find(PlayerMessageSettingsEntity.class, player.getId());
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/internal/MessengerTargetVisibility.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/internal/MessengerTargetVisibility.java
index a1859389..d8ea102e 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/internal/MessengerTargetVisibility.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/internal/MessengerTargetVisibility.java
@@ -1,9 +1,8 @@
package nl.hauntedmc.proxyfeatures.features.messager.internal;
import com.velocitypowered.api.proxy.Player;
+import nl.hauntedmc.proxyfeatures.api.capability.presence.PresenceApi;
import nl.hauntedmc.proxyfeatures.features.messager.Messenger;
-import nl.hauntedmc.proxyfeatures.features.vanish.internal.VanishAPI;
-import nl.hauntedmc.proxyfeatures.framework.service.FeatureServices;
import java.util.List;
import java.util.Objects;
@@ -48,9 +47,10 @@ public static List list(Messenger feature, Player viewer) {
return List.copyOf(feature.getPlugin().getProxy().getAllPlayers());
}
try {
- return FeatureServices.find(feature, VanishAPI.class)
- .map(VanishAPI::getAdjustedOnlinePlayers)
- .map(List::copyOf)
+ return feature.findCapability(PresenceApi.class)
+ .map(api -> feature.getPlugin().getProxy().getAllPlayers().stream()
+ .filter(player -> !api.isHidden(player.getUniqueId()))
+ .toList())
.orElseGet(() -> List.copyOf(feature.getPlugin().getProxy().getAllPlayers()));
} catch (RuntimeException failure) {
feature.getPlugin().getLogger()
@@ -64,8 +64,8 @@ private static boolean isVisible(Messenger feature, Player viewer, Player target
return true;
}
try {
- return FeatureServices.find(feature, VanishAPI.class)
- .map(api -> !api.isVanished(target.getUniqueId()))
+ return feature.findCapability(PresenceApi.class)
+ .map(api -> !api.isHidden(target.getUniqueId()))
.orElse(true);
} catch (RuntimeException failure) {
feature.getPlugin().getLogger()
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/meta/Meta.java
deleted file mode 100644
index 5c6dd9e0..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/meta/Meta.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.messager.meta;
-
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-import java.util.List;
-
-public class Meta implements BaseMeta {
-
- @Override
- public String getFeatureName() {
- return "Messenger";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.1.0";
- }
-
- @Override
- public List getPluginDependencies() {
- return List.of(DATA_PROVIDER, DATA_REGISTRY);
- }
-}
diff --git a/proxyfeatures-api/src/main/java/nl/hauntedmc/proxyfeatures/api/messaging/MessageMode.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/model/MessageMode.java
similarity index 94%
rename from proxyfeatures-api/src/main/java/nl/hauntedmc/proxyfeatures/api/messaging/MessageMode.java
rename to proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/model/MessageMode.java
index c807abc8..e4cce0c2 100644
--- a/proxyfeatures-api/src/main/java/nl/hauntedmc/proxyfeatures/api/messaging/MessageMode.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/messager/model/MessageMode.java
@@ -1,4 +1,4 @@
-package nl.hauntedmc.proxyfeatures.api.messaging;
+package nl.hauntedmc.proxyfeatures.features.messager.model;
import java.util.Locale;
import java.util.Optional;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/motd/Motd.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/motd/Motd.java
index 2bf4628d..ba71e954 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/motd/Motd.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/motd/Motd.java
@@ -1,12 +1,11 @@
package nl.hauntedmc.proxyfeatures.features.motd;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.features.motd.internal.MotdHandler;
import nl.hauntedmc.proxyfeatures.features.motd.listener.PingListener;
-import nl.hauntedmc.proxyfeatures.features.motd.meta.Meta;
import java.util.List;
@@ -35,11 +34,11 @@
* {@code random_words}: compose line 2 from random words plus the configured suffix.
*
*/
-public class Motd extends VelocityBaseFeature {
+public class Motd extends VelocityBaseFeature {
private MotdHandler motdHandler;
- public Motd(FeatureContext context) {
+ public Motd(FeatureContext context) {
super(context);
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/motd/internal/MotdHandler.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/motd/internal/MotdHandler.java
index 1029e35a..2d4a3532 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/motd/internal/MotdHandler.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/motd/internal/MotdHandler.java
@@ -3,13 +3,13 @@
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.velocitypowered.api.proxy.server.ServerPing;
+import nl.hauntedmc.proxyfeatures.api.extension.MotdContext;
import net.kyori.adventure.text.Component;
-import nl.hauntedmc.proxyfeatures.api.util.text.format.ComponentFormatter;
-import nl.hauntedmc.proxyfeatures.api.util.text.format.TextFormatter;
+import nl.hauntedmc.proxyfeatures.toolkit.text.format.ComponentFormatter;
+import nl.hauntedmc.proxyfeatures.toolkit.text.format.TextFormatter;
import nl.hauntedmc.proxyfeatures.features.motd.Motd;
-import nl.hauntedmc.proxyfeatures.features.vanish.internal.VanishAPI;
-import nl.hauntedmc.proxyfeatures.features.versioncheck.VersionCheck;
-import nl.hauntedmc.proxyfeatures.framework.service.FeatureServices;
+import nl.hauntedmc.proxyfeatures.api.capability.presence.PresenceApi;
+import nl.hauntedmc.proxyfeatures.api.capability.operations.VersionApi;
import java.util.ArrayList;
import java.util.Collections;
@@ -52,14 +52,14 @@ public MotdHandler(Motd feature) {
.build();
}
- public ServerPing modifyServerPing(ServerPing unmodifiedPing) {
- return createNewServerPing(unmodifiedPing);
+ public ServerPing modifyServerPing(ServerPing unmodifiedPing, MotdContext context) {
+ return createNewServerPing(unmodifiedPing, context);
}
- private ServerPing createNewServerPing(ServerPing unmodifiedPing) {
+ private ServerPing createNewServerPing(ServerPing unmodifiedPing, MotdContext context) {
PlayerCountSnapshot playerCounts = getPlayerCountSnapshot(unmodifiedPing);
ServerPing.Version version = getDisplayedVersion(unmodifiedPing);
- Component motd = getMotd(playerCounts, version);
+ Component motd = getMotd(playerCounts, version, context);
return new ServerPing(version,
playerCounts.adjustedPlayers(),
@@ -70,14 +70,12 @@ private ServerPing createNewServerPing(ServerPing unmodifiedPing) {
private ServerPing.Version getDisplayedVersion(ServerPing unmodifiedPing) {
ServerPing.Version version = unmodifiedPing.getVersion();
- if (feature.getPlugin().getFeatureLoadManager().getFeatureRegistry().isFeatureLoaded("VersionCheck")) {
- VersionCheck versionCheck = (VersionCheck) feature.getPlugin()
- .getFeatureLoadManager()
- .getFeatureRegistry()
- .getLoadedFeature("VersionCheck");
- if (versionCheck.getVersionHandler().isUnsupportedVersion(unmodifiedPing.getVersion().getProtocol())) {
- int minProtocol = versionCheck.getVersionHandler().getMinimumProtcolVersion();
- String friendlyName = versionCheck.getVersionHandler().getFriendlyProtocolName() + "+";
+ Optional versionApi = feature.findCapability(VersionApi.class);
+ if (versionApi.isPresent()) {
+ VersionApi supported = versionApi.get();
+ if (!supported.isSupported(unmodifiedPing.getVersion().getProtocol())) {
+ int minProtocol = supported.minimumProtocolVersion();
+ String friendlyName = supported.minimumVersionName() + "+";
version = new ServerPing.Version(minProtocol, friendlyName);
}
}
@@ -95,8 +93,9 @@ private PlayerCountSnapshot getPlayerCountSnapshot(ServerPing unmodifiedPing) {
int onlinePlayers = players.map(ServerPing.Players::getOnline).orElse(0);
int maxPlayers = players.map(ServerPing.Players::getMax).orElse(onlinePlayers);
List sample = players.map(ServerPing.Players::getSample).orElse(List.of());
- int vanishedPlayers = FeatureServices.find(feature, VanishAPI.class)
- .map(VanishAPI::getVanishedCount)
+ int vanishedPlayers = feature.findCapability(PresenceApi.class)
+ .map(PresenceApi::snapshot)
+ .map(snapshot -> snapshot.hiddenCount())
.orElse(0);
int visiblePlayers = Math.max(0, onlinePlayers - vanishedPlayers);
@@ -115,9 +114,9 @@ private double readPlayerCountMultiplier() {
return feature.getConfigHandler().get("player_count_multiplier", Double.class, 1.0D);
}
- private Component getMotd(PlayerCountSnapshot playerCounts, ServerPing.Version version) {
+ private Component getMotd(PlayerCountSnapshot playerCounts, ServerPing.Version version, MotdContext context) {
MotdConfig config = configCache.get(TEMPLATE_CACHE_KEY, ignored -> MotdConfig.load(feature));
- String renderedMotd = renderMotd(config, playerCounts, version);
+ String renderedMotd = renderMotd(config, playerCounts, version, context);
return motdCache.get(renderedMotd, this::buildComponent);
}
@@ -127,9 +126,16 @@ public void invalidateCache() {
rotatingMessageCursor.set(0);
}
- private String renderMotd(MotdConfig config, PlayerCountSnapshot playerCounts, ServerPing.Version version) {
+ private String renderMotd(
+ MotdConfig config,
+ PlayerCountSnapshot playerCounts,
+ ServerPing.Version version,
+ MotdContext context
+ ) {
String line1 = applyPlaceholders(config.line1(), playerCounts, version);
- String line2Template = MotdLine2OverrideRegistry.resolve().orElseGet(() -> resolveLine2(config));
+ String line2Template = feature.getPlugin().getMotdExtensions().resolve(context)
+ .flatMap(contribution -> contribution.secondLine())
+ .orElseGet(() -> resolveLine2(config));
String line2 = applyPlaceholders(line2Template, playerCounts, version);
return line1 + "\n" + line2;
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/motd/internal/MotdLine2OverrideRegistry.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/motd/internal/MotdLine2OverrideRegistry.java
deleted file mode 100644
index 91065527..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/motd/internal/MotdLine2OverrideRegistry.java
+++ /dev/null
@@ -1,75 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.motd.internal;
-
-import java.util.Comparator;
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Supplier;
-
-/**
- * Simple global registry for runtime line-2 MOTD overrides.
- *
- * This keeps features such as Maintenance decoupled from Motd load order.
- * Providers return a raw line-2 template, or {@code null} when they do not want
- * to override the MOTD for the current moment.
- *
- *
If multiple features register an override at the same time, the entry with the
- * highest priority wins. Ties are resolved deterministically by registry key.
- */
-public final class MotdLine2OverrideRegistry {
-
- private static final Map ENTRIES = new ConcurrentHashMap<>();
-
- private MotdLine2OverrideRegistry() {
- }
-
- public static void register(String key, int priority, Supplier supplier) {
- if (key == null || key.isBlank() || supplier == null) {
- return;
- }
- ENTRIES.put(key, new Entry(priority, supplier));
- }
-
- public static void unregister(String key) {
- if (key == null || key.isBlank()) {
- return;
- }
- ENTRIES.remove(key);
- }
-
- public static Optional resolve() {
- return resolveRegistration().map(ResolvedOverride::value);
- }
-
- public static Optional resolveRegistration() {
- return ENTRIES.entrySet().stream()
- .sorted(Comparator.>comparingInt(entry -> entry.getValue().priority()).reversed()
- .thenComparing(Map.Entry::getKey))
- .map(entry -> {
- String value = entry.getValue().supply();
- if (value == null || value.isBlank()) {
- return null;
- }
- return new ResolvedOverride(entry.getKey(), entry.getValue().priority(), value);
- })
- .filter(entry -> entry != null)
- .findFirst();
- }
-
- static void clear() {
- ENTRIES.clear();
- }
-
- public record ResolvedOverride(String key, int priority, String value) {
- }
-
- private record Entry(int priority, Supplier supplier) {
- private String supply() {
- try {
- return supplier.get();
- } catch (RuntimeException ignored) {
- return null;
- }
- }
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/motd/listener/PingListener.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/motd/listener/PingListener.java
index 25231d9f..7ffa2839 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/motd/listener/PingListener.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/motd/listener/PingListener.java
@@ -3,6 +3,7 @@
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.proxy.ProxyPingEvent;
import com.velocitypowered.api.proxy.server.ServerPing;
+import nl.hauntedmc.proxyfeatures.api.extension.MotdContext;
import nl.hauntedmc.proxyfeatures.features.motd.Motd;
public class PingListener {
@@ -15,7 +16,11 @@ public PingListener(Motd feature) {
@Subscribe(priority = 10)
public void onProxyPing(ProxyPingEvent event) {
- ServerPing serverPing = feature.getMotdHandler().modifyServerPing(event.getPing());
+ MotdContext context = new MotdContext(
+ event.getConnection().getRemoteAddress().getAddress(),
+ event.getConnection().getProtocolVersion().getProtocol()
+ );
+ ServerPing serverPing = feature.getMotdHandler().modifyServerPing(event.getPing(), context);
event.setPing(serverPing);
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/motd/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/motd/meta/Meta.java
deleted file mode 100644
index 44381627..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/motd/meta/Meta.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.motd.meta;
-
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-public class Meta implements BaseMeta {
-
- @Override
- public String getFeatureName() {
- return "Motd";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.2.0";
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playercount/PlayerCount.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playercount/PlayerCount.java
index 5473dae5..d34be7cc 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playercount/PlayerCount.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playercount/PlayerCount.java
@@ -1,15 +1,14 @@
package nl.hauntedmc.proxyfeatures.features.playercount;
import nl.hauntedmc.dataprovider.database.messaging.MessagingDataAccess;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
-import nl.hauntedmc.proxyfeatures.features.playercount.internal.PlayerCountAPI;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.features.playercount.internal.PlayerCountService;
import nl.hauntedmc.proxyfeatures.features.playercount.internal.PlayerCountPublisher;
-import nl.hauntedmc.proxyfeatures.features.playercount.meta.Meta;
-import nl.hauntedmc.proxyfeatures.features.vanish.internal.VanishAPI;
-import nl.hauntedmc.proxyfeatures.framework.service.FeatureServices;
+import nl.hauntedmc.proxyfeatures.api.capability.presence.PresenceApi;
+import nl.hauntedmc.proxyfeatures.api.capability.network.PlayerCountApi;
import java.time.Duration;
import java.util.Optional;
@@ -17,16 +16,16 @@
/**
* Publishes vanish-aware player counts for the complete network and every backend.
*/
-public final class PlayerCount extends VelocityBaseFeature {
+public final class PlayerCount extends VelocityBaseFeature {
static final String DEFAULT_CHANNEL = "proxy.playercount.snapshot";
static final int DEFAULT_PUBLISH_INTERVAL_SECONDS = 2;
static final String DEFAULT_PUBLISHER_ID = "proxy";
- private PlayerCountAPI api;
+ private PlayerCountService api;
private PlayerCountPublisher publisher;
- public PlayerCount(FeatureContext context) {
+ public PlayerCount(FeatureContext context) {
super(context);
}
@@ -47,11 +46,11 @@ public MessageMap getDefaultMessages() {
@Override
public void initialize() {
- api = new PlayerCountAPI(
+ api = new PlayerCountService(
getPlugin().getProxy(),
- () -> FeatureServices.find(this, VanishAPI.class)
+ () -> findCapability(PresenceApi.class)
);
- getLifecycleManager().getApiManager().registerService(PlayerCountAPI.class, api);
+ getLifecycleManager().getApiManager().registerService(PlayerCountApi.class, api);
Optional redisBus = registerRedisMessagingDataAccess(
"redis",
@@ -90,7 +89,7 @@ public void disable() {
}
}
- public PlayerCountAPI getApi() {
+ public PlayerCountService getApi() {
return api;
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playercount/internal/PlayerCountPublisher.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playercount/internal/PlayerCountPublisher.java
index 09f2ef21..d8bc8f11 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playercount/internal/PlayerCountPublisher.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playercount/internal/PlayerCountPublisher.java
@@ -2,6 +2,7 @@
import nl.hauntedmc.dataprovider.database.messaging.MessagingDataAccess;
import nl.hauntedmc.proxyfeatures.contracts.messaging.PlayerCountSnapshotMessage;
+import nl.hauntedmc.proxyfeatures.api.capability.network.PlayerCountSnapshot;
import nl.hauntedmc.proxyfeatures.features.playercount.PlayerCount;
import java.util.LinkedHashMap;
@@ -22,7 +23,7 @@ public final class PlayerCountPublisher {
private final PlayerCount feature;
private final MessagingDataAccess redisBus;
- private final PlayerCountAPI api;
+ private final PlayerCountService api;
private final String channel;
private final String publisherId;
private final String publisherEpoch = UUID.randomUUID().toString();
@@ -34,7 +35,7 @@ public final class PlayerCountPublisher {
public PlayerCountPublisher(
PlayerCount feature,
MessagingDataAccess redisBus,
- PlayerCountAPI api,
+ PlayerCountService api,
String channel,
String publisherId
) {
@@ -52,7 +53,7 @@ public void publishNow() {
PlayerCountSnapshotMessage message;
try {
- PlayerCountSnapshot snapshot = api.capture();
+ PlayerCountSnapshot snapshot = api.snapshot();
if (closed) {
publishInFlight.set(false);
return;
@@ -104,8 +105,8 @@ public void close() {
private PlayerCountSnapshotMessage toMessage(PlayerCountSnapshot snapshot, long nextSequence) {
Map servers = new LinkedHashMap<>();
snapshot.servers().forEach((serverName, counts) -> servers.put(
- serverName,
- new PlayerCountSnapshotMessage.ServerCounts(counts.online(), counts.vanished())
+ serverName.value(),
+ new PlayerCountSnapshotMessage.ServerCounts(counts.online(), counts.hidden())
));
return new PlayerCountSnapshotMessage(
publisherId,
@@ -113,7 +114,7 @@ private PlayerCountSnapshotMessage toMessage(PlayerCountSnapshot snapshot, long
nextSequence,
System.currentTimeMillis(),
snapshot.network().online(),
- snapshot.network().vanished(),
+ snapshot.network().hidden(),
servers
);
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playercount/internal/PlayerCountAPI.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playercount/internal/PlayerCountService.java
similarity index 65%
rename from proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playercount/internal/PlayerCountAPI.java
rename to proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playercount/internal/PlayerCountService.java
index ec0d24fd..21b4d5a8 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playercount/internal/PlayerCountAPI.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playercount/internal/PlayerCountService.java
@@ -2,33 +2,37 @@
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer;
+import nl.hauntedmc.proxyfeatures.api.capability.presence.PresenceApi;
+import nl.hauntedmc.proxyfeatures.api.capability.network.PlayerCountApi;
+import nl.hauntedmc.proxyfeatures.api.capability.network.PlayerCountSnapshot;
+import nl.hauntedmc.proxyfeatures.api.capability.network.PlayerCounts;
+import nl.hauntedmc.proxyfeatures.api.model.ServerId;
import nl.hauntedmc.proxyfeatures.contracts.messaging.PlayerCountSnapshotMessage;
-import nl.hauntedmc.proxyfeatures.features.vanish.internal.VanishAPI;
-import java.util.Collection;
-import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
+import java.time.Instant;
import java.util.function.Supplier;
/**
* Reusable source of vanish-aware network and backend player counts.
*/
-public final class PlayerCountAPI {
+public final class PlayerCountService implements PlayerCountApi {
private final ProxyServer proxy;
- private final Supplier> vanishApi;
+ private final Supplier> presenceApi;
- public PlayerCountAPI(ProxyServer proxy, Supplier> vanishApi) {
+ public PlayerCountService(ProxyServer proxy, Supplier> presenceApi) {
this.proxy = java.util.Objects.requireNonNull(proxy, "proxy");
- this.vanishApi = java.util.Objects.requireNonNull(vanishApi, "vanishApi");
+ this.presenceApi = java.util.Objects.requireNonNull(presenceApi, "presenceApi");
}
- public PlayerCountSnapshot capture() {
+ @Override
+ public PlayerCountSnapshot snapshot() {
List players = List.copyOf(proxy.getAllPlayers());
Set vanishedPlayers = currentVanishedPlayers();
Map perServer = new LinkedHashMap<>();
@@ -64,34 +68,21 @@ public PlayerCountSnapshot capture() {
});
}
- Map immutableServers = new LinkedHashMap<>();
- perServer.forEach((serverName, counts) -> immutableServers.put(serverName, counts.snapshot()));
+ Map immutableServers = new LinkedHashMap<>();
+ perServer.forEach((serverName, counts) -> immutableServers.put(ServerId.of(serverName), counts.snapshot()));
return new PlayerCountSnapshot(
- new PlayerCountSnapshot.Counts(players.size(), networkVanished),
- immutableServers
+ new PlayerCounts(players.size(), networkVanished),
+ immutableServers,
+ Instant.now()
);
}
- public PlayerCountSnapshot.Counts getNetworkCounts() {
- return capture().network();
- }
-
- public PlayerCountSnapshot.Counts getServerCounts(String serverName) {
- return capture().server(serverName);
- }
-
private Set currentVanishedPlayers() {
- Optional current = vanishApi.get();
+ Optional current = presenceApi.get();
if (current == null || current.isEmpty()) {
return Set.of();
}
- Collection vanished = current.get().getVanishedPlayers();
- if (vanished == null || vanished.isEmpty()) {
- return Set.of();
- }
- Set playerIds = new HashSet<>();
- vanished.forEach(player -> playerIds.add(player.getUniqueId()));
- return Set.copyOf(playerIds);
+ return current.get().snapshot().hiddenPlayers();
}
private static final class MutableCounts {
@@ -105,8 +96,8 @@ private void increment(boolean isVanished) {
}
}
- private PlayerCountSnapshot.Counts snapshot() {
- return new PlayerCountSnapshot.Counts(online, vanished);
+ private PlayerCounts snapshot() {
+ return new PlayerCounts(online, vanished);
}
}
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playercount/internal/PlayerCountSnapshot.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playercount/internal/PlayerCountSnapshot.java
deleted file mode 100644
index 6239d31c..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playercount/internal/PlayerCountSnapshot.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.playercount.internal;
-
-import nl.hauntedmc.proxyfeatures.contracts.messaging.PlayerCountSnapshotMessage;
-
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * Immutable in-process view of current network and backend player counts.
- */
-public record PlayerCountSnapshot(
- Counts network,
- Map servers
-) {
-
- public PlayerCountSnapshot {
- network = Objects.requireNonNull(network, "network");
- if (servers == null || servers.isEmpty()) {
- servers = Map.of();
- } else {
- Map normalized = new LinkedHashMap<>();
- servers.forEach((name, counts) -> {
- String normalizedName = PlayerCountSnapshotMessage.normalizeServerName(name);
- if (normalizedName.isEmpty()) {
- throw new IllegalArgumentException("server name must not be blank");
- }
- Counts normalizedCounts = Objects.requireNonNull(counts, "server counts");
- if (normalized.putIfAbsent(normalizedName, normalizedCounts) != null) {
- throw new IllegalArgumentException(
- "duplicate normalized server name: " + normalizedName
- );
- }
- });
- servers = Map.copyOf(normalized);
- }
- }
-
- public Counts server(String serverName) {
- return servers.getOrDefault(
- PlayerCountSnapshotMessage.normalizeServerName(serverName),
- Counts.empty()
- );
- }
-
- public record Counts(int online, int vanished) {
- public Counts {
- if (online < 0 || vanished < 0 || vanished > online) {
- throw new IllegalArgumentException("invalid player counts");
- }
- }
-
- public static Counts empty() {
- return new Counts(0, 0);
- }
-
- public int visible() {
- return Math.max(0, online - vanished);
- }
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playercount/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playercount/meta/Meta.java
deleted file mode 100644
index 8f957f8b..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playercount/meta/Meta.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.playercount.meta;
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-import java.util.List;
-
-public final class Meta implements BaseMeta {
-
- @Override
- public String getFeatureName() {
- return "PlayerCount";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.0.0";
- }
-
- @Override
- public List getPluginDependencies() {
- return List.of(DATA_PROVIDER);
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerinfo/PlayerInfo.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerinfo/PlayerInfo.java
index bf6f7fb1..4fad384d 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerinfo/PlayerInfo.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerinfo/PlayerInfo.java
@@ -1,21 +1,17 @@
package nl.hauntedmc.proxyfeatures.features.playerinfo;
-import nl.hauntedmc.dataprovider.api.orm.ORMContext;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.features.playerinfo.command.PlayerInfoCommand;
-import nl.hauntedmc.proxyfeatures.features.playerinfo.meta.Meta;
import nl.hauntedmc.proxyfeatures.features.playerinfo.service.PlayerInfoService;
-import nl.hauntedmc.proxyfeatures.features.sanctions.entity.SanctionEntity;
-public class PlayerInfo extends VelocityBaseFeature {
+public class PlayerInfo extends VelocityBaseFeature {
- private ORMContext ormContext;
private PlayerInfoService service;
- public PlayerInfo(FeatureContext context) {
+ public PlayerInfo(FeatureContext context) {
super(context);
}
@@ -70,10 +66,6 @@ public MessageMap getDefaultMessages() {
@Override
public void initialize() {
- ormContext = createPlayerOrmContext(
- SanctionEntity.class
- ).orElseThrow();
-
service = new PlayerInfoService(this);
// Command
@@ -84,10 +76,6 @@ public void initialize() {
public void disable() {
}
- public ORMContext getOrmContext() {
- return ormContext;
- }
-
public PlayerInfoService getService() {
return service;
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerinfo/command/PlayerInfoCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerinfo/command/PlayerInfoCommand.java
index 33e3a299..9e950b71 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerinfo/command/PlayerInfoCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerinfo/command/PlayerInfoCommand.java
@@ -6,11 +6,11 @@
import nl.hauntedmc.dataregistry.api.player.PlayerIdentity;
import nl.hauntedmc.dataregistry.api.player.PlayerNameHistoryEntry;
import nl.hauntedmc.dataregistry.api.player.PlayerProfile;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
-import nl.hauntedmc.proxyfeatures.api.util.text.format.ComponentFormatter;
+import nl.hauntedmc.proxyfeatures.api.capability.moderation.SanctionSnapshot;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.toolkit.text.format.ComponentFormatter;
import nl.hauntedmc.proxyfeatures.features.playerinfo.PlayerInfo;
import nl.hauntedmc.proxyfeatures.features.playerinfo.service.PlayerInfoService;
-import nl.hauntedmc.proxyfeatures.features.sanctions.entity.SanctionEntity;
import java.util.List;
import java.util.Locale;
@@ -93,7 +93,7 @@ private void renderProfile(
Optional languageOpt = svc.getLanguage(playerIdentity);
// Active sanctions
- List activeSanctions = svc.getActiveSanctions(playerIdentity);
+ var activeSanctionsStage = svc.getActiveSanctions(playerIdentity);
// Header
source.sendMessage(feature.getLocalizationHandler()
@@ -158,9 +158,14 @@ private void renderProfile(
}
// Possible alts by last known IP (exclude self, A-Z)
- svc.findUsernamesSharingLastIp(profile).whenComplete((altNames, altThrowable) ->
+ svc.findUsernamesSharingLastIp(profile)
+ .thenCombine(activeSanctionsStage, ProfileExtras::new)
+ .whenComplete((extras, extrasThrowable) ->
feature.getLifecycleManager().getTaskManager().scheduleTask(() -> {
- List safeAltNames = altThrowable == null && altNames != null ? altNames : List.of();
+ List safeAltNames = extrasThrowable == null && extras != null
+ ? extras.altNames() : List.of();
+ List activeSanctions = extrasThrowable == null && extras != null
+ ? extras.sanctions() : List.of();
if (safeAltNames.isEmpty()) {
sendEntry(source, lblAlts, raw(source, "playerinfo.alts_none"));
} else {
@@ -172,14 +177,14 @@ private void renderProfile(
} else {
sendListHeader(source, lblPunishments);
String permanentLabel = raw(source, "playerinfo.permanent");
- for (SanctionEntity s : activeSanctions) {
- String expires = (s.getExpiresAt() == null) ? permanentLabel : svc.fmt(s.getExpiresAt());
- String created = svc.fmt(s.getCreatedAt());
+ for (SanctionSnapshot sanction : activeSanctions) {
+ String expires = sanction.expiresAt().map(svc::fmt).orElse(permanentLabel);
+ String created = svc.fmt(sanction.createdAt());
source.sendMessage(feature.getLocalizationHandler()
.getMessage("playerinfo.punishment_item")
- .with("type", s.getType().name())
- .with("reason", s.getReason())
+ .with("type", sanction.type().name())
+ .with("reason", sanction.reason())
.with("expires", expires)
.with("created", created)
.forAudience(source)
@@ -190,6 +195,9 @@ private void renderProfile(
);
}
+ private record ProfileExtras(List altNames, List sanctions) {
+ }
+
private void sendList(CommandSource audience, String field, List values) {
sendListHeader(audience, field);
for (String value : values) {
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerinfo/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerinfo/meta/Meta.java
deleted file mode 100644
index 09bad1f3..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerinfo/meta/Meta.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.playerinfo.meta;
-
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-import java.util.List;
-
-public class Meta implements BaseMeta {
-
- @Override
- public String getFeatureName() {
- return "PlayerInfo";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.0.0";
- }
-
- @Override
- public List getPluginDependencies() {
- return List.of(DATA_PROVIDER, DATA_REGISTRY);
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerinfo/service/PlayerInfoService.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerinfo/service/PlayerInfoService.java
index 571b1bae..24776f26 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerinfo/service/PlayerInfoService.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerinfo/service/PlayerInfoService.java
@@ -7,10 +7,11 @@
import nl.hauntedmc.dataregistry.api.player.PlayerData;
import nl.hauntedmc.dataregistry.api.player.PlayerIdentity;
import nl.hauntedmc.dataregistry.api.player.PlayerProfile;
-import nl.hauntedmc.proxyfeatures.features.playerlanguage.api.LanguageAPI;
+import nl.hauntedmc.proxyfeatures.api.capability.player.PlayerLanguageApi;
+import nl.hauntedmc.proxyfeatures.api.capability.moderation.SanctionFilter;
+import nl.hauntedmc.proxyfeatures.api.capability.moderation.SanctionSnapshot;
+import nl.hauntedmc.proxyfeatures.api.capability.moderation.SanctionsApi;
import nl.hauntedmc.proxyfeatures.features.playerinfo.PlayerInfo;
-import nl.hauntedmc.proxyfeatures.features.sanctions.entity.SanctionEntity;
-import nl.hauntedmc.proxyfeatures.framework.service.FeatureServices;
import java.time.Instant;
import java.time.ZoneId;
@@ -92,32 +93,26 @@ public Optional getLanguage(PlayerIdentity player) {
return Optional.empty();
}
UUID uuid = player.uuid();
- return FeatureServices.find(feature, LanguageAPI.class)
+ return feature.findCapability(PlayerLanguageApi.class)
.map(api -> {
- var preference = api.getPreference(uuid);
- if (preference == nl.hauntedmc.proxyfeatures.api.io.localization.Language.AUTO) {
- return "AUTO (" + api.get(uuid).name() + ")";
+ Optional preference = api.preference(uuid);
+ if (preference.isEmpty()) {
+ String effective = api.resolvedLanguage(uuid)
+ .map(java.util.Locale::toLanguageTag)
+ .orElse("unknown");
+ return "AUTO (" + effective.toUpperCase(java.util.Locale.ROOT) + ")";
}
- return preference.name();
+ return preference.get().toLanguageTag().toUpperCase(java.util.Locale.ROOT);
});
}
- public List getActiveSanctions(PlayerIdentity player) {
+ public CompletionStage> getActiveSanctions(PlayerIdentity player) {
if (player == null) {
- return List.of();
+ return CompletableFuture.completedFuture(List.of());
}
- Instant now = Instant.now();
- return feature.getOrmContext().runInTransaction(session ->
- session.createQuery(
- "SELECT s FROM SanctionEntity s " +
- "WHERE s.targetPlayerId = :playerId AND s.active = true " +
- "AND (s.expiresAt IS NULL OR s.expiresAt > :now) " +
- "ORDER BY s.createdAt DESC",
- SanctionEntity.class)
- .setParameter("playerId", player.playerId())
- .setParameter("now", now)
- .getResultList()
- );
+ return feature.findCapability(SanctionsApi.class)
+ .map(api -> api.find(player.uuid(), SanctionFilter.ACTIVE))
+ .orElseGet(() -> CompletableFuture.completedFuture(List.of()));
}
public OnlineStatus getOnlineStatus(String nameOrUuid) {
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlanguage/PlayerLanguage.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlanguage/PlayerLanguage.java
index 51300d77..2db862b9 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlanguage/PlayerLanguage.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlanguage/PlayerLanguage.java
@@ -2,19 +2,18 @@
import nl.hauntedmc.dataregistry.api.DataRegistryApi;
import nl.hauntedmc.dataregistry.api.DataRegistryFeature;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
-import nl.hauntedmc.proxyfeatures.features.playerlanguage.api.LanguageAPI;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.api.capability.player.PlayerLanguageApi;
import nl.hauntedmc.proxyfeatures.features.playerlanguage.command.LanguageCommand;
import nl.hauntedmc.proxyfeatures.features.playerlanguage.listener.LanguageListener;
-import nl.hauntedmc.proxyfeatures.features.playerlanguage.meta.Meta;
import nl.hauntedmc.proxyfeatures.features.playerlanguage.service.LanguageService;
import java.util.List;
-public class PlayerLanguage extends VelocityBaseFeature {
+public class PlayerLanguage extends VelocityBaseFeature {
public static final String DEFAULT_LANGUAGE = "AUTO";
public static final String DEFAULT_AUTO_FALLBACK_LANGUAGE = "EN";
@@ -22,7 +21,7 @@ public class PlayerLanguage extends VelocityBaseFeature {
private LanguageService service;
- public PlayerLanguage(FeatureContext context) {
+ public PlayerLanguage(FeatureContext context) {
super(context);
}
@@ -69,7 +68,7 @@ public void initialize() {
service = new LanguageService(this, dataRegistry);
getLifecycleManager().getCommandManager().registerFeatureCommand(new LanguageCommand(this));
getLifecycleManager().getListenerManager().registerListener(new LanguageListener(this));
- getLifecycleManager().getApiManager().registerService(LanguageAPI.class, service);
+ getLifecycleManager().getApiManager().registerService(PlayerLanguageApi.class, service);
}
@Override
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlanguage/api/LanguageAPI.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlanguage/api/LanguageAPI.java
deleted file mode 100644
index e56967b1..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlanguage/api/LanguageAPI.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.playerlanguage.api;
-
-import nl.hauntedmc.proxyfeatures.api.io.localization.Language;
-
-import java.util.UUID;
-
-public interface LanguageAPI {
- Language get(UUID playerUuid);
-
- Language getPreference(UUID playerUuid);
-
- void set(UUID playerUuid, Language language);
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlanguage/command/LanguageCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlanguage/command/LanguageCommand.java
index 631222e9..0eec3ecf 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlanguage/command/LanguageCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlanguage/command/LanguageCommand.java
@@ -2,9 +2,9 @@
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
-import nl.hauntedmc.proxyfeatures.api.io.localization.Language;
import nl.hauntedmc.proxyfeatures.features.playerlanguage.PlayerLanguage;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.Language;
import java.util.Arrays;
import java.util.List;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlanguage/command/LanguageCommandPolicy.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlanguage/command/LanguageCommandPolicy.java
index 92dad1a5..a010ced0 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlanguage/command/LanguageCommandPolicy.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlanguage/command/LanguageCommandPolicy.java
@@ -1,6 +1,6 @@
package nl.hauntedmc.proxyfeatures.features.playerlanguage.command;
-import nl.hauntedmc.proxyfeatures.api.io.localization.Language;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.Language;
import java.util.ArrayList;
import java.util.List;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlanguage/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlanguage/meta/Meta.java
deleted file mode 100644
index ebb71f4e..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlanguage/meta/Meta.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.playerlanguage.meta;
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-import java.util.List;
-
-public class Meta implements BaseMeta {
-
- @Override
- public String getFeatureName() {
- return "PlayerLanguage";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.0.0";
- }
-
- @Override
- public List getDependencies() {
- return List.of("AntiVPN");
- }
-
- @Override
- public List getPluginDependencies() {
- return List.of(DATA_PROVIDER, DATA_REGISTRY);
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlanguage/service/LanguageService.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlanguage/service/LanguageService.java
index 0a919816..1dd962c4 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlanguage/service/LanguageService.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlanguage/service/LanguageService.java
@@ -4,11 +4,10 @@
import nl.hauntedmc.dataregistry.api.DataRegistryApi;
import nl.hauntedmc.dataregistry.api.player.PlayerData;
import nl.hauntedmc.dataregistry.api.player.PlayerLanguageSettings;
-import nl.hauntedmc.proxyfeatures.api.io.localization.Language;
-import nl.hauntedmc.proxyfeatures.features.antivpn.api.CountryAPI;
+import nl.hauntedmc.proxyfeatures.api.capability.player.NetworkLocationApi;
+import nl.hauntedmc.proxyfeatures.api.capability.player.PlayerLanguageApi;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.Language;
import nl.hauntedmc.proxyfeatures.features.playerlanguage.PlayerLanguage;
-import nl.hauntedmc.proxyfeatures.features.playerlanguage.api.LanguageAPI;
-import nl.hauntedmc.proxyfeatures.framework.service.FeatureServices;
import java.time.Duration;
import java.util.Locale;
@@ -22,7 +21,7 @@
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;
-public final class LanguageService implements LanguageAPI {
+public final class LanguageService implements PlayerLanguageApi {
public record LanguageState(Language preference, Language effective) {
}
@@ -61,12 +60,10 @@ public void forget(UUID playerUuid) {
stateCache.remove(playerUuid);
}
- @Override
public Language get(UUID playerUuid) {
return getState(playerUuid).effective();
}
- @Override
public Language getPreference(UUID playerUuid) {
return getState(playerUuid).preference();
}
@@ -75,7 +72,6 @@ public Language getEffectiveLanguage(UUID playerUuid) {
return getState(playerUuid).effective();
}
- @Override
public void set(UUID playerUuid, Language language) {
setAsync(playerUuid, language);
}
@@ -110,6 +106,38 @@ public CompletionStage setAsync(UUID playerUuid, Language language) {
});
}
+ @Override
+ public Optional resolvedLanguage(UUID playerId) {
+ return Optional.of(toLocale(get(playerId)));
+ }
+
+ @Override
+ public Optional preference(UUID playerId) {
+ Language preference = getPreference(playerId);
+ return preference == Language.AUTO ? Optional.empty() : Optional.of(toLocale(preference));
+ }
+
+ @Override
+ public CompletionStage setPreference(UUID playerId, Locale language) {
+ Objects.requireNonNull(language, "language");
+ Language requested = fromLocale(language);
+ return savePreference(playerId, requested);
+ }
+
+ @Override
+ public CompletionStage clearPreference(UUID playerId) {
+ return savePreference(playerId, Language.AUTO);
+ }
+
+ private CompletionStage savePreference(UUID playerId, Language requested) {
+ Objects.requireNonNull(playerId, "playerId");
+ return setAsync(playerId, requested).thenCompose(saved -> saved
+ ? CompletableFuture.completedFuture(null)
+ : CompletableFuture.failedFuture(new nl.hauntedmc.proxyfeatures.api.ApiOperationException(
+ nl.hauntedmc.proxyfeatures.api.ApiFailureCode.PERSISTENCE_UNAVAILABLE,
+ "Language preference was not saved")));
+ }
+
public Optional resolveUuidByName(String username) {
if (username == null || username.isBlank()) {
return Optional.empty();
@@ -244,8 +272,9 @@ private Language resolveEffectiveLanguage(UUID playerUuid, Language preference,
}
private String resolveCountryCode(UUID playerUuid) {
- return FeatureServices.find(feature, CountryAPI.class)
- .flatMap(countryApi -> countryApi.getCountry(playerUuid))
+ return feature.findCapability(NetworkLocationApi.class)
+ .flatMap(countryApi -> countryApi.countryCode(playerUuid))
+ .map(nl.hauntedmc.proxyfeatures.api.model.CountryCode::value)
.orElse("UNKNOWN");
}
@@ -320,6 +349,17 @@ private static String nullToEmpty(String value) {
return value == null ? "" : value;
}
+ private static Locale toLocale(Language language) {
+ return switch (language) {
+ case NL -> Locale.forLanguageTag("nl");
+ case EN, AUTO -> Locale.ENGLISH;
+ };
+ }
+
+ private static Language fromLocale(Locale locale) {
+ return "nl".equalsIgnoreCase(locale.getLanguage()) ? Language.NL : Language.EN;
+ }
+
private static String rootMessage(Throwable throwable) {
Throwable current = throwable;
while (current.getCause() != null) {
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlist/PlayerList.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlist/PlayerList.java
index aa4cd202..2bd53e10 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlist/PlayerList.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlist/PlayerList.java
@@ -1,21 +1,20 @@
package nl.hauntedmc.proxyfeatures.features.playerlist;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.features.playerlist.command.GlobalListCommand;
import nl.hauntedmc.proxyfeatures.features.playerlist.command.ListCommand;
import nl.hauntedmc.proxyfeatures.features.playerlist.internal.PlayerListHandler;
-import nl.hauntedmc.proxyfeatures.features.playerlist.meta.Meta;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
import java.util.List;
-public class PlayerList extends VelocityBaseFeature {
+public class PlayerList extends VelocityBaseFeature {
private PlayerListHandler playerListHandler;
- public PlayerList(FeatureContext context) {
+ public PlayerList(FeatureContext context) {
super(context);
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlist/command/GlobalListCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlist/command/GlobalListCommand.java
index cd809d69..951c387a 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlist/command/GlobalListCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlist/command/GlobalListCommand.java
@@ -4,8 +4,7 @@
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import net.kyori.adventure.text.Component;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
-import nl.hauntedmc.proxyfeatures.api.util.type.CastUtils;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
import nl.hauntedmc.proxyfeatures.features.playerlist.PlayerList;
import java.util.Collection;
@@ -19,7 +18,7 @@ public class GlobalListCommand implements FeatureCommand {
public GlobalListCommand(PlayerList feature) {
this.feature = feature;
- blacklist = CastUtils.safeCastToList(feature.getConfigHandler().get("blacklist"), String.class);
+ blacklist = feature.getConfigHandler().getList("blacklist", String.class, List.of());
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlist/command/ListCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlist/command/ListCommand.java
index 2d9fccd2..8e1617c1 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlist/command/ListCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlist/command/ListCommand.java
@@ -5,8 +5,7 @@
import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import net.kyori.adventure.text.Component;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
-import nl.hauntedmc.proxyfeatures.api.util.type.CastUtils;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
import nl.hauntedmc.proxyfeatures.features.playerlist.PlayerList;
import java.util.List;
@@ -21,7 +20,7 @@ public class ListCommand implements FeatureCommand {
public ListCommand(PlayerList feature) {
this.feature = feature;
- this.blacklist = CastUtils.safeCastToList(feature.getConfigHandler().get("blacklist"), String.class);
+ this.blacklist = feature.getConfigHandler().getList("blacklist", String.class, List.of());
}
public void execute(Invocation invocation) {
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlist/internal/PlayerListHandler.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlist/internal/PlayerListHandler.java
index 68e34d86..ad102612 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlist/internal/PlayerListHandler.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlist/internal/PlayerListHandler.java
@@ -6,8 +6,7 @@
import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.TextComponent;
import nl.hauntedmc.proxyfeatures.features.playerlist.PlayerList;
-import nl.hauntedmc.proxyfeatures.features.vanish.internal.VanishAPI;
-import nl.hauntedmc.proxyfeatures.framework.service.FeatureServices;
+import nl.hauntedmc.proxyfeatures.api.capability.presence.PresenceApi;
import java.util.*;
import java.util.concurrent.TimeUnit;
@@ -184,13 +183,13 @@ public Component formatPlayerList(String serverName, Collection players,
}
private Predicate visiblePlayerPredicate() {
- Optional vanishApi = FeatureServices.find(feature, VanishAPI.class);
+ Optional vanishApi = feature.findCapability(PresenceApi.class);
if (vanishApi.isEmpty()) {
return player -> true;
}
- VanishAPI api = vanishApi.get();
- return player -> !api.isVanished(player.getUniqueId());
+ PresenceApi api = vanishApi.get();
+ return player -> !api.isHidden(player.getUniqueId());
}
private ServerView createServerView(RegisteredServer server, Predicate isVisible) {
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlist/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlist/meta/Meta.java
deleted file mode 100644
index d6880a0e..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/playerlist/meta/Meta.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.playerlist.meta;
-
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-import java.util.List;
-
-public class Meta implements BaseMeta {
-
- @Override
- public String getFeatureName() {
- return "PlayerList";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.1.0";
- }
-
- @Override
- public List getDependencies() {
- return List.of();
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/proxyinfo/ProxyInfo.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/proxyinfo/ProxyInfo.java
index 266d35b0..9f17b347 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/proxyinfo/ProxyInfo.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/proxyinfo/ProxyInfo.java
@@ -1,19 +1,18 @@
// src/main/java/nl/hauntedmc/proxyfeatures/features/proxyinfo/ProxyInfo.java
package nl.hauntedmc.proxyfeatures.features.proxyinfo;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.features.proxyinfo.command.ProxyInfoCommand;
-import nl.hauntedmc.proxyfeatures.features.proxyinfo.meta.Meta;
import java.time.Instant;
-public class ProxyInfo extends VelocityBaseFeature {
+public class ProxyInfo extends VelocityBaseFeature {
private final Instant startTime;
- public ProxyInfo(FeatureContext context) {
+ public ProxyInfo(FeatureContext context) {
super(context);
this.startTime = Instant.now();
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/proxyinfo/command/ProxyInfoCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/proxyinfo/command/ProxyInfoCommand.java
index 0b774de6..8285c946 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/proxyinfo/command/ProxyInfoCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/proxyinfo/command/ProxyInfoCommand.java
@@ -4,7 +4,7 @@
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.server.RegisteredServer;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
import nl.hauntedmc.proxyfeatures.features.proxyinfo.ProxyInfo;
import java.lang.management.ManagementFactory;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/proxyinfo/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/proxyinfo/meta/Meta.java
deleted file mode 100644
index 37a2c197..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/proxyinfo/meta/Meta.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.proxyinfo.meta;
-
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-public class Meta implements BaseMeta {
-
- @Override
- public String getFeatureName() {
- return "ProxyInfo";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.0.0";
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/queue/Queue.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/queue/Queue.java
index a2a069db..c24bfb7c 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/queue/Queue.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/queue/Queue.java
@@ -1,24 +1,22 @@
package nl.hauntedmc.proxyfeatures.features.queue;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.api.queue.QueueAdmissionAPI;
-import nl.hauntedmc.proxyfeatures.api.queue.QueueObservabilityAPI;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityAPI;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
+import nl.hauntedmc.proxyfeatures.api.capability.queue.QueueApi;
+import nl.hauntedmc.proxyfeatures.framework.admission.CapacityAPI;
+import nl.hauntedmc.proxyfeatures.framework.admission.QueueAdmissionPort;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.features.queue.command.QueueCommand;
import nl.hauntedmc.proxyfeatures.features.queue.listener.ConnectionListener;
-import nl.hauntedmc.proxyfeatures.features.queue.meta.Meta;
-import nl.hauntedmc.proxyfeatures.framework.service.FeatureServices;
import java.time.Duration;
import java.util.List;
-public class Queue extends VelocityBaseFeature {
+public class Queue extends VelocityBaseFeature {
private QueueManager manager;
- public Queue(FeatureContext context) {
+ public Queue(FeatureContext context) {
super(context);
}
@@ -71,10 +69,10 @@ public MessageMap getDefaultMessages() {
@Override
public void initialize() {
- CapacityAPI capacity = FeatureServices.require(this, CapacityAPI.class);
+ CapacityAPI capacity = requireInternalService(CapacityAPI.class);
manager = new QueueManager(this, getPlugin().getLogger(), capacity);
- getLifecycleManager().getApiManager().registerService(QueueAdmissionAPI.class, manager);
- getLifecycleManager().getApiManager().registerService(QueueObservabilityAPI.class, manager);
+ getLifecycleManager().getApiManager().registerService(QueueApi.class, manager);
+ getLifecycleManager().getApiManager().registerInternalService(QueueAdmissionPort.class, manager);
getLifecycleManager().getListenerManager().registerListener(new ConnectionListener(manager));
getLifecycleManager().getCommandManager().registerFeatureCommand(new QueueCommand(this, manager));
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/queue/QueueManager.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/queue/QueueManager.java
index 42cd49fb..64437378 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/queue/QueueManager.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/queue/QueueManager.java
@@ -4,16 +4,22 @@
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.scheduler.ScheduledTask;
-import nl.hauntedmc.proxyfeatures.api.queue.QueueAdmissionAPI;
-import nl.hauntedmc.proxyfeatures.api.queue.QueueObservabilityAPI;
-import nl.hauntedmc.proxyfeatures.api.queue.QueueServerSnapshot;
-import nl.hauntedmc.proxyfeatures.api.queue.QueueSnapshot;
-import nl.hauntedmc.proxyfeatures.api.capacity.AdmissionIntent;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityAPI;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityDecision;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityDenialReason;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityLease;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityRequest;
+import nl.hauntedmc.proxyfeatures.api.capability.queue.QueueApi;
+import nl.hauntedmc.proxyfeatures.api.capability.queue.QueueJoinRequest;
+import nl.hauntedmc.proxyfeatures.api.capability.queue.QueueJoinResult;
+import nl.hauntedmc.proxyfeatures.api.capability.queue.QueueJoinStatus;
+import nl.hauntedmc.proxyfeatures.api.capability.queue.QueueLeaveStatus;
+import nl.hauntedmc.proxyfeatures.api.capability.queue.QueueServerSnapshot;
+import nl.hauntedmc.proxyfeatures.api.capability.queue.QueueSnapshot;
+import nl.hauntedmc.proxyfeatures.api.capability.queue.QueuedPlayerSnapshot;
+import nl.hauntedmc.proxyfeatures.api.model.ServerId;
+import nl.hauntedmc.proxyfeatures.framework.admission.AdmissionIntent;
+import nl.hauntedmc.proxyfeatures.framework.admission.CapacityAPI;
+import nl.hauntedmc.proxyfeatures.framework.admission.CapacityDecision;
+import nl.hauntedmc.proxyfeatures.framework.admission.CapacityDenialReason;
+import nl.hauntedmc.proxyfeatures.framework.admission.CapacityLease;
+import nl.hauntedmc.proxyfeatures.framework.admission.CapacityRequest;
+import nl.hauntedmc.proxyfeatures.framework.admission.QueueAdmissionPort;
import nl.hauntedmc.proxyfeatures.features.queue.model.QueueEntry;
import nl.hauntedmc.proxyfeatures.features.queue.model.ServerQueue;
import nl.hauntedmc.proxyfeatures.features.queue.util.PriorityResolver;
@@ -30,12 +36,14 @@
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
/** Priority/FIFO waiting and paced dispatch. Capacity is the sole capacity authority. */
-public final class QueueManager implements QueueAdmissionAPI, QueueObservabilityAPI {
+public final class QueueManager implements QueueApi, QueueAdmissionPort {
private static final class Dispatch {
private final QueueEntry entry;
@@ -158,24 +166,71 @@ public synchronized void shutdown() {
actionbarCycle.clear();
}
- @Override
public boolean isQueueEnabled(String serverName) {
return !closed && queues.containsKey(normalize(serverName));
}
+ @Override
+ public boolean isEnabled(ServerId server) {
+ return isQueueEnabled(server.value());
+ }
+
public boolean isServerQueued(String serverName) {
return isQueueEnabled(serverName);
}
@Override
- public synchronized boolean enqueue(Player player, String serverName, CapacityDenialReason reason,
- CapacityRequest admissionContext) {
+ public synchronized boolean enqueueDenied(
+ Player player,
+ ServerId server,
+ CapacityDenialReason reason,
+ CapacityRequest admissionContext
+ ) {
+ QueueJoinResult result = enqueueNow(player, server.value(), reason, admissionContext);
+ return result.entry().isPresent();
+ }
+
+ @Override
+ public CompletionStage join(QueueJoinRequest request) {
+ if (request == null || closed) {
+ return CompletableFuture.completedFuture(QueueJoinResult.failure(QueueJoinStatus.UNAVAILABLE));
+ }
+ Player player = proxy.getPlayer(request.playerId()).orElse(null);
+ if (player == null) {
+ return CompletableFuture.completedFuture(QueueJoinResult.failure(QueueJoinStatus.PLAYER_OFFLINE));
+ }
+ if (!isEnabled(request.targetServer())) {
+ return CompletableFuture.completedFuture(QueueJoinResult.failure(QueueJoinStatus.QUEUE_DISABLED));
+ }
+ String previous = player.getCurrentServer()
+ .map(connection -> connection.getServerInfo().getName())
+ .orElse("");
+ CapacityRequest context = capacity.createRequest(
+ player,
+ previous,
+ request.targetServer().value(),
+ AdmissionIntent.INTERNAL
+ );
+ return CompletableFuture.completedFuture(enqueueNow(
+ player,
+ request.targetServer().value(),
+ CapacityDenialReason.FULL,
+ context
+ ));
+ }
+
+ private synchronized QueueJoinResult enqueueNow(
+ Player player,
+ String serverName,
+ CapacityDenialReason reason,
+ CapacityRequest admissionContext
+ ) {
if (closed || player == null || admissionContext == null || reason != CapacityDenialReason.FULL) {
- return false;
+ return QueueJoinResult.failure(QueueJoinStatus.REJECTED);
}
String server = normalize(serverName);
ServerQueue target = queues.get(server);
- if (target == null) return false;
+ if (target == null) return QueueJoinResult.failure(QueueJoinStatus.QUEUE_DISABLED);
UUID playerId = player.getUniqueId();
admissionContexts.put(playerId, admissionContext);
@@ -187,6 +242,11 @@ public synchronized boolean enqueue(Player player, String serverName, CapacityDe
player.sendMessage(feature.getLocalizationHandler().getMessage("queue.join.already_in_queue")
.with("server", server).with("position", position)
.forAudience(player).build());
+ actionbarCycle.putIfAbsent(playerId, -1);
+ return QueueJoinResult.success(
+ QueueJoinStatus.ALREADY_QUEUED,
+ snapshotEntry(target, target.find(playerId).orElseThrow(), position, false)
+ );
} else {
cancelInFlight(playerId, false);
ServerQueue old = queues.get(current);
@@ -196,12 +256,15 @@ public synchronized boolean enqueue(Player player, String serverName, CapacityDe
player.sendMessage(feature.getLocalizationHandler().getMessage("queue.join.moved_between_queues")
.with("server", server).with("position", position)
.forAudience(player).build());
+ actionbarCycle.putIfAbsent(playerId, -1);
+ return QueueJoinResult.success(
+ QueueJoinStatus.MOVED,
+ snapshotEntry(target, target.find(playerId).orElseThrow(), position, false)
+ );
}
- actionbarCycle.putIfAbsent(playerId, -1);
- return true;
}
- target.enqueue(playerId, resolvePriority(player));
+ QueueEntry entry = target.enqueue(playerId, resolvePriority(player));
int position = target.positionOf(playerId).orElse(0) + 1;
player.sendMessage(feature.getLocalizationHandler().getMessage("queue.join.denied.full")
.with("server", server).with("position", position)
@@ -210,12 +273,15 @@ public synchronized boolean enqueue(Player player, String serverName, CapacityDe
.with("seconds", graceSeconds).forAudience(player).build());
actionbarCycle.putIfAbsent(playerId, -1);
scheduleDispatch(server, false);
- return true;
+ return QueueJoinResult.success(
+ QueueJoinStatus.JOINED,
+ snapshotEntry(target, entry, position, false)
+ );
}
@Override
- public void wake(String serverName) {
- scheduleDispatch(normalize(serverName), true);
+ public void capacityChanged(ServerId server) {
+ scheduleDispatch(server.value(), true);
}
private void scheduleDispatch(String server, boolean resetBackoff) {
@@ -233,7 +299,7 @@ private void scheduleDispatch(String server, boolean resetBackoff) {
}
@Override
- public synchronized boolean consumeCancelledAdvance(UUID playerId, String targetServer) {
+ public synchronized boolean consumeCancelledAdvance(UUID playerId, ServerId targetServer) {
if (playerId == null) return false;
long now = System.currentTimeMillis();
CancelledAdvance cancelled = cancelledAdvances.get(playerId);
@@ -242,14 +308,14 @@ public synchronized boolean consumeCancelledAdvance(UUID playerId, String target
cancelledAdvances.remove(playerId, cancelled);
return false;
}
- if (!cancelled.server().equals(normalize(targetServer))) return false;
+ if (!cancelled.server().equals(targetServer.value())) return false;
return cancelledAdvances.remove(playerId, cancelled);
}
@Override
public synchronized QueueSnapshot snapshot() {
long now = System.currentTimeMillis();
- Map snapshots = new LinkedHashMap<>();
+ Map snapshots = new LinkedHashMap<>();
for (String server : queues.keySet().stream().sorted().toList()) {
ServerQueue queue = queues.get(server);
if (queue == null) continue;
@@ -262,14 +328,15 @@ public synchronized QueueSnapshot snapshot() {
int dispatching = (int) inFlight.values().stream()
.filter(dispatch -> dispatch.server().equals(server))
.count();
- snapshots.put(server, new QueueServerSnapshot(
- server,
+ ServerId serverId = ServerId.of(server);
+ snapshots.put(serverId, new QueueServerSnapshot(
+ serverId,
queue.size(),
connected.get(),
queue.graceCount(),
dispatching,
blocked.get(),
- queue.oldestEnqueuedAt().orElse(null)
+ queue.oldestEnqueuedAt()
));
}
return new QueueSnapshot(snapshots, Instant.ofEpochMilli(now));
@@ -283,7 +350,7 @@ public int resolvePriority(Player player) {
return priorityResolver.resolve(player);
}
- public synchronized Optional leave(UUID playerId) {
+ public synchronized Optional leaveNow(UUID playerId) {
Optional server = findQueueOf(playerId);
if (server.isEmpty()) {
clearPlayerState(playerId);
@@ -299,6 +366,40 @@ public synchronized Optional leave(UUID playerId) {
return server;
}
+ @Override
+ public CompletionStage leave(UUID playerId) {
+ if (playerId == null || closed) {
+ return CompletableFuture.completedFuture(QueueLeaveStatus.UNAVAILABLE);
+ }
+ return CompletableFuture.completedFuture(
+ leaveNow(playerId).isPresent() ? QueueLeaveStatus.LEFT : QueueLeaveStatus.NOT_QUEUED
+ );
+ }
+
+ @Override
+ public synchronized Optional find(UUID playerId) {
+ if (playerId == null || closed) return Optional.empty();
+ for (ServerQueue queue : queues.values()) {
+ Optional entry = queue.find(playerId);
+ if (entry.isPresent()) {
+ int position = queue.positionOf(playerId).orElse(0) + 1;
+ return Optional.of(snapshotEntry(queue, entry.get(), position, false));
+ }
+ }
+ Dispatch dispatch = inFlight.get(playerId);
+ if (dispatch == null) return Optional.empty();
+ ServerQueue queue = queues.get(dispatch.server());
+ ServerId server = ServerId.of(dispatch.server());
+ return Optional.of(new QueuedPlayerSnapshot(
+ playerId,
+ server,
+ 1,
+ dispatch.entry().priority(),
+ dispatch.entry().enqueuedAt(),
+ true
+ ));
+ }
+
public synchronized void onDisconnect(UUID playerId) {
cancelledAdvances.remove(playerId);
Dispatch dispatch = inFlight.remove(playerId);
@@ -579,6 +680,22 @@ private static long deadline(long now, long delay) {
return now + Math.max(0L, delay);
}
+ private static QueuedPlayerSnapshot snapshotEntry(
+ ServerQueue queue,
+ QueueEntry entry,
+ int position,
+ boolean inFlight
+ ) {
+ return new QueuedPlayerSnapshot(
+ entry.playerId(),
+ ServerId.of(queue.serverName()),
+ position,
+ entry.priority(),
+ entry.enqueuedAt(),
+ inFlight
+ );
+ }
+
private static String normalize(String value) {
return value == null ? "" : value.trim().toLowerCase(Locale.ROOT);
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/queue/command/QueueCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/queue/command/QueueCommand.java
index 870ab697..c928ed48 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/queue/command/QueueCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/queue/command/QueueCommand.java
@@ -2,7 +2,7 @@
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
import nl.hauntedmc.proxyfeatures.features.queue.Queue;
import nl.hauntedmc.proxyfeatures.features.queue.QueueManager;
import nl.hauntedmc.proxyfeatures.features.queue.model.ServerQueue;
@@ -99,7 +99,7 @@ private void handleLeave(Invocation invocation) {
.build());
return;
}
- Optional queueServer = manager.leave(player.getUniqueId());
+ Optional queueServer = manager.leaveNow(player.getUniqueId());
if (queueServer.isEmpty()) {
source.sendMessage(feature.getLocalizationHandler()
.getMessage("queue.status.none")
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/queue/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/queue/meta/Meta.java
deleted file mode 100644
index 500563d3..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/queue/meta/Meta.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.queue.meta;
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-import java.util.List;
-
-public class Meta implements BaseMeta {
- @Override
- public String getFeatureName() {
- return "Queue";
- }
-
- @Override
- public String getFeatureVersion() {
- return "2.0.0";
- }
-
- @Override
- public List getDependencies() {
- return List.of("Capacity");
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/queue/model/ServerQueue.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/queue/model/ServerQueue.java
index cd6dc670..e4ee516a 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/queue/model/ServerQueue.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/queue/model/ServerQueue.java
@@ -107,6 +107,10 @@ public synchronized boolean contains(UUID playerId) {
return index.containsKey(playerId);
}
+ public synchronized Optional find(UUID playerId) {
+ return Optional.ofNullable(index.get(playerId));
+ }
+
public synchronized int size() {
return index.size();
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/resourcepack/ResourcePack.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/resourcepack/ResourcePack.java
index a9b1c4b4..13cc98bb 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/resourcepack/ResourcePack.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/resourcepack/ResourcePack.java
@@ -1,20 +1,19 @@
package nl.hauntedmc.proxyfeatures.features.resourcepack;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.features.resourcepack.command.ResourcePackCommand;
import nl.hauntedmc.proxyfeatures.features.resourcepack.internal.ResourcePackHandler;
import nl.hauntedmc.proxyfeatures.features.resourcepack.listener.PlayerListener;
import nl.hauntedmc.proxyfeatures.features.resourcepack.listener.ResourcePackStatusListener;
-import nl.hauntedmc.proxyfeatures.features.resourcepack.meta.Meta;
-public class ResourcePack extends VelocityBaseFeature {
+public class ResourcePack extends VelocityBaseFeature {
private ResourcePackHandler handler;
- public ResourcePack(FeatureContext context) {
+ public ResourcePack(FeatureContext context) {
super(context);
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/resourcepack/command/ResourcePackCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/resourcepack/command/ResourcePackCommand.java
index a6da5d83..b9400446 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/resourcepack/command/ResourcePackCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/resourcepack/command/ResourcePackCommand.java
@@ -4,16 +4,12 @@
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.player.ResourcePackInfo;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
import nl.hauntedmc.proxyfeatures.features.resourcepack.ResourcePack;
import nl.hauntedmc.proxyfeatures.features.resourcepack.internal.ResourcePackHandler;
import nl.hauntedmc.proxyfeatures.features.resourcepack.util.ResourceUtils;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Locale;
-import java.util.Optional;
+import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/resourcepack/internal/ResourcePackHandler.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/resourcepack/internal/ResourcePackHandler.java
index 182b6e0e..88c1fe15 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/resourcepack/internal/ResourcePackHandler.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/resourcepack/internal/ResourcePackHandler.java
@@ -5,9 +5,9 @@
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.player.ResourcePackInfo;
import com.velocitypowered.api.scheduler.ScheduledTask;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigNode;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigService;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigView;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigNode;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigService;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigView;
import nl.hauntedmc.proxyfeatures.features.resourcepack.ResourcePack;
import nl.hauntedmc.proxyfeatures.features.resourcepack.util.ResourceUtils;
import org.jetbrains.annotations.NotNull;
@@ -202,11 +202,6 @@ private void rememberPackIds(Map loadedPacks) {
.build();
}
- /** For legacy callers that didn’t specify force/prompt. Defaults: force=true, prompt_key=resourcepack.prompt */
- public @NotNull ResourcePackInfo buildPackInfo(String url, byte[] hash) {
- return buildPackInfo(url, hash, true, "resourcepack.prompt");
- }
-
public ConfigurationToken blockConfiguration(
Player player,
ResourcePackInfo packInfo,
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/resourcepack/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/resourcepack/meta/Meta.java
deleted file mode 100644
index b4fa002d..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/resourcepack/meta/Meta.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.resourcepack.meta;
-
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-public class Meta implements BaseMeta {
-
- @Override
- public String getFeatureName() {
- return "ResourcePack";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.0.0";
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/Restart.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/Restart.java
index 5931a84f..d2dbeb78 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/Restart.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/Restart.java
@@ -2,12 +2,13 @@
import nl.hauntedmc.dataprovider.database.messaging.MessagingDatabaseProvider;
import nl.hauntedmc.dataprovider.database.messaging.durable.DurableMessagingDataAccess;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityAPI;
-import nl.hauntedmc.proxyfeatures.api.capacity.RestartAdmissionAPI;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
+import nl.hauntedmc.proxyfeatures.api.capability.operations.RestartApi;
+import nl.hauntedmc.proxyfeatures.framework.admission.CapacityAPI;
+import nl.hauntedmc.proxyfeatures.framework.admission.RestartCoordinationPort;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.features.restart.command.AutoreconnectCommand;
import nl.hauntedmc.proxyfeatures.features.restart.command.ProxyRestartCommand;
import nl.hauntedmc.proxyfeatures.features.restart.internal.BackendReconnectManager;
@@ -15,15 +16,13 @@
import nl.hauntedmc.proxyfeatures.features.restart.internal.RestartCapacityCoordinator;
import nl.hauntedmc.proxyfeatures.features.restart.listener.BackendReconnectListener;
import nl.hauntedmc.proxyfeatures.features.restart.messaging.RestartLifecycleBus;
-import nl.hauntedmc.proxyfeatures.features.restart.meta.Meta;
-import nl.hauntedmc.proxyfeatures.framework.service.FeatureServices;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
-public class Restart extends VelocityBaseFeature {
+public class Restart extends VelocityBaseFeature {
private static final String DEFAULT_RESTART_STREAM = "server.restart.lifecycle";
private static final String DEFAULT_CONSUMER_GROUP = "proxyfeatures.restart.autoreconnect";
private static final int MAX_CONSUMER_GROUP_LENGTH = 128;
@@ -36,7 +35,7 @@ public class Restart extends VelocityBaseFeature {
private BackendReconnectListener reconnectListener;
private AutoreconnectCommand autoreconnectCommand;
- public Restart(FeatureContext context) {
+ public Restart(FeatureContext context) {
super(context);
}
@@ -119,6 +118,12 @@ public MessageMap getDefaultMessages() {
public void initialize() {
handler = new RestartHandler(this);
getLifecycleManager().getCommandManager().registerFeatureCommand(new ProxyRestartCommand(this));
+ capacityCoordinator = new RestartCapacityCoordinator(this, null);
+ getLifecycleManager().getApiManager().registerService(RestartApi.class, capacityCoordinator);
+ getLifecycleManager().getApiManager().registerInternalService(
+ RestartCoordinationPort.class,
+ capacityCoordinator
+ );
if (!getConfigHandler().get("backend_autoreconnect.enabled", Boolean.class, true)) return;
Optional redisProvider = registerRedisMessagingProvider("backend-autoreconnect-redis");
@@ -135,7 +140,10 @@ public void initialize() {
getLifecycleManager().getCommandManager().registerFeatureCommand(autoreconnectCommand);
getLifecycleManager().getListenerManager().registerListener(reconnectListener);
- FeatureServices.find(this, CapacityAPI.class).ifPresent(this::attachCapacity);
+ restartLifecycleBus.setCapacityCoordinator(capacityCoordinator);
+ reconnectListener.setCapacityCoordinator(capacityCoordinator);
+ autoreconnectCommand.setCapacityCoordinator(capacityCoordinator);
+ findInternalService(CapacityAPI.class).ifPresent(this::attachCapacity);
String configuredStream = getConfigHandler().get("backend_autoreconnect.stream", String.class,
DEFAULT_RESTART_STREAM);
@@ -164,12 +172,10 @@ public synchronized boolean attachCapacity(CapacityAPI capacity) {
if (attachedCapacity != null) detachCapacityInternal(false);
RestartCapacityCoordinator coordinator = capacityCoordinator;
- if (coordinator == null) coordinator = new RestartCapacityCoordinator(this, capacity);
- else coordinator.attachCapacity(capacity);
+ if (coordinator == null) return false;
+ coordinator.attachCapacity(capacity);
attachedCapacity = capacity;
- capacityCoordinator = coordinator;
- getLifecycleManager().getApiManager().registerService(RestartAdmissionAPI.class, coordinator);
if (restartLifecycleBus != null) restartLifecycleBus.setCapacityCoordinator(coordinator);
if (reconnectListener != null) reconnectListener.setCapacityCoordinator(coordinator);
if (autoreconnectCommand != null) autoreconnectCommand.setCapacityCoordinator(coordinator);
@@ -187,7 +193,6 @@ private void detachCapacityInternal(boolean shutdownCoordinator) {
RestartCapacityCoordinator coordinator = capacityCoordinator;
CapacityAPI previousCapacity = attachedCapacity;
attachedCapacity = null;
- getLifecycleManager().getApiManager().unregisterService(RestartAdmissionAPI.class);
if (coordinator == null) return;
if (shutdownCoordinator) {
if (restartLifecycleBus != null) restartLifecycleBus.setCapacityCoordinator(null);
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/command/AutoreconnectCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/command/AutoreconnectCommand.java
index fa68ee12..16ac6e7c 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/command/AutoreconnectCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/command/AutoreconnectCommand.java
@@ -2,10 +2,10 @@
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
import nl.hauntedmc.proxyfeatures.features.restart.Restart;
import nl.hauntedmc.proxyfeatures.features.restart.internal.BackendReconnectManager;
import nl.hauntedmc.proxyfeatures.features.restart.internal.RestartCapacityCoordinator;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
import java.util.List;
import java.util.Locale;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/command/ProxyRestartCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/command/ProxyRestartCommand.java
index e512b9dc..de733114 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/command/ProxyRestartCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/command/ProxyRestartCommand.java
@@ -1,7 +1,7 @@
package nl.hauntedmc.proxyfeatures.features.restart.command;
import com.velocitypowered.api.command.CommandSource;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
import nl.hauntedmc.proxyfeatures.features.restart.Restart;
import nl.hauntedmc.proxyfeatures.features.restart.internal.RestartHandler;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/internal/BackendReconnectManager.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/internal/BackendReconnectManager.java
index 1fe00145..fd6e0431 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/internal/BackendReconnectManager.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/internal/BackendReconnectManager.java
@@ -4,20 +4,12 @@
import com.velocitypowered.api.proxy.ProxyServer;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.scheduler.ScheduledTask;
+import nl.hauntedmc.proxyfeatures.contracts.messaging.RestartLifecycleMessage;
import nl.hauntedmc.proxyfeatures.features.restart.Restart;
-import nl.hauntedmc.proxyfeatures.features.restart.messaging.RestartLifecycleMessage;
import nl.hauntedmc.proxyfeatures.framework.lifecycle.FeatureTaskManager;
import java.time.Duration;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
+import java.util.*;
/**
* Owns proxy-side eligibility for backend restart reconnects.
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/internal/RestartCapacityCoordinator.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/internal/RestartCapacityCoordinator.java
index 16ff3d5e..7c0a47cb 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/internal/RestartCapacityCoordinator.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/internal/RestartCapacityCoordinator.java
@@ -2,28 +2,23 @@
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.scheduler.ScheduledTask;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityAPI;
-import nl.hauntedmc.proxyfeatures.api.capacity.RestartAdmissionAPI;
-import nl.hauntedmc.proxyfeatures.api.capacity.CapacityState;
+import nl.hauntedmc.proxyfeatures.api.capability.operations.RestartApi;
+import nl.hauntedmc.proxyfeatures.api.model.ServerId;
+import nl.hauntedmc.proxyfeatures.contracts.messaging.RestartLifecycleMessage;
import nl.hauntedmc.proxyfeatures.features.restart.Restart;
-import nl.hauntedmc.proxyfeatures.features.restart.messaging.RestartLifecycleMessage;
+import nl.hauntedmc.proxyfeatures.framework.admission.CapacityAPI;
+import nl.hauntedmc.proxyfeatures.framework.admission.CapacityState;
+import nl.hauntedmc.proxyfeatures.framework.admission.RestartCoordinationPort;
import java.time.Duration;
import java.time.Instant;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
+import java.util.*;
/**
* Mirrors validated backend restart lifecycle messages into Capacity state and return reservations.
* The existing BackendReconnectManager remains authoritative for reconnect eligibility and pacing.
*/
-public final class RestartCapacityCoordinator implements RestartAdmissionAPI {
+public final class RestartCapacityCoordinator implements RestartApi, RestartCoordinationPort {
private static final String STATE_OWNER = "restart";
@@ -90,6 +85,16 @@ public RestartCapacityCoordinator(Restart feature, CapacityAPI capacity) {
this.capacity = capacity;
}
+ @Override
+ public void attachAdmission(CapacityAPI capacity) {
+ attachCapacity(capacity);
+ }
+
+ @Override
+ public void detachAdmission(CapacityAPI capacity) {
+ detachCapacity(capacity);
+ }
+
/** Rebinds a replacement Capacity service without losing an active restart session. */
public synchronized void attachCapacity(CapacityAPI newCapacity) {
if (closed || newCapacity == null) return;
@@ -121,7 +126,7 @@ public synchronized void handle(RestartLifecycleMessage message) {
}
@Override
- public synchronized boolean isRestartReturn(UUID playerId, String serverName) {
+ public synchronized boolean isExpectedReturn(UUID playerId, ServerId server) {
if (closed || playerId == null) return false;
Candidate candidate = candidatesByPlayer.get(playerId);
if (candidate == null || !candidate.session.ready || candidate.phase != Phase.READY) return false;
@@ -129,7 +134,19 @@ public synchronized boolean isRestartReturn(UUID playerId, String serverName) {
removeSession(candidate.session, true);
return false;
}
- return candidate.session.serverName.equals(normalize(serverName));
+ return candidate.session.serverName.equals(server.value());
+ }
+
+ @Override
+ public synchronized boolean isDraining(ServerId server) {
+ if (closed) return false;
+ Session session = sessionsByServer.get(server.value());
+ if (session == null) return false;
+ if (session.expiresAtMillis <= System.currentTimeMillis()) {
+ removeSession(session, true);
+ return false;
+ }
+ return !session.ready;
}
/** Records the authoritative kick before normal proxy fallback routing selects its destination. */
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/messaging/RestartLifecycleBus.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/messaging/RestartLifecycleBus.java
index 139a22dd..e5987bfe 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/messaging/RestartLifecycleBus.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/messaging/RestartLifecycleBus.java
@@ -1,5 +1,7 @@
package nl.hauntedmc.proxyfeatures.features.restart.messaging;
+import nl.hauntedmc.proxyfeatures.contracts.messaging.RestartLifecycleMessage;
+
import nl.hauntedmc.dataprovider.database.messaging.durable.DurableDelivery;
import nl.hauntedmc.dataprovider.database.messaging.durable.DurableMessagingDataAccess;
import nl.hauntedmc.dataprovider.database.messaging.durable.DurableSubscription;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/meta/Meta.java
deleted file mode 100644
index e32dee6b..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/restart/meta/Meta.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.restart.meta;
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-public class Meta implements BaseMeta {
- @Override
- public String getFeatureName() {
- return "Restart";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.4.0";
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/Sanctions.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/Sanctions.java
index c7ec66b3..ae9f6785 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/Sanctions.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/Sanctions.java
@@ -1,24 +1,25 @@
package nl.hauntedmc.proxyfeatures.features.sanctions;
import nl.hauntedmc.dataprovider.api.orm.ORMContext;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.api.capability.moderation.SanctionsApi;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.features.sanctions.audit.PlayerSanctionsSecurityLogEntity;
import nl.hauntedmc.proxyfeatures.features.sanctions.audit.SanctionsSecurityAuditLogService;
import nl.hauntedmc.proxyfeatures.features.sanctions.command.*;
import nl.hauntedmc.proxyfeatures.features.sanctions.entity.SanctionEntity;
import nl.hauntedmc.proxyfeatures.features.sanctions.listener.ConnectListener;
-import nl.hauntedmc.proxyfeatures.features.sanctions.meta.Meta;
import nl.hauntedmc.proxyfeatures.features.sanctions.service.DiscordService;
import nl.hauntedmc.proxyfeatures.features.sanctions.service.SanctionsService;
+import nl.hauntedmc.proxyfeatures.features.sanctions.service.SanctionsCapability;
import nl.hauntedmc.proxyfeatures.features.sanctions.service.ServiceLookup;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReferenceResolver;
import java.time.Duration;
-public class Sanctions extends VelocityBaseFeature {
+public class Sanctions extends VelocityBaseFeature {
private ORMContext orm;
private SanctionsService service;
@@ -26,7 +27,7 @@ public class Sanctions extends VelocityBaseFeature {
private DiscordService discordService;
private SanctionsSecurityAuditLogService securityAuditLogService;
- public Sanctions(FeatureContext context) {
+ public Sanctions(FeatureContext context) {
super(context);
}
@@ -189,13 +190,14 @@ public void initialize() {
SanctionEntity.class,
PlayerSanctionsSecurityLogEntity.class)
.orElseThrow();
- PlayerReferenceResolver playerResolver = new PlayerReferenceResolver(
- getPlugin().getDataRegistry()
- .orElseThrow(() -> new IllegalStateException("DataRegistryApi is required for Sanctions."))
- );
+ PlayerReferenceResolver playerResolver = getPlugin().getPlayerReferenceResolver();
securityAuditLogService = new SanctionsSecurityAuditLogService(getLogger(), orm, playerResolver);
service = new SanctionsService(this);
+ getLifecycleManager().getApiManager().registerService(
+ SanctionsApi.class,
+ new SanctionsCapability(service)
+ );
serviceLookup = new ServiceLookup(this);
discordService = new DiscordService(this);
@@ -218,7 +220,7 @@ public void initialize() {
getLifecycleManager().getTaskManager().scheduleRepeatingTask(() -> service.sweepExpiries(), Duration.ofSeconds(sweep));
}
- private void registerAsyncCommand(nl.hauntedmc.proxyfeatures.api.command.FeatureCommand command) {
+ private void registerAsyncCommand(nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand command) {
getLifecycleManager().getCommandManager()
.registerFeatureCommand(new nl.hauntedmc.proxyfeatures.features.sanctions.command.AsyncSanctionsCommand(
this,
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/AsyncSanctionsCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/AsyncSanctionsCommand.java
index 8eee4a36..a3232ba8 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/AsyncSanctionsCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/AsyncSanctionsCommand.java
@@ -2,7 +2,7 @@
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.command.SimpleCommand;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
import nl.hauntedmc.proxyfeatures.features.sanctions.Sanctions;
import java.util.List;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/BanCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/BanCommand.java
index 3e69b952..338ff4af 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/BanCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/BanCommand.java
@@ -3,7 +3,7 @@
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReference;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
import nl.hauntedmc.proxyfeatures.features.sanctions.Sanctions;
import nl.hauntedmc.proxyfeatures.features.sanctions.entity.SanctionEntity;
import nl.hauntedmc.proxyfeatures.framework.log.ConnectionLogHelper;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/BanIpCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/BanIpCommand.java
index 306e193d..3084216f 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/BanIpCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/BanIpCommand.java
@@ -3,7 +3,7 @@
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReference;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
import nl.hauntedmc.proxyfeatures.features.sanctions.Sanctions;
import nl.hauntedmc.proxyfeatures.features.sanctions.entity.SanctionEntity;
import nl.hauntedmc.proxyfeatures.framework.log.ConnectionLogHelper;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/KickCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/KickCommand.java
index c139b931..a3b875ab 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/KickCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/KickCommand.java
@@ -3,8 +3,8 @@
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReference;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
-import nl.hauntedmc.proxyfeatures.api.util.text.placeholder.MessagePlaceholders;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.toolkit.text.placeholder.MessagePlaceholders;
import nl.hauntedmc.proxyfeatures.features.sanctions.Sanctions;
import nl.hauntedmc.proxyfeatures.framework.log.ConnectionLogHelper;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/MuteCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/MuteCommand.java
index a8299d80..a0736450 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/MuteCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/MuteCommand.java
@@ -3,7 +3,7 @@
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReference;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
import nl.hauntedmc.proxyfeatures.features.sanctions.Sanctions;
import nl.hauntedmc.proxyfeatures.features.sanctions.entity.SanctionEntity;
import nl.hauntedmc.proxyfeatures.framework.log.ConnectionLogHelper;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/SanctionListCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/SanctionListCommand.java
index c91b0d5e..dd2924af 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/SanctionListCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/SanctionListCommand.java
@@ -3,10 +3,10 @@
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReference;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
-import nl.hauntedmc.proxyfeatures.api.util.text.format.ComponentFormatter;
-import nl.hauntedmc.proxyfeatures.api.util.text.format.TextFormatter;
-import nl.hauntedmc.proxyfeatures.api.util.text.placeholder.MessagePlaceholders;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.toolkit.text.format.ComponentFormatter;
+import nl.hauntedmc.proxyfeatures.toolkit.text.format.TextFormatter;
+import nl.hauntedmc.proxyfeatures.toolkit.text.placeholder.MessagePlaceholders;
import nl.hauntedmc.proxyfeatures.features.sanctions.Sanctions;
import nl.hauntedmc.proxyfeatures.features.sanctions.entity.SanctionEntity;
import nl.hauntedmc.proxyfeatures.features.sanctions.entity.SanctionType;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/UnbanCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/UnbanCommand.java
index 0c89c95d..3c4162c6 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/UnbanCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/UnbanCommand.java
@@ -3,8 +3,8 @@
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReference;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
-import nl.hauntedmc.proxyfeatures.api.util.text.placeholder.MessagePlaceholders;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.toolkit.text.placeholder.MessagePlaceholders;
import nl.hauntedmc.proxyfeatures.features.sanctions.Sanctions;
import nl.hauntedmc.proxyfeatures.features.sanctions.entity.SanctionType;
import nl.hauntedmc.proxyfeatures.features.sanctions.service.SanctionSuggestionLookup;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/UnbanIpCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/UnbanIpCommand.java
index bdcbc605..34973e68 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/UnbanIpCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/UnbanIpCommand.java
@@ -1,8 +1,8 @@
package nl.hauntedmc.proxyfeatures.features.sanctions.command;
import com.velocitypowered.api.command.CommandSource;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
-import nl.hauntedmc.proxyfeatures.api.util.text.placeholder.MessagePlaceholders;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.toolkit.text.placeholder.MessagePlaceholders;
import nl.hauntedmc.proxyfeatures.features.sanctions.Sanctions;
import java.net.InetAddress;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/UnmuteCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/UnmuteCommand.java
index 76b64816..6fef4836 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/UnmuteCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/UnmuteCommand.java
@@ -3,8 +3,8 @@
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReference;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
-import nl.hauntedmc.proxyfeatures.api.util.text.placeholder.MessagePlaceholders;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.toolkit.text.placeholder.MessagePlaceholders;
import nl.hauntedmc.proxyfeatures.features.sanctions.Sanctions;
import nl.hauntedmc.proxyfeatures.features.sanctions.entity.SanctionType;
import nl.hauntedmc.proxyfeatures.features.sanctions.service.SanctionSuggestionLookup;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/WarnCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/WarnCommand.java
index 91044eda..12ad3259 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/WarnCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/command/WarnCommand.java
@@ -3,8 +3,8 @@
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReference;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
-import nl.hauntedmc.proxyfeatures.api.util.text.placeholder.MessagePlaceholders;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.toolkit.text.placeholder.MessagePlaceholders;
import nl.hauntedmc.proxyfeatures.features.sanctions.Sanctions;
import java.util.*;
diff --git a/proxyfeatures-contracts/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/entity/SanctionEntity.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/entity/SanctionEntity.java
similarity index 100%
rename from proxyfeatures-contracts/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/entity/SanctionEntity.java
rename to proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/entity/SanctionEntity.java
diff --git a/proxyfeatures-contracts/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/entity/SanctionType.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/entity/SanctionType.java
similarity index 100%
rename from proxyfeatures-contracts/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/entity/SanctionType.java
rename to proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/entity/SanctionType.java
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/listener/ConnectListener.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/listener/ConnectListener.java
index 867a7a75..821f7041 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/listener/ConnectListener.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/listener/ConnectListener.java
@@ -8,7 +8,7 @@
import nl.hauntedmc.proxyfeatures.features.sanctions.entity.SanctionEntity;
import nl.hauntedmc.proxyfeatures.features.sanctions.service.SanctionsService;
import nl.hauntedmc.proxyfeatures.framework.log.ConnectionLogHelper;
-import nl.hauntedmc.proxyfeatures.api.util.text.placeholder.MessagePlaceholders;
+import nl.hauntedmc.proxyfeatures.toolkit.text.placeholder.MessagePlaceholders;
import java.net.InetAddress;
import java.net.InetSocketAddress;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/meta/Meta.java
deleted file mode 100644
index ad27cbd6..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/meta/Meta.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.sanctions.meta;
-
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-import java.util.List;
-
-public class Meta implements BaseMeta {
-
- @Override
- public String getFeatureName() {
- return "Sanctions";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.1.1";
- }
-
- @Override
- public List getPluginDependencies() {
- return List.of(DATA_PROVIDER, DATA_REGISTRY);
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/service/DiscordService.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/service/DiscordService.java
index 60e6f88d..b635277a 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/service/DiscordService.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/service/DiscordService.java
@@ -1,9 +1,9 @@
package nl.hauntedmc.proxyfeatures.features.sanctions.service;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReference;
-import nl.hauntedmc.proxyfeatures.api.util.http.DiscordUtils;
-import nl.hauntedmc.proxyfeatures.api.util.parse.JsonUtils;
-import nl.hauntedmc.proxyfeatures.api.util.text.placeholder.MessagePlaceholders;
+import nl.hauntedmc.proxyfeatures.toolkit.http.HttpTransport;
+import nl.hauntedmc.proxyfeatures.toolkit.json.JsonStrings;
+import nl.hauntedmc.proxyfeatures.toolkit.text.placeholder.MessagePlaceholders;
import nl.hauntedmc.proxyfeatures.features.sanctions.Sanctions;
import nl.hauntedmc.proxyfeatures.features.sanctions.entity.SanctionEntity;
@@ -11,10 +11,21 @@
public class DiscordService {
+ @FunctionalInterface
+ interface WebhookTransport {
+ void post(String url, String payload) throws java.io.IOException, InterruptedException;
+ }
+
private final Sanctions feature;
+ private final WebhookTransport transport;
public DiscordService(Sanctions feature) {
+ this(feature, HttpTransport::postJsonHttps);
+ }
+
+ DiscordService(Sanctions feature, WebhookTransport transport) {
this.feature = feature;
+ this.transport = java.util.Objects.requireNonNull(transport, "transport");
}
/* ========================= Public API ========================= */
@@ -128,8 +139,12 @@ private void sendEmbed(String sanctionType, int color, String... fieldJsonParts)
+ "}";
feature.getLifecycleManager().getTaskManager().scheduleTask(() -> {
- boolean delivered = DiscordUtils.sendPayload(webhookUrl, payload);
- if (!delivered) {
+ try {
+ transport.post(webhookUrl, payload);
+ } catch (InterruptedException interrupted) {
+ Thread.currentThread().interrupt();
+ feature.getLogger().warn("[Sanctions/Discord] Webhook delivery was interrupted.");
+ } catch (java.io.IOException | RuntimeException error) {
feature.getLogger().warn("[Sanctions/Discord] Failed to deliver webhook payload.");
}
});
@@ -156,7 +171,7 @@ private String joinWithCommas(String[] parts) {
}
private String json(String s) {
- return JsonUtils.escapeJson(s == null ? "" : s);
+ return JsonStrings.escapeJson(s == null ? "" : s);
}
private String nullToDash(String s) {
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/service/SanctionSuggestionLookup.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/service/SanctionSuggestionLookup.java
index 8ae23fb6..2589b1a1 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/service/SanctionSuggestionLookup.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/service/SanctionSuggestionLookup.java
@@ -30,8 +30,7 @@ public static List activeTargetNames(
}
String prefix = startsWith == null ? "" : startsWith.toLowerCase(Locale.ROOT);
- PlayerReferenceResolver resolver = new PlayerReferenceResolver(feature.getPlugin().getDataRegistry()
- .orElseThrow(() -> new IllegalStateException("DataRegistryApi is required for sanction suggestions.")));
+ PlayerReferenceResolver resolver = feature.getPlugin().getPlayerReferenceResolver();
return feature.getOrm().runInTransaction(session ->
session.createQuery(
@@ -42,7 +41,7 @@ public static List activeTargetNames(
Long.class)
.setParameter("type", type)
.list().stream()
- .map(resolver::findActiveIdentityById)
+ .map(resolver::findIdentityById)
.flatMap(java.util.Optional::stream)
.map(identity -> identity.username())
.filter(Objects::nonNull)
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/service/SanctionsCapability.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/service/SanctionsCapability.java
new file mode 100644
index 00000000..5d976859
--- /dev/null
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/service/SanctionsCapability.java
@@ -0,0 +1,59 @@
+package nl.hauntedmc.proxyfeatures.features.sanctions.service;
+
+import nl.hauntedmc.proxyfeatures.api.capability.moderation.SanctionFilter;
+import nl.hauntedmc.proxyfeatures.api.capability.moderation.SanctionSnapshot;
+import nl.hauntedmc.proxyfeatures.api.capability.moderation.SanctionsApi;
+import nl.hauntedmc.proxyfeatures.features.sanctions.entity.SanctionEntity;
+
+import java.time.Clock;
+import java.time.Instant;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.concurrent.CompletionStage;
+
+/** Persistence-independent projection of the sanctions subsystem. */
+public final class SanctionsCapability implements SanctionsApi {
+ private final SanctionsService service;
+ private final Clock clock;
+
+ public SanctionsCapability(SanctionsService service) {
+ this(service, Clock.systemUTC());
+ }
+
+ SanctionsCapability(SanctionsService service, Clock clock) {
+ this.service = Objects.requireNonNull(service, "service");
+ this.clock = Objects.requireNonNull(clock, "clock");
+ }
+
+ @Override
+ public CompletionStage> find(UUID playerId, SanctionFilter filter) {
+ Objects.requireNonNull(playerId, "playerId");
+ Objects.requireNonNull(filter, "filter");
+ return service.findSanctionsByUuid(playerId, filter == SanctionFilter.ACTIVE)
+ .thenApply(entities -> entities.stream().map(entity -> snapshot(playerId, entity, clock)).toList());
+ }
+
+ private static SanctionSnapshot snapshot(UUID playerId, SanctionEntity entity, Clock clock) {
+ Instant expiresAt = entity.getExpiresAt();
+ boolean active = entity.isActive() && (expiresAt == null || expiresAt.isAfter(clock.instant()));
+ return new SanctionSnapshot(
+ entity.getId() == null ? 0L : entity.getId(),
+ playerId,
+ switch (entity.getType()) {
+ case BAN -> nl.hauntedmc.proxyfeatures.api.capability.moderation.SanctionType.BAN;
+ case BAN_IP -> nl.hauntedmc.proxyfeatures.api.capability.moderation.SanctionType.IP_BAN;
+ case MUTE -> nl.hauntedmc.proxyfeatures.api.capability.moderation.SanctionType.MUTE;
+ case WARN -> nl.hauntedmc.proxyfeatures.api.capability.moderation.SanctionType.WARNING;
+ case KICK -> nl.hauntedmc.proxyfeatures.api.capability.moderation.SanctionType.KICK;
+ },
+ entity.getReason(),
+ entity.getActorName() == null || entity.getActorName().isBlank()
+ ? "CONSOLE" : entity.getActorName(),
+ entity.getCreatedAt(),
+ Optional.ofNullable(expiresAt),
+ active
+ );
+ }
+}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/service/SanctionsService.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/service/SanctionsService.java
index 82bb5321..968c6829 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/service/SanctionsService.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/service/SanctionsService.java
@@ -1,11 +1,9 @@
package nl.hauntedmc.proxyfeatures.features.sanctions.service;
-import nl.hauntedmc.dataregistry.api.DataRegistryApi;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReference;
import nl.hauntedmc.dataregistry.api.player.PlayerData;
-import nl.hauntedmc.dataregistry.api.player.PlayerDirectory;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReferenceResolver;
-import nl.hauntedmc.proxyfeatures.api.util.text.placeholder.MessagePlaceholders;
+import nl.hauntedmc.proxyfeatures.toolkit.text.placeholder.MessagePlaceholders;
import nl.hauntedmc.proxyfeatures.features.sanctions.Sanctions;
import nl.hauntedmc.proxyfeatures.features.sanctions.entity.SanctionEntity;
import nl.hauntedmc.proxyfeatures.features.sanctions.entity.SanctionType;
@@ -32,25 +30,22 @@ public class SanctionsService {
private final Map muteCache = new ConcurrentHashMap<>();
public SanctionsService(Sanctions feature) {
- this(feature, feature.getPlugin().getDataRegistry()
- .orElseThrow(() -> new IllegalStateException("DataRegistryApi is required for Sanctions.")));
- }
-
- SanctionsService(Sanctions feature, DataRegistryApi dataRegistry) {
this.feature = feature;
- this.playerResolver = new PlayerReferenceResolver(dataRegistry);
- this.players = dataRegistry.players();
+ this.playerResolver = feature.getPlugin().getPlayerReferenceResolver();
+ this.players = feature.getPlugin().getDataRegistry()
+ .orElseThrow(() -> new IllegalStateException("DataRegistryApi is required for Sanctions."))
+ .players();
}
- SanctionsService(Sanctions feature, PlayerDirectory playerDirectory) {
+ SanctionsService(Sanctions feature, PlayerReferenceResolver playerResolver) {
this.feature = feature;
- this.playerResolver = new PlayerReferenceResolver(playerDirectory);
+ this.playerResolver = playerResolver;
this.players = null;
}
- SanctionsService(Sanctions feature, PlayerDirectory playerDirectory, PlayerData players) {
+ SanctionsService(Sanctions feature, PlayerReferenceResolver playerResolver, PlayerData players) {
this.feature = feature;
- this.playerResolver = new PlayerReferenceResolver(playerDirectory);
+ this.playerResolver = playerResolver;
this.players = Objects.requireNonNull(players, "players");
}
@@ -108,8 +103,8 @@ private SanctionEntity createActive(SanctionType type, PlayerReference target, S
String sanitized = sanitizeReason(reason);
Instant now = Instant.now();
return feature.getOrm().runInTransaction(session -> {
- PlayerReference managedTarget = target == null ? null : playerResolver.resolveManagedById(session, target.getId());
- PlayerReference managedActor = actor == null ? null : playerResolver.resolveManagedById(session, actor.getId());
+ PlayerReference managedTarget = target == null ? null : playerResolver.resolveReferenceById(target.getId());
+ PlayerReference managedActor = actor == null ? null : playerResolver.resolveReferenceById(actor.getId());
// Deactivate any pre-existing active sanctions of same type & target/IP in the same transaction
if (managedTarget != null) {
session.createMutationQuery(
@@ -149,8 +144,8 @@ private void createInstant(SanctionType type, PlayerReference target, String ip,
String sanitized = sanitizeReason(reason);
Instant now = Instant.now();
feature.getOrm().runInTransaction(session -> {
- PlayerReference managedTarget = target == null ? null : playerResolver.resolveManagedById(session, target.getId());
- PlayerReference managedActor = actor == null ? null : playerResolver.resolveManagedById(session, actor.getId());
+ PlayerReference managedTarget = target == null ? null : playerResolver.resolveReferenceById(target.getId());
+ PlayerReference managedActor = actor == null ? null : playerResolver.resolveReferenceById(actor.getId());
SanctionEntity s = new SanctionEntity();
s.setType(type);
s.setTargetPlayer(managedTarget);
@@ -519,7 +514,7 @@ private String resolveUsername(PlayerReference ref) {
}
Long id = ref.getId();
if (id == null) return null;
- return playerResolver.findActiveIdentityById(id)
+ return playerResolver.findIdentityById(id)
.map(nl.hauntedmc.dataregistry.api.player.PlayerIdentity::username)
.orElse(null);
}
@@ -670,6 +665,15 @@ public List listSanctionsForPlayer(PlayerReference p, boolean ac
});
}
+ /** Resolves a public UUID without exposing DataRegistry or ORM types to callers. */
+ public CompletionStage> findSanctionsByUuid(UUID playerId, boolean activeOnly) {
+ if (playerId == null) return CompletableFuture.completedFuture(List.of());
+ return playerResolver.findByUuidAsync(playerId)
+ .thenApply(reference -> reference
+ .map(value -> listSanctionsForPlayer(value, activeOnly))
+ .orElseGet(List::of));
+ }
+
/**
* Resolve a username for a (possibly detached) PlayerReference safely.
*/
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/service/ServiceLookup.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/service/ServiceLookup.java
index 199b9edf..71fefd9c 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/service/ServiceLookup.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/sanctions/service/ServiceLookup.java
@@ -1,8 +1,6 @@
package nl.hauntedmc.proxyfeatures.features.sanctions.service;
-import nl.hauntedmc.dataregistry.api.DataRegistryApi;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReference;
-import nl.hauntedmc.dataregistry.api.player.PlayerDirectory;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReferenceResolver;
import nl.hauntedmc.proxyfeatures.features.sanctions.Sanctions;
@@ -12,16 +10,11 @@ public class ServiceLookup {
private final PlayerReferenceResolver playerResolver;
public ServiceLookup(Sanctions feature) {
- this(feature.getPlugin().getDataRegistry()
- .orElseThrow(() -> new IllegalStateException("DataRegistryApi is required for Sanctions.")));
+ this.playerResolver = feature.getPlugin().getPlayerReferenceResolver();
}
- ServiceLookup(DataRegistryApi dataRegistry) {
- this.playerResolver = new PlayerReferenceResolver(dataRegistry);
- }
-
- ServiceLookup(PlayerDirectory playerDirectory) {
- this.playerResolver = new PlayerReferenceResolver(playerDirectory);
+ ServiceLookup(PlayerReferenceResolver playerResolver) {
+ this.playerResolver = playerResolver;
}
public Optional byName(String name) {
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/serverlinks/ServerLinks.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/serverlinks/ServerLinks.java
index eb276363..3759c67f 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/serverlinks/ServerLinks.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/serverlinks/ServerLinks.java
@@ -1,19 +1,18 @@
package nl.hauntedmc.proxyfeatures.features.serverlinks;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.features.serverlinks.internal.ServerLinksHandler;
import nl.hauntedmc.proxyfeatures.features.serverlinks.listener.JoinListener;
-import nl.hauntedmc.proxyfeatures.features.serverlinks.meta.Meta;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
-public class ServerLinks extends VelocityBaseFeature {
+public class ServerLinks extends VelocityBaseFeature {
private ServerLinksHandler serverLinksHandler;
- public ServerLinks(FeatureContext context) {
+ public ServerLinks(FeatureContext context) {
super(context);
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/serverlinks/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/serverlinks/meta/Meta.java
deleted file mode 100644
index 63565d5c..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/serverlinks/meta/Meta.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.serverlinks.meta;
-
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-public class Meta implements BaseMeta {
-
- @Override
- public String getFeatureName() {
- return "ServerLinks";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.0.0";
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/slashserver/SlashServer.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/slashserver/SlashServer.java
index acb358fb..bbe2387e 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/slashserver/SlashServer.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/slashserver/SlashServer.java
@@ -1,14 +1,13 @@
package nl.hauntedmc.proxyfeatures.features.slashserver;
import com.velocitypowered.api.proxy.server.RegisteredServer;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigNode;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigNode;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.features.slashserver.command.SlashServerAdminCommand;
import nl.hauntedmc.proxyfeatures.features.slashserver.command.SlashServerCommand;
-import nl.hauntedmc.proxyfeatures.features.slashserver.meta.Meta;
import java.util.ArrayList;
import java.util.LinkedHashMap;
@@ -19,11 +18,11 @@
import java.util.Optional;
import java.util.Set;
-public class SlashServer extends VelocityBaseFeature {
+public class SlashServer extends VelocityBaseFeature {
private final Set registeredShorthandCommands = new LinkedHashSet<>();
- public SlashServer(FeatureContext context) {
+ public SlashServer(FeatureContext context) {
super(context);
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/slashserver/command/SlashServerAdminCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/slashserver/command/SlashServerAdminCommand.java
index 3ea6087a..d942e15e 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/slashserver/command/SlashServerAdminCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/slashserver/command/SlashServerAdminCommand.java
@@ -2,7 +2,7 @@
import com.velocitypowered.api.command.CommandSource;
import net.kyori.adventure.text.Component;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
import nl.hauntedmc.proxyfeatures.features.slashserver.SlashServer;
import java.util.ArrayList;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/slashserver/command/SlashServerCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/slashserver/command/SlashServerCommand.java
index 454e5f92..142e2664 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/slashserver/command/SlashServerCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/slashserver/command/SlashServerCommand.java
@@ -3,7 +3,7 @@
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.server.RegisteredServer;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
import nl.hauntedmc.proxyfeatures.features.slashserver.SlashServer;
import java.util.List;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/slashserver/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/slashserver/meta/Meta.java
deleted file mode 100644
index 082f1597..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/slashserver/meta/Meta.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.slashserver.meta;
-
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-public class Meta implements BaseMeta {
-
- @Override
- public String getFeatureName() {
- return "SlashServer";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.0.0";
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/staffchat/StaffChat.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/staffchat/StaffChat.java
index 926fb80a..62c55ed5 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/staffchat/StaffChat.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/staffchat/StaffChat.java
@@ -1,21 +1,20 @@
package nl.hauntedmc.proxyfeatures.features.staffchat;
import nl.hauntedmc.dataprovider.database.messaging.MessagingDataAccess;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.features.staffchat.internal.ChatChannelHandler;
import nl.hauntedmc.proxyfeatures.features.staffchat.internal.messaging.EventBusHandler;
import nl.hauntedmc.proxyfeatures.features.staffchat.listener.ConnectListener;
-import nl.hauntedmc.proxyfeatures.features.staffchat.meta.Meta;
-public class StaffChat extends VelocityBaseFeature {
+public class StaffChat extends VelocityBaseFeature {
private ChatChannelHandler chatChannelHandler;
private EventBusHandler eventBusHandler;
- public StaffChat(FeatureContext context) {
+ public StaffChat(FeatureContext context) {
super(context);
}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/staffchat/internal/ChatChannel.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/staffchat/internal/ChatChannel.java
index 207ef5b9..dbf64edb 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/staffchat/internal/ChatChannel.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/staffchat/internal/ChatChannel.java
@@ -2,7 +2,7 @@
import com.velocitypowered.api.proxy.Player;
import net.kyori.adventure.text.Component;
-import nl.hauntedmc.proxyfeatures.api.util.text.format.TextFormatter;
+import nl.hauntedmc.proxyfeatures.toolkit.text.format.TextFormatter;
import nl.hauntedmc.proxyfeatures.features.staffchat.StaffChat;
import java.util.Collections;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/staffchat/listener/ConnectListener.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/staffchat/listener/ConnectListener.java
index 642064ef..c257109e 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/staffchat/listener/ConnectListener.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/staffchat/listener/ConnectListener.java
@@ -6,7 +6,7 @@
import com.velocitypowered.api.event.player.ServerPostConnectEvent;
import com.velocitypowered.api.proxy.Player;
import net.kyori.adventure.text.Component;
-import nl.hauntedmc.proxyfeatures.api.util.text.placeholder.MessagePlaceholders;
+import nl.hauntedmc.proxyfeatures.toolkit.text.placeholder.MessagePlaceholders;
import nl.hauntedmc.proxyfeatures.features.staffchat.StaffChat;
import nl.hauntedmc.proxyfeatures.features.staffchat.internal.ChatChannel;
import nl.hauntedmc.proxyfeatures.features.staffchat.internal.ChatChannelHandler;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/staffchat/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/staffchat/meta/Meta.java
deleted file mode 100644
index 3504ac26..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/staffchat/meta/Meta.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.staffchat.meta;
-
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-import java.util.List;
-
-public class Meta implements BaseMeta {
-
- @Override
- public String getFeatureName() {
- return "StaffChat";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.0.0";
- }
-
- @Override
- public List getPluginDependencies() {
- return List.of(DATA_PROVIDER);
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/textcommands/TextCommands.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/textcommands/TextCommands.java
index e23d38e7..dc964b5c 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/textcommands/TextCommands.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/textcommands/TextCommands.java
@@ -1,24 +1,23 @@
package nl.hauntedmc.proxyfeatures.features.textcommands;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.features.textcommands.command.TextCommand;
-import nl.hauntedmc.proxyfeatures.features.textcommands.meta.Meta;
import java.util.LinkedHashMap;
import java.util.HashMap;
import java.util.Map;
-public class TextCommands extends VelocityBaseFeature {
+public class TextCommands extends VelocityBaseFeature {
/**
* name -> command definition (message key + placeholders)
*/
private Map commands;
- public TextCommands(FeatureContext context) {
+ public TextCommands(FeatureContext context) {
super(context);
}
@@ -47,7 +46,7 @@ public void initialize() {
private void initializeTextCommands() {
var commandNodes = getConfigHandler().node("commands").children();
- for (Map.Entry entry : commandNodes.entrySet()) {
+ for (Map.Entry entry : commandNodes.entrySet()) {
String name = entry.getKey();
if (name == null || name.isBlank()) {
continue;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/textcommands/command/TextCommand.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/textcommands/command/TextCommand.java
index f238edf2..3defbf8e 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/textcommands/command/TextCommand.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/textcommands/command/TextCommand.java
@@ -3,9 +3,9 @@
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.Player;
import net.kyori.adventure.text.Component;
-import nl.hauntedmc.proxyfeatures.api.command.FeatureCommand;
-import nl.hauntedmc.proxyfeatures.api.util.text.placeholder.MessagePlaceholders;
import nl.hauntedmc.proxyfeatures.features.textcommands.TextCommands;
+import nl.hauntedmc.proxyfeatures.framework.command.FeatureCommand;
+import nl.hauntedmc.proxyfeatures.toolkit.text.placeholder.MessagePlaceholders;
import java.util.List;
import java.util.Map;
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/textcommands/meta/Meta.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/textcommands/meta/Meta.java
deleted file mode 100644
index 6d094f7b..00000000
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/textcommands/meta/Meta.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package nl.hauntedmc.proxyfeatures.features.textcommands.meta;
-
-
-import nl.hauntedmc.proxyfeatures.api.feature.meta.BaseMeta;
-
-public class Meta implements BaseMeta {
-
- @Override
- public String getFeatureName() {
- return "TextCommands";
- }
-
- @Override
- public String getFeatureVersion() {
- return "1.1.0";
- }
-}
diff --git a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/twofactor/TwoFactor.java b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/twofactor/TwoFactor.java
index ab3f1181..badf301a 100644
--- a/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/twofactor/TwoFactor.java
+++ b/proxyfeatures-platform-velocity/src/main/java/nl/hauntedmc/proxyfeatures/features/twofactor/TwoFactor.java
@@ -4,10 +4,10 @@
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.scheduler.ScheduledTask;
import nl.hauntedmc.dataprovider.api.orm.ORMContext;
-import nl.hauntedmc.proxyfeatures.api.io.config.ConfigMap;
-import nl.hauntedmc.proxyfeatures.api.io.localization.MessageMap;
-import nl.hauntedmc.proxyfeatures.features.FeatureContext;
-import nl.hauntedmc.proxyfeatures.features.VelocityBaseFeature;
+import nl.hauntedmc.proxyfeatures.toolkit.io.config.ConfigMap;
+import nl.hauntedmc.proxyfeatures.toolkit.io.localization.MessageMap;
+import nl.hauntedmc.proxyfeatures.framework.feature.FeatureContext;
+import nl.hauntedmc.proxyfeatures.framework.feature.VelocityBaseFeature;
import nl.hauntedmc.proxyfeatures.features.twofactor.audit.PlayerTwoFactorLogEntity;
import nl.hauntedmc.proxyfeatures.features.twofactor.audit.TwoFactorAuditLogService;
import nl.hauntedmc.proxyfeatures.features.twofactor.command.TwoFactorCommand;
@@ -15,11 +15,12 @@
import nl.hauntedmc.proxyfeatures.features.twofactor.crypto.TotpService;
import nl.hauntedmc.proxyfeatures.features.twofactor.crypto.TwoFactorCrypto;
import nl.hauntedmc.proxyfeatures.features.twofactor.listener.TwoFactorListener;
-import nl.hauntedmc.proxyfeatures.features.twofactor.meta.Meta;
import nl.hauntedmc.proxyfeatures.features.twofactor.persistence.OrmTwoFactorAccountStore;
import nl.hauntedmc.proxyfeatures.features.twofactor.persistence.PlayerTwoFactorEntity;
import nl.hauntedmc.proxyfeatures.features.twofactor.persistence.TwoFactorAccountStore;
import nl.hauntedmc.proxyfeatures.features.twofactor.service.TwoFactorService;
+import nl.hauntedmc.proxyfeatures.features.twofactor.service.TwoFactorCapability;
+import nl.hauntedmc.proxyfeatures.api.capability.operations.TwoFactorApi;
import nl.hauntedmc.proxyfeatures.framework.persistence.PlayerReferenceResolver;
import java.nio.file.Path;
@@ -42,7 +43,7 @@
* secure operating mode requires a dedicated lock server where unauthenticated
* players are held until they complete {@code /2fa }.
*/
-public class TwoFactor extends VelocityBaseFeature {
+public class TwoFactor extends VelocityBaseFeature {
private static final Duration LOCK_PROMPT_DELAY = Duration.ofMillis(750);
@@ -52,7 +53,7 @@ public class TwoFactor extends VelocityBaseFeature {
private final Set lockTransfersInFlight = ConcurrentHashMap.newKeySet();
private final ConcurrentHashMap trustedExpiryTasks = new ConcurrentHashMap<>();
- public TwoFactor(FeatureContext