2
0
mirror of https://github.com/proxiodev/RedisBungee.git synced 2026-04-02 21:20:47 +00:00

make RedisBungee projects compilable

still same 0.13.0 core nothing changed yet but with formatting appiled
This commit is contained in:
Mohammed Alteneiji 2026-04-01 15:58:59 +04:00
parent 88ef7822b9
commit e4fe6014ac
Signed by: ham1255
GPG Key ID: EF343502046229F4
95 changed files with 4969 additions and 4613 deletions

View File

@ -29,14 +29,14 @@ jobs:
# Artifact name # Artifact name
name: RedisBungee-Bungee name: RedisBungee-Bungee
# Destination path # Destination path
path: proxies/bungeecord/build/libs/* path: redisbungee/proxies/bungeecord/build/libs/*
- name: Upload Velocity - name: Upload Velocity
uses: actions/upload-artifact@v4.4.0 uses: actions/upload-artifact@v4.4.0
with: with:
name: RedisBungee-Velocity name: RedisBungee-Velocity
path: proxies/velocity/build/libs/* path: redisbungee/proxies/velocity/build/libs/*
- name: Upload API - name: Upload API
uses: actions/upload-artifact@v4.4.0 uses: actions/upload-artifact@v4.4.0
with: with:
name: RedisBungee-API name: RedisBungee-API
path: api/build/libs/* path: redisbungee/api/build/libs/*

View File

@ -25,11 +25,14 @@ subprojects {
} }
} }
extensions.configure<com.diffplug.gradle.spotless.SpotlessExtension> { extensions.configure<com.diffplug.gradle.spotless.SpotlessExtension> {
var redisBungeeProjects = sequenceOf("RedisBungee-API", "RedisBungee-Lang", "RedisBungee-Commands", "RedisBungee-Bungee", "RedisBungee-Proxy-Bungee", "RedisBungee-Velocity", "RedisBungee-Proxy-Velocity")
java { java {
removeUnusedImports() removeUnusedImports()
googleJavaFormat() googleJavaFormat()
if (project.name == "valiobungee-api") { if (project.name == "valiobungee-api") {
licenseHeaderFile(file("copyright_header.txt")) licenseHeaderFile(file("copyright_header.txt"))
} else if (redisBungeeProjects.contains(project.name)) {
licenseHeaderFile(rootProject.file("redisbungee/copyright_header.txt"))
} else { } else {
licenseHeaderFile(rootProject.file("copyright_header.txt")) licenseHeaderFile(rootProject.file("copyright_header.txt"))
} }

View File

@ -1,3 +1,6 @@
group=net.limework group=net.limework
version=1.0.0-SNAPSHOT version=1.0.0-SNAPSHOT
api-version=v1 api-version=v1
redisbungee-group=com.imaginarycode.minecraft
redisbungee-version=0.13.0-SNAPSHOT

View File

@ -3,22 +3,52 @@ protobuf-plugin = "0.9.5"
protobuf = "3.25.8" # needed for reference to be used for protoc protobuf = "3.25.8" # needed for reference to be used for protoc
slf4j = "2.0.17" slf4j = "2.0.17"
guava = "33.5.0-jre"
jedis = "5.2.0"
okhttp = "4.12.0"
configurateV3 = "3.7.3"
caffeine = "3.2.3"
adventure = "4.26.1"
adventure-bungeecord-platform = "4.4.1"
acf = "e2005dd62d"
bungeecordApi = "1.21-R0.5-SNAPSHOT"
velocity = "3.5.0-SNAPSHOT"
[plugins] [plugins]
blossom = "net.kyori.blossom:2.2.0" blossom = "net.kyori.blossom:2.2.0"
indragit = "net.kyori.indra.git:4.0.0" indragit = "net.kyori.indra.git:4.0.0"
shadow = "com.gradleup.shadow:9.3.1" shadow = "com.gradleup.shadow:9.3.1"
spotless = "com.diffplug.spotless:8.2.0" spotless = "com.diffplug.spotless:8.2.0"
protobuf = { id = "com.google.protobuf", version.ref = "protobuf-plugin" } protobuf = { id = "com.google.protobuf", version.ref = "protobuf-plugin" }
run-velocity = { id = "xyz.jpenilla.run-velocity", version = "2.3.1" }
[libraries] [libraries]
redisson = "org.redisson:redisson:4.3.0" # protobuf
protobuf = { group = "com.google.protobuf", name = "protobuf-java", version.ref = "protobuf" } protobuf = { group = "com.google.protobuf", name = "protobuf-java", version.ref = "protobuf" }
protoc = { group = "com.google.protobuf", name = "protoc", version.ref = "protobuf" } protoc = { group = "com.google.protobuf", name = "protoc", version.ref = "protobuf" }
caffeine = "com.github.ben-manes.caffeine:caffeine:3.2.3"
# valiobungee
redisson = "org.redisson:redisson:4.3.0"
caffeine = "com.github.ben-manes.caffeine:caffeine:3.2.3"
# logging # logging
slf4j = { group = "org.slf4j", name = "slf4j-api", version.ref = "slf4j" } slf4j = { group = "org.slf4j", name = "slf4j-api", version.ref = "slf4j" }
# testing # testing
testing-juipter = "org.junit.jupiter:junit-jupiter:6.0.3" testing-juipter = "org.junit.jupiter:junit-jupiter:6.0.3"
testing-slf4j-simple = { group = "org.slf4j", name = "slf4j-simple", version.ref = "slf4j" } testing-slf4j-simple = { group = "org.slf4j", name = "slf4j-simple", version.ref = "slf4j" }
# redisbungee speific
guava = { module = "com.google.guava:guava", version.ref = "guava" }
jedis = { module = "redis.clients:jedis", version.ref = "jedis" }
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
configurateV3 = { module = "org.spongepowered:configurate-yaml", version.ref = "configurateV3" }
# minecraft speific
adventure-api = { module = "net.kyori:adventure-api", version.ref = "adventure" }
adventure-miniMessage = { module = "net.kyori:adventure-text-minimessage", version.ref = "adventure" }
acf-core = { module = "com.github.ProxioDev.commands:acf-core", version.ref = "acf" }
acf-bungeecord = { module = "com.github.ProxioDev.commands:acf-bungee", version.ref = "acf" }
acf-velocity = { module = "com.github.ProxioDev.commands:acf-velocity", version.ref = "acf" }
platform-bungeecord = { module = "net.md-5:bungeecord-api", version.ref = "bungeecordApi" }
adventure-platforms-bungeecord = { module = "net.kyori:adventure-platform-bungeecord", version.ref = "adventure-bungeecord-platform" }
platform-velocity = { module = "com.velocitypowered:velocity-api", version.ref = "velocity" }

View File

@ -1,5 +1,3 @@
import java.io.ByteArrayOutputStream
plugins { plugins {
`java-library` `java-library`
`maven-publish` `maven-publish`
@ -8,6 +6,10 @@ plugins {
} }
version = property("redisbungee-version").toString()
group = property("redisbungee-group").toString()
dependencies { dependencies {
api(libs.guava) api(libs.guava)
api(libs.jedis) api(libs.jedis)
@ -22,8 +24,8 @@ sourceSets {
main { main {
blossom { blossom {
javaSources { javaSources {
property("version", "$version") property("version", version.toString())
property("git-commit", indraGit.commit().toString()) property("git", indraGit.commit().get().name)
} }
} }
} }

View File

@ -0,0 +1,20 @@
/*
* Copyright (c) 2013-2013 RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee;
public class Constants {
public static final String VERSION = "{{ version }}";
public static final String GIT_COMMIT = "{{ git }}";
public static String getGithubCommitLink() {
return "https://github.com/ProxioDev/ValioBungee/commit/" + GIT_COMMIT;
}
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee; package com.imaginarycode.minecraft.redisbungee;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
@ -15,13 +14,12 @@ import com.google.common.collect.Multimap;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeeMode; import com.imaginarycode.minecraft.redisbungee.api.RedisBungeeMode;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin; import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner; import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner;
import java.net.InetAddress;
import java.util.*;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPool;
import java.net.InetAddress;
import java.util.*;
/** /**
* This abstract class is extended by platform plugin to provide some platform specific methods. * This abstract class is extended by platform plugin to provide some platform specific methods.
* overall its general contains all methods needed by external usage. * overall its general contains all methods needed by external usage.
@ -52,8 +50,9 @@ public abstract class AbstractRedisBungeeAPI {
} }
/** /**
* Get the last time a player was on. If the player is currently online, this will return 0. If the player has not been recorded, * Get the last time a player was on. If the player is currently online, this will return 0. If
* this will return -1. Otherwise it will return a value in milliseconds. * the player has not been recorded, this will return -1. Otherwise it will return a value in
* milliseconds.
* *
* @param player a player name * @param player a player name
* @return the last time a player was on, if online returns a 0 * @return the last time a player was on, if online returns a 0
@ -63,11 +62,12 @@ public abstract class AbstractRedisBungeeAPI {
} }
/** /**
* Get the server where the specified player is playing. This function also deals with the case of local players * Get the server where the specified player is playing. This function also deals with the case of
* as well, and will return local information on them. * local players as well, and will return local information on them.
* *
* @param player a player uuid * @param player a player uuid
* @return a String name for the server the player is on. Can be Null if plugins is doing weird stuff to the proxy internals * @return a String name for the server the player is on. Can be Null if plugins is doing weird
* stuff to the proxy internals
*/ */
@Nullable @Nullable
public final String getServerNameFor(@NonNull UUID player) { public final String getServerNameFor(@NonNull UUID player) {
@ -76,8 +76,9 @@ public abstract class AbstractRedisBungeeAPI {
/** /**
* Get a combined list of players on this network. * Get a combined list of players on this network.
* <p> *
* <strong>Note that this function returns an instance of {@link com.google.common.collect.ImmutableSet}.</strong> * <p><strong>Note that this function returns an instance of {@link
* com.google.common.collect.ImmutableSet}.</strong>
* *
* @return a Set with all players found * @return a Set with all players found
*/ */
@ -188,8 +189,8 @@ public abstract class AbstractRedisBungeeAPI {
/** /**
* Sends a message to a PubSub channel which makes PubSubMessageEvent fire. * Sends a message to a PubSub channel which makes PubSubMessageEvent fire.
* <p> *
* Note: Since 0.12.0 registering a channel api is no longer required * <p>Note: Since 0.12.0 registering a channel api is no longer required
* *
* @param channel The PubSub channel * @param channel The PubSub channel
* @param message the message body to send * @param message the message body to send
@ -222,12 +223,14 @@ public abstract class AbstractRedisBungeeAPI {
} }
/** /**
* Fetch a name from the specified UUID. UUIDs are cached locally and in Redis. This function falls back to Mojang * Fetch a name from the specified UUID. UUIDs are cached locally and in Redis. This function
* as a last resort, so calls <strong>may</strong> be blocking. * falls back to Mojang as a last resort, so calls <strong>may</strong> be blocking.
* <p> *
* For the common use case of translating a list of UUIDs into names, use {@link #getHumanPlayersOnline()} instead. * <p>For the common use case of translating a list of UUIDs into names, use {@link
* <p> * #getHumanPlayersOnline()} instead.
* If performance is a concern, use {@link #getNameFromUuid(java.util.UUID, boolean)} as this allows you to disable Mojang lookups. *
* <p>If performance is a concern, use {@link #getNameFromUuid(java.util.UUID, boolean)} as this
* allows you to disable Mojang lookups.
* *
* @param uuid the UUID to fetch the name for * @param uuid the UUID to fetch the name for
* @return the name for the UUID * @return the name for the UUID
@ -238,12 +241,15 @@ public abstract class AbstractRedisBungeeAPI {
} }
/** /**
* Fetch a name from the specified UUID. UUIDs are cached locally and in Redis. This function can fall back to Mojang * Fetch a name from the specified UUID. UUIDs are cached locally and in Redis. This function can
* as a last resort if {@code expensiveLookups} is true, so calls <strong>may</strong> be blocking. * fall back to Mojang as a last resort if {@code expensiveLookups} is true, so calls
* <p> * <strong>may</strong> be blocking.
* For the common use case of translating the list of online players into names, use {@link #getHumanPlayersOnline()}. *
* <p> * <p>For the common use case of translating the list of online players into names, use {@link
* If performance is a concern, set {@code expensiveLookups} to false as this will disable lookups via Mojang. * #getHumanPlayersOnline()}.
*
* <p>If performance is a concern, set {@code expensiveLookups} to false as this will disable
* lookups via Mojang.
* *
* @param uuid the UUID to fetch the name for * @param uuid the UUID to fetch the name for
* @param expensiveLookups whether or not to perform potentially expensive lookups * @param expensiveLookups whether or not to perform potentially expensive lookups
@ -255,13 +261,15 @@ public abstract class AbstractRedisBungeeAPI {
} }
/** /**
* Fetch a UUID from the specified name. Names are cached locally and in Redis. This function falls back to Mojang * Fetch a UUID from the specified name. Names are cached locally and in Redis. This function
* as a last resort, so calls <strong>may</strong> be blocking. * falls back to Mojang as a last resort, so calls <strong>may</strong> be blocking.
* <p> *
* If performance is a concern, see {@link #getUuidFromName(String, boolean)}, which disables the following functions: * <p>If performance is a concern, see {@link #getUuidFromName(String, boolean)}, which disables
* the following functions:
*
* <ul> * <ul>
* <li>Searching local entries case-insensitively</li> * <li>Searching local entries case-insensitively
* <li>Searching Mojang</li> * <li>Searching Mojang
* </ul> * </ul>
* *
* @param name the UUID to fetch the name for * @param name the UUID to fetch the name for
@ -273,11 +281,12 @@ public abstract class AbstractRedisBungeeAPI {
} }
/** /**
* Fetch a UUID from the specified name. Names are cached locally and in Redis. This function falls back to Mojang * Fetch a UUID from the specified name. Names are cached locally and in Redis. This function
* as a last resort if {@code expensiveLookups} is true, so calls <strong>may</strong> be blocking. * falls back to Mojang as a last resort if {@code expensiveLookups} is true, so calls
* <p> * <strong>may</strong> be blocking.
* If performance is a concern, set {@code expensiveLookups} to false to disable searching Mojang and searching for usernames *
* case-insensitively. * <p>If performance is a concern, set {@code expensiveLookups} to false to disable searching
* Mojang and searching for usernames case-insensitively.
* *
* @param name the UUID to fetch the name for * @param name the UUID to fetch the name for
* @param expensiveLookups whether or not to perform potentially expensive lookups * @param expensiveLookups whether or not to perform potentially expensive lookups
@ -288,9 +297,9 @@ public abstract class AbstractRedisBungeeAPI {
return plugin.getUuidTranslator().getTranslatedUuid(name, expensiveLookups); return plugin.getUuidTranslator().getTranslatedUuid(name, expensiveLookups);
} }
/** /**
* returns Summoner class responsible for Single Jedis {@link redis.clients.jedis.JedisPooled} with {@link JedisPool}, Cluster Jedis {@link redis.clients.jedis.JedisCluster} handling * returns Summoner class responsible for Single Jedis {@link redis.clients.jedis.JedisPooled}
* with {@link JedisPool}, Cluster Jedis {@link redis.clients.jedis.JedisCluster} handling
* *
* @return {@link Summoner} * @return {@link Summoner}
* @since 0.8.0 * @since 0.8.0
@ -300,9 +309,9 @@ public abstract class AbstractRedisBungeeAPI {
} }
/** /**
* Kicks a player from the network using miniMessage * Kicks a player from the network using miniMessage calls {@link #getUuidFromName(String)} to get
* calls {@link #getUuidFromName(String)} to get uuid * uuid <a href="https://docs.advntr.dev/minimessage/format.html">...</a>
* <a href="https://docs.advntr.dev/minimessage/format.html">...</a> *
* @param playerName player name * @param playerName player name
* @param miniMessage kick message that player will see on kick using minimessage as format * @param miniMessage kick message that player will see on kick using minimessage as format
* @since 0.13.0 * @since 0.13.0
@ -312,8 +321,9 @@ public abstract class AbstractRedisBungeeAPI {
} }
/** /**
* Kicks a player from the network * Kicks a player from the network <a
* <a href="https://docs.advntr.dev/minimessage/format.html">...</a> * href="https://docs.advntr.dev/minimessage/format.html">...</a>
*
* @param player player uuid * @param player player uuid
* @param miniMessage kick message that player will see on kick using minimessage as format * @param miniMessage kick message that player will see on kick using minimessage as format
* @since 0.13.0 * @since 0.13.0
@ -323,8 +333,8 @@ public abstract class AbstractRedisBungeeAPI {
} }
/** /**
* shows what mode is RedisBungee is on * shows what mode is RedisBungee is on Basically what every redis mode is used like cluster or
* Basically what every redis mode is used like cluster or single instance. * single instance.
* *
* @return {@link RedisBungeeMode} * @return {@link RedisBungeeMode}
* @since 0.8.0 * @since 0.8.0

View File

@ -1,23 +0,0 @@
/*
* Copyright (c) 2013-present RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee;
public class Constants {
public final static String VERSION = "{{ version }}";
public final static String GIT_COMMIT = "{{ git-commit }}";
public static String getGithubCommitLink() {
return "https://github.com/ProxioDev/ValioBungee/commit/" + GIT_COMMIT;
}
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api; package com.imaginarycode.minecraft.redisbungee.api;
import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.Caffeine;
@ -20,16 +19,15 @@ import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerJoinedNetworkEv
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerLeftNetworkEvent; import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerLeftNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.api.events.IPubSubMessageEvent; import com.imaginarycode.minecraft.redisbungee.api.events.IPubSubMessageEvent;
import com.imaginarycode.minecraft.redisbungee.api.tasks.RedisPipelineTask; import com.imaginarycode.minecraft.redisbungee.api.tasks.RedisPipelineTask;
import java.net.InetAddress;
import java.util.*;
import java.util.concurrent.TimeUnit;
import org.json.JSONObject; import org.json.JSONObject;
import redis.clients.jedis.ClusterPipeline; import redis.clients.jedis.ClusterPipeline;
import redis.clients.jedis.Pipeline; import redis.clients.jedis.Pipeline;
import redis.clients.jedis.Response; import redis.clients.jedis.Response;
import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.UnifiedJedis;
import java.net.InetAddress;
import java.util.*;
import java.util.concurrent.TimeUnit;
public abstract class PlayerDataManager<P> { public abstract class PlayerDataManager<P> {
protected final RedisBungeePlugin<P> plugin; protected final RedisBungeePlugin<P> plugin;
@ -37,12 +35,20 @@ public abstract class PlayerDataManager<P> {
private final UnifiedJedis unifiedJedis; private final UnifiedJedis unifiedJedis;
private final String proxyId; private final String proxyId;
private final String networkId; private final String networkId;
private final LoadingCache<UUID, String> serverCache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build(this::getServerFromRedis); private final LoadingCache<UUID, String> serverCache =
private final LoadingCache<UUID, String> lastServerCache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build(this::getLastServerFromRedis); Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build(this::getServerFromRedis);
private final LoadingCache<UUID, String> proxyCache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build(this::getProxyFromRedis); private final LoadingCache<UUID, String> lastServerCache =
private final LoadingCache<UUID, InetAddress> ipCache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build(this::getIpAddressFromRedis); Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build(this::getLastServerFromRedis);
private final LoadingCache<UUID, Long> lastOnlineCache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build(this::getLastOnlineFromRedis); private final LoadingCache<UUID, String> proxyCache =
private final LoadingCache<Object, Multimap<String, UUID>> serverToPlayersCache = Caffeine.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES).build(this::serversToPlayersBuilder); Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build(this::getProxyFromRedis);
private final LoadingCache<UUID, InetAddress> ipCache =
Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build(this::getIpAddressFromRedis);
private final LoadingCache<UUID, Long> lastOnlineCache =
Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build(this::getLastOnlineFromRedis);
private final LoadingCache<Object, Multimap<String, UUID>> serverToPlayersCache =
Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(this::serversToPlayersBuilder);
public PlayerDataManager(RedisBungeePlugin<P> plugin) { public PlayerDataManager(RedisBungeePlugin<P> plugin) {
this.plugin = plugin; this.plugin = plugin;
@ -72,7 +78,8 @@ public abstract class PlayerDataManager<P> {
this.serverCache.invalidate(event.getUuid()); this.serverCache.invalidate(event.getUuid());
this.lastServerCache.invalidate(event.getUuid()); this.lastServerCache.invalidate(event.getUuid());
//TODO: We could also rely on redisbungee-serverchange pubsub messages to update the cache in-place without querying redis. That would be a lot more efficient. // TODO: We could also rely on redisbungee-serverchange pubsub messages to update the cache
// in-place without querying redis. That would be a lot more efficient.
this.serverToPlayersCache.invalidate(SERVERS_TO_PLAYERS_KEY); this.serverToPlayersCache.invalidate(SERVERS_TO_PLAYERS_KEY);
} }
@ -82,7 +89,8 @@ public abstract class PlayerDataManager<P> {
this.ipCache.invalidate(event.getUuid()); this.ipCache.invalidate(event.getUuid());
this.lastOnlineCache.invalidate(event.getUuid()); this.lastOnlineCache.invalidate(event.getUuid());
//TODO: We could also rely on redisbungee-serverchange pubsub messages to update the cache in-place without querying redis. That would be a lot more efficient. // TODO: We could also rely on redisbungee-serverchange pubsub messages to update the cache
// in-place without querying redis. That would be a lot more efficient.
this.serverToPlayersCache.invalidate(SERVERS_TO_PLAYERS_KEY); this.serverToPlayersCache.invalidate(SERVERS_TO_PLAYERS_KEY);
} }
@ -92,11 +100,11 @@ public abstract class PlayerDataManager<P> {
this.ipCache.invalidate(event.getUuid()); this.ipCache.invalidate(event.getUuid());
this.lastOnlineCache.invalidate(event.getUuid()); this.lastOnlineCache.invalidate(event.getUuid());
//TODO: We could also rely on redisbungee-serverchange pubsub messages to update the cache in-place without querying redis. That would be a lot more efficient. // TODO: We could also rely on redisbungee-serverchange pubsub messages to update the cache
// in-place without querying redis. That would be a lot more efficient.
this.serverToPlayersCache.invalidate(SERVERS_TO_PLAYERS_KEY); this.serverToPlayersCache.invalidate(SERVERS_TO_PLAYERS_KEY);
} }
protected void handlePubSubMessageEvent(IPubSubMessageEvent event) { protected void handlePubSubMessageEvent(IPubSubMessageEvent event) {
switch (event.getChannel()) { switch (event.getChannel()) {
case "redisbungee-serverchange" -> { case "redisbungee-serverchange" -> {
@ -124,7 +132,6 @@ public abstract class PlayerDataManager<P> {
handleSerializedKick(uuid, message); handleSerializedKick(uuid, message);
} }
} }
} }
protected void playerChangedServer(UUID uuid, String from, String to) { protected void playerChangedServer(UUID uuid, String from, String to) {
@ -178,8 +185,12 @@ public abstract class PlayerDataManager<P> {
} }
protected void removePlayer(UUID uuid) { protected void removePlayer(UUID uuid) {
unifiedJedis.hset("redisbungee::" + this.networkId + "::player::" + uuid + "::data", "last-online", String.valueOf(System.currentTimeMillis())); unifiedJedis.hset(
unifiedJedis.hdel("redisbungee::" + this.networkId + "::player::" + uuid + "::data", "server", "proxy", "ip"); "redisbungee::" + this.networkId + "::player::" + uuid + "::data",
"last-online",
String.valueOf(System.currentTimeMillis()));
unifiedJedis.hdel(
"redisbungee::" + this.networkId + "::player::" + uuid + "::data", "server", "proxy", "ip");
JSONObject data = new JSONObject(); JSONObject data = new JSONObject();
data.put("proxy", this.proxyId); data.put("proxy", this.proxyId);
data.put("uuid", uuid); data.put("uuid", uuid);
@ -188,27 +199,32 @@ public abstract class PlayerDataManager<P> {
this.plugin.proxyDataManager().removePlayer(uuid); this.plugin.proxyDataManager().removePlayer(uuid);
} }
protected String getProxyFromRedis(UUID uuid) { protected String getProxyFromRedis(UUID uuid) {
return unifiedJedis.hget("redisbungee::" + this.networkId + "::player::" + uuid + "::data", "proxy"); return unifiedJedis.hget(
"redisbungee::" + this.networkId + "::player::" + uuid + "::data", "proxy");
} }
protected String getServerFromRedis(UUID uuid) { protected String getServerFromRedis(UUID uuid) {
return unifiedJedis.hget("redisbungee::" + this.networkId + "::player::" + uuid + "::data", "server"); return unifiedJedis.hget(
"redisbungee::" + this.networkId + "::player::" + uuid + "::data", "server");
} }
protected String getLastServerFromRedis(UUID uuid) { protected String getLastServerFromRedis(UUID uuid) {
return unifiedJedis.hget("redisbungee::" + this.networkId + "::player::" + uuid + "::data", "last-server"); return unifiedJedis.hget(
"redisbungee::" + this.networkId + "::player::" + uuid + "::data", "last-server");
} }
protected InetAddress getIpAddressFromRedis(UUID uuid) { protected InetAddress getIpAddressFromRedis(UUID uuid) {
String ip = unifiedJedis.hget("redisbungee::" + this.networkId + "::player::" + uuid + "::data", "ip"); String ip =
unifiedJedis.hget("redisbungee::" + this.networkId + "::player::" + uuid + "::data", "ip");
if (ip == null) return null; if (ip == null) return null;
return InetAddresses.forString(ip); return InetAddresses.forString(ip);
} }
protected long getLastOnlineFromRedis(UUID uuid) { protected long getLastOnlineFromRedis(UUID uuid) {
String unixString = unifiedJedis.hget("redisbungee::" + this.networkId + "::player::" + uuid + "::data", "last-online"); String unixString =
unifiedJedis.hget(
"redisbungee::" + this.networkId + "::player::" + uuid + "::data", "last-online");
if (unixString == null) return -1; if (unixString == null) return -1;
return Long.parseLong(unixString); return Long.parseLong(unixString);
} }
@ -247,12 +263,17 @@ public abstract class PlayerDataManager<P> {
public Multimap<String, UUID> doPooledPipeline(Pipeline pipeline) { public Multimap<String, UUID> doPooledPipeline(Pipeline pipeline) {
HashMap<UUID, Response<String>> responses = new HashMap<>(); HashMap<UUID, Response<String>> responses = new HashMap<>();
for (UUID uuid : uuids) { for (UUID uuid : uuids) {
Optional.ofNullable(pipeline.hget("redisbungee::" + networkId + "::player::" + uuid + "::data", "server")).ifPresent(stringResponse -> { Optional.ofNullable(
pipeline.hget(
"redisbungee::" + networkId + "::player::" + uuid + "::data", "server"))
.ifPresent(
stringResponse -> {
responses.put(uuid, stringResponse); responses.put(uuid, stringResponse);
}); });
} }
pipeline.sync(); pipeline.sync();
responses.forEach((uuid, response) -> { responses.forEach(
(uuid, response) -> {
String key = response.get(); String key = response.get();
if (key == null) return; if (key == null) return;
@ -265,12 +286,17 @@ public abstract class PlayerDataManager<P> {
public Multimap<String, UUID> clusterPipeline(ClusterPipeline pipeline) { public Multimap<String, UUID> clusterPipeline(ClusterPipeline pipeline) {
HashMap<UUID, Response<String>> responses = new HashMap<>(); HashMap<UUID, Response<String>> responses = new HashMap<>();
for (UUID uuid : uuids) { for (UUID uuid : uuids) {
Optional.ofNullable(pipeline.hget("redisbungee::" + networkId + "::player::" + uuid + "::data", "server")).ifPresent(stringResponse -> { Optional.ofNullable(
pipeline.hget(
"redisbungee::" + networkId + "::player::" + uuid + "::data", "server"))
.ifPresent(
stringResponse -> {
responses.put(uuid, stringResponse); responses.put(uuid, stringResponse);
}); });
} }
pipeline.sync(); pipeline.sync();
responses.forEach((uuid, response) -> { responses.forEach(
(uuid, response) -> {
String key = response.get(); String key = response.get();
if (key == null) return; if (key == null) return;
builder.put(key, uuid); builder.put(key, uuid);
@ -282,5 +308,4 @@ public abstract class PlayerDataManager<P> {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,9 +7,10 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api; package com.imaginarycode.minecraft.redisbungee.api;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
@ -25,17 +26,14 @@ import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson.PubSubPay
import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson.RunCommandPayloadSerializer; import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson.RunCommandPayloadSerializer;
import com.imaginarycode.minecraft.redisbungee.api.tasks.RedisPipelineTask; import com.imaginarycode.minecraft.redisbungee.api.tasks.RedisPipelineTask;
import com.imaginarycode.minecraft.redisbungee.api.util.RedisUtil; import com.imaginarycode.minecraft.redisbungee.api.util.RedisUtil;
import redis.clients.jedis.*;
import redis.clients.jedis.params.XAddParams;
import redis.clients.jedis.params.XReadParams;
import redis.clients.jedis.resps.StreamEntry;
import java.time.Instant; import java.time.Instant;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import redis.clients.jedis.*;
import static com.google.common.base.Preconditions.checkArgument; import redis.clients.jedis.params.XAddParams;
import redis.clients.jedis.params.XReadParams;
import redis.clients.jedis.resps.StreamEntry;
public abstract class ProxyDataManager implements Runnable { public abstract class ProxyDataManager implements Runnable {
@ -47,7 +45,8 @@ public abstract class ProxyDataManager implements Runnable {
// data: // data:
// Proxy id, heartbeat (unix epoch from instant), players as int // Proxy id, heartbeat (unix epoch from instant), players as int
private final ConcurrentHashMap<String, HeartbeatPayload.HeartbeatData> heartbeats = new ConcurrentHashMap<>(); private final ConcurrentHashMap<String, HeartbeatPayload.HeartbeatData> heartbeats =
new ConcurrentHashMap<>();
private final String networkId; private final String networkId;
@ -60,7 +59,14 @@ public abstract class ProxyDataManager implements Runnable {
protected final RedisBungeePlugin<?> plugin; protected final RedisBungeePlugin<?> plugin;
private final Gson gson = new GsonBuilder().registerTypeAdapter(AbstractPayload.class, new AbstractPayloadSerializer()).registerTypeAdapter(HeartbeatPayload.class, new HeartbeatPayloadSerializer()).registerTypeAdapter(DeathPayload.class, new DeathPayloadSerializer()).registerTypeAdapter(PubSubPayload.class, new PubSubPayloadSerializer()).registerTypeAdapter(RunCommandPayload.class, new RunCommandPayloadSerializer()).create(); private final Gson gson =
new GsonBuilder()
.registerTypeAdapter(AbstractPayload.class, new AbstractPayloadSerializer())
.registerTypeAdapter(HeartbeatPayload.class, new HeartbeatPayloadSerializer())
.registerTypeAdapter(DeathPayload.class, new DeathPayloadSerializer())
.registerTypeAdapter(PubSubPayload.class, new PubSubPayloadSerializer())
.registerTypeAdapter(RunCommandPayload.class, new RunCommandPayloadSerializer())
.create();
public ProxyDataManager(RedisBungeePlugin<?> plugin) { public ProxyDataManager(RedisBungeePlugin<?> plugin) {
this.plugin = plugin; this.plugin = plugin;
@ -85,10 +91,11 @@ public abstract class ProxyDataManager implements Runnable {
// this skip checking if proxy is and its package private // this skip checking if proxy is and its package private
// due proxy shutdown shenanigans // due proxy shutdown shenanigans
public boolean isPlayerTrulyOnProxy(String proxyId, UUID uuid) { public boolean isPlayerTrulyOnProxy(String proxyId, UUID uuid) {
return unifiedJedis.sismember("redisbungee::" + this.networkId + "::proxies::" + proxyId + "::online-players", uuid.toString()); return unifiedJedis.sismember(
"redisbungee::" + this.networkId + "::proxies::" + proxyId + "::online-players",
uuid.toString());
} }
public List<String> proxiesIds() { public List<String> proxiesIds() {
return Collections.list(this.heartbeats.keys()); return Collections.list(this.heartbeats.keys());
} }
@ -109,7 +116,9 @@ public abstract class ProxyDataManager implements Runnable {
// call every 1 second // call every 1 second
public synchronized void publishHeartbeat() { public synchronized void publishHeartbeat() {
if (isClosed()) return; if (isClosed()) return;
HeartbeatPayload.HeartbeatData heartbeatData = new HeartbeatPayload.HeartbeatData(Instant.now().getEpochSecond(), this.getLocalOnlineUUIDs().size()); HeartbeatPayload.HeartbeatData heartbeatData =
new HeartbeatPayload.HeartbeatData(
Instant.now().getEpochSecond(), this.getLocalOnlineUUIDs().size());
this.heartbeats.put(this.proxyId(), heartbeatData); this.heartbeats.put(this.proxyId(), heartbeatData);
publishPayload(new HeartbeatPayload(this.proxyId, heartbeatData)); publishPayload(new HeartbeatPayload(this.proxyId, heartbeatData));
} }
@ -121,7 +130,9 @@ public abstract class ProxyDataManager implements Runnable {
public Set<UUID> doPooledPipeline(Pipeline pipeline) { public Set<UUID> doPooledPipeline(Pipeline pipeline) {
HashSet<Response<Set<String>>> responses = new HashSet<>(); HashSet<Response<Set<String>>> responses = new HashSet<>();
for (String proxyId : proxiesIds()) { for (String proxyId : proxiesIds()) {
responses.add(pipeline.smembers("redisbungee::" + networkId + "::proxies::" + proxyId + "::online-players")); responses.add(
pipeline.smembers(
"redisbungee::" + networkId + "::proxies::" + proxyId + "::online-players"));
} }
pipeline.sync(); pipeline.sync();
HashSet<UUID> uuids = new HashSet<>(); HashSet<UUID> uuids = new HashSet<>();
@ -137,7 +148,9 @@ public abstract class ProxyDataManager implements Runnable {
public Set<UUID> clusterPipeline(ClusterPipeline pipeline) { public Set<UUID> clusterPipeline(ClusterPipeline pipeline) {
HashSet<Response<Set<String>>> responses = new HashSet<>(); HashSet<Response<Set<String>>> responses = new HashSet<>();
for (String proxyId : proxiesIds()) { for (String proxyId : proxiesIds()) {
responses.add(pipeline.smembers("redisbungee::" + networkId + "::proxies::" + proxyId + "::online-players")); responses.add(
pipeline.smembers(
"redisbungee::" + networkId + "::proxies::" + proxyId + "::online-players"));
} }
pipeline.sync(); pipeline.sync();
HashSet<UUID> uuids = new HashSet<>(); HashSet<UUID> uuids = new HashSet<>();
@ -152,7 +165,6 @@ public abstract class ProxyDataManager implements Runnable {
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException("unable to get network players", e); throw new RuntimeException("unable to get network players", e);
} }
} }
public int totalNetworkPlayers() { public int totalNetworkPlayers() {
@ -179,10 +191,10 @@ public abstract class ProxyDataManager implements Runnable {
data.put("payload", gson.toJson(payload)); data.put("payload", gson.toJson(payload));
data.put("data-manager-uuid", this.dataManagerUUID.toString()); data.put("data-manager-uuid", this.dataManagerUUID.toString());
data.put("class", payload.getClassName()); data.put("class", payload.getClassName());
this.unifiedJedis.xadd(STREAM_ID, XAddParams.xAddParams().maxLen(MAX_ENTRIES).id(StreamEntryID.NEW_ENTRY), data); this.unifiedJedis.xadd(
STREAM_ID, XAddParams.xAddParams().maxLen(MAX_ENTRIES).id(StreamEntryID.NEW_ENTRY), data);
} }
private void handleHeartBeat(HeartbeatPayload payload) { private void handleHeartBeat(HeartbeatPayload payload) {
String id = payload.senderProxy(); String id = payload.senderProxy();
if (!heartbeats.containsKey(id)) { if (!heartbeats.containsKey(id)) {
@ -191,7 +203,6 @@ public abstract class ProxyDataManager implements Runnable {
heartbeats.put(id, payload.data()); heartbeats.put(id, payload.data());
} }
// call every 1 minutes // call every 1 minutes
public void correctionTask() { public void correctionTask() {
// let's check this proxy players // let's check this proxy players
@ -208,7 +219,8 @@ public abstract class ProxyDataManager implements Runnable {
plugin.logWarn("found {} that isn't in the set, adding it to the Corrected set", uuid); plugin.logWarn("found {} that isn't in the set, adding it to the Corrected set", uuid);
} }
for (UUID uuid : remove) { for (UUID uuid : remove) {
plugin.logWarn("found {} that does not belong to this proxy removing it from the corrected set", uuid); plugin.logWarn(
"found {} that does not belong to this proxy removing it from the corrected set", uuid);
} }
try { try {
new RedisPipelineTask<Void>(plugin) { new RedisPipelineTask<Void>(plugin) {
@ -222,8 +234,12 @@ public abstract class ProxyDataManager implements Runnable {
for (UUID uuid : add) { for (UUID uuid : add) {
addString.add(uuid.toString()); addString.add(uuid.toString());
} }
pipeline.srem("redisbungee::" + networkId + "::proxies::" + proxyId + "::online-players", removeString.toArray(new String[]{})); pipeline.srem(
pipeline.sadd("redisbungee::" + networkId + "::proxies::" + proxyId + "::online-players", addString.toArray(new String[]{})); "redisbungee::" + networkId + "::proxies::" + proxyId + "::online-players",
removeString.toArray(new String[] {}));
pipeline.sadd(
"redisbungee::" + networkId + "::proxies::" + proxyId + "::online-players",
addString.toArray(new String[] {}));
pipeline.sync(); pipeline.sync();
return null; return null;
} }
@ -238,8 +254,12 @@ public abstract class ProxyDataManager implements Runnable {
for (UUID uuid : add) { for (UUID uuid : add) {
addString.add(uuid.toString()); addString.add(uuid.toString());
} }
pipeline.srem("redisbungee::" + networkId + "::proxies::" + proxyId + "::online-players", removeString.toArray(new String[]{})); pipeline.srem(
pipeline.sadd("redisbungee::" + networkId + "::proxies::" + proxyId + "::online-players", addString.toArray(new String[]{})); "redisbungee::" + networkId + "::proxies::" + proxyId + "::online-players",
removeString.toArray(new String[] {}));
pipeline.sadd(
"redisbungee::" + networkId + "::proxies::" + proxyId + "::online-players",
addString.toArray(new String[] {}));
pipeline.sync(); pipeline.sync();
return null; return null;
} }
@ -250,10 +270,11 @@ public abstract class ProxyDataManager implements Runnable {
plugin.logInfo("Player set has been corrected!"); plugin.logInfo("Player set has been corrected!");
} }
// handle dead proxies "THAT" Didn't send death payload but considered dead due TIMEOUT ~30
// handle dead proxies "THAT" Didn't send death payload but considered dead due TIMEOUT ~30 seconds // seconds
final Set<String> deadProxies = new HashSet<>(); final Set<String> deadProxies = new HashSet<>();
for (Map.Entry<String, HeartbeatPayload.HeartbeatData> stringHeartbeatDataEntry : this.heartbeats.entrySet()) { for (Map.Entry<String, HeartbeatPayload.HeartbeatData> stringHeartbeatDataEntry :
this.heartbeats.entrySet()) {
String id = stringHeartbeatDataEntry.getKey(); String id = stringHeartbeatDataEntry.getKey();
long heartbeat = stringHeartbeatDataEntry.getValue().heartbeat(); long heartbeat = stringHeartbeatDataEntry.getValue().heartbeat();
if (Instant.now().getEpochSecond() - heartbeat > RedisUtil.PROXY_TIMEOUT) { if (Instant.now().getEpochSecond() - heartbeat > RedisUtil.PROXY_TIMEOUT) {
@ -266,7 +287,8 @@ public abstract class ProxyDataManager implements Runnable {
@Override @Override
public Void doPooledPipeline(Pipeline pipeline) { public Void doPooledPipeline(Pipeline pipeline) {
for (String deadProxy : deadProxies) { for (String deadProxy : deadProxies) {
pipeline.del("redisbungee::" + networkId + "::proxies::" + deadProxy + "::online-players"); pipeline.del(
"redisbungee::" + networkId + "::proxies::" + deadProxy + "::online-players");
} }
pipeline.sync(); pipeline.sync();
return null; return null;
@ -275,7 +297,8 @@ public abstract class ProxyDataManager implements Runnable {
@Override @Override
public Void clusterPipeline(ClusterPipeline pipeline) { public Void clusterPipeline(ClusterPipeline pipeline) {
for (String deadProxy : deadProxies) { for (String deadProxy : deadProxies) {
pipeline.del("redisbungee::" + networkId + "::proxies::" + deadProxy + "::online-players"); pipeline.del(
"redisbungee::" + networkId + "::proxies::" + deadProxy + "::online-players");
} }
pipeline.sync(); pipeline.sync();
return null; return null;
@ -294,7 +317,8 @@ public abstract class ProxyDataManager implements Runnable {
if (id.equals(this.proxyId())) { if (id.equals(this.proxyId())) {
return; return;
} }
for (UUID uuid : getProxyMembers(id)) plugin.fireEvent(plugin.createPlayerLeftNetworkEvent(uuid)); for (UUID uuid : getProxyMembers(id))
plugin.fireEvent(plugin.createPlayerLeftNetworkEvent(uuid));
this.heartbeats.remove(id); this.heartbeats.remove(id);
plugin.logInfo("Proxy {} has disconnected", id); plugin.logInfo("Proxy {} has disconnected", id);
} }
@ -315,21 +339,27 @@ public abstract class ProxyDataManager implements Runnable {
} }
} }
public void addPlayer(UUID uuid) { public void addPlayer(UUID uuid) {
this.unifiedJedis.sadd("redisbungee::" + this.networkId + "::proxies::" + this.proxyId + "::online-players", uuid.toString()); this.unifiedJedis.sadd(
"redisbungee::" + this.networkId + "::proxies::" + this.proxyId + "::online-players",
uuid.toString());
} }
public void removePlayer(UUID uuid) { public void removePlayer(UUID uuid) {
this.unifiedJedis.srem("redisbungee::" + this.networkId + "::proxies::" + this.proxyId + "::online-players", uuid.toString()); this.unifiedJedis.srem(
"redisbungee::" + this.networkId + "::proxies::" + this.proxyId + "::online-players",
uuid.toString());
} }
private void destroyProxyMembers() { private void destroyProxyMembers() {
unifiedJedis.del("redisbungee::" + this.networkId + "::proxies::" + this.proxyId + "::online-players"); unifiedJedis.del(
"redisbungee::" + this.networkId + "::proxies::" + this.proxyId + "::online-players");
} }
private Set<UUID> getProxyMembers(String proxyId) { private Set<UUID> getProxyMembers(String proxyId) {
Set<String> uuidsStrings = unifiedJedis.smembers("redisbungee::" + this.networkId + "::proxies::" + proxyId + "::online-players"); Set<String> uuidsStrings =
unifiedJedis.smembers(
"redisbungee::" + this.networkId + "::proxies::" + proxyId + "::online-players");
HashSet<UUID> uuids = new HashSet<>(); HashSet<UUID> uuids = new HashSet<>();
for (String proxyMember : uuidsStrings) { for (String proxyMember : uuidsStrings) {
uuids.add(UUID.fromString(proxyMember)); uuids.add(UUID.fromString(proxyMember));
@ -344,19 +374,28 @@ public abstract class ProxyDataManager implements Runnable {
public void run() { public void run() {
while (!isClosed()) { while (!isClosed()) {
try { try {
List<java.util.Map.Entry<String, List<StreamEntry>>> data = unifiedJedis.xread(XReadParams.xReadParams().block(0), Collections.singletonMap(STREAM_ID, lastStreamEntryID != null ? lastStreamEntryID : StreamEntryID.LAST_ENTRY)); List<java.util.Map.Entry<String, List<StreamEntry>>> data =
unifiedJedis.xread(
XReadParams.xReadParams().block(0),
Collections.singletonMap(
STREAM_ID,
lastStreamEntryID != null ? lastStreamEntryID : StreamEntryID.LAST_ENTRY));
for (Map.Entry<String, List<StreamEntry>> datum : data) { for (Map.Entry<String, List<StreamEntry>> datum : data) {
for (StreamEntry streamEntry : datum.getValue()) { for (StreamEntry streamEntry : datum.getValue()) {
this.lastStreamEntryID = streamEntry.getID(); this.lastStreamEntryID = streamEntry.getID();
String payloadData = streamEntry.getFields().get("payload"); String payloadData = streamEntry.getFields().get("payload");
String clazz = streamEntry.getFields().get("class"); String clazz = streamEntry.getFields().get("class");
UUID payloadDataManagerUUID = UUID.fromString(streamEntry.getFields().get("data-manager-uuid")); UUID payloadDataManagerUUID =
UUID.fromString(streamEntry.getFields().get("data-manager-uuid"));
AbstractPayload unknownPayload = (AbstractPayload) gson.fromJson(payloadData, Class.forName(clazz)); AbstractPayload unknownPayload =
(AbstractPayload) gson.fromJson(payloadData, Class.forName(clazz));
if (unknownPayload.senderProxy().equals(this.proxyId)) { if (unknownPayload.senderProxy().equals(this.proxyId)) {
if (!payloadDataManagerUUID.equals(this.dataManagerUUID)) { if (!payloadDataManagerUUID.equals(this.dataManagerUUID)) {
plugin.logWarn("detected other proxy is using same ID! {} this can cause issues, please shutdown this proxy and change the id!", this.proxyId); plugin.logWarn(
"detected other proxy is using same ID! {} this can cause issues, please shutdown this proxy and change the id!",
this.proxyId);
} }
continue; continue;
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,9 +7,9 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api; package com.imaginarycode.minecraft.redisbungee.api;
public enum RedisBungeeMode { public enum RedisBungeeMode {
SINGLE, CLUSTER SINGLE,
CLUSTER
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api; package com.imaginarycode.minecraft.redisbungee.api;
import com.imaginarycode.minecraft.redisbungee.AbstractRedisBungeeAPI; import com.imaginarycode.minecraft.redisbungee.AbstractRedisBungeeAPI;
@ -15,29 +14,25 @@ import com.imaginarycode.minecraft.redisbungee.api.config.RedisBungeeConfigurati
import com.imaginarycode.minecraft.redisbungee.api.events.EventsPlatform; import com.imaginarycode.minecraft.redisbungee.api.events.EventsPlatform;
import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner; import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner;
import com.imaginarycode.minecraft.redisbungee.api.util.uuid.UUIDTranslator; import com.imaginarycode.minecraft.redisbungee.api.util.uuid.UUIDTranslator;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
* This Class has all internal methods needed by every redis bungee plugin, and it can be used to implement another platforms than bungeecord or another forks of RedisBungee * This Class has all internal methods needed by every redis bungee plugin, and it can be used to
* <p> * implement another platforms than bungeecord or another forks of RedisBungee
* Reason this is interface because some proxies implementations require the user to extend class for plugins for example bungeecord. *
* <p>Reason this is interface because some proxies implementations require the user to extend class
* for plugins for example bungeecord.
* *
* @author Ham1255 * @author Ham1255
* @since 0.7.0 * @since 0.7.0
*/ */
public interface RedisBungeePlugin<P> extends EventsPlatform { public interface RedisBungeePlugin<P> extends EventsPlatform {
default void initialize() { default void initialize() {}
} default void stop() {}
default void stop() {
}
void logInfo(String msg); void logInfo(String msg);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.config; package com.imaginarycode.minecraft.redisbungee.api.config;
public enum HandleMotdOrder { public enum HandleMotdOrder {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,12 +7,10 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.config; package com.imaginarycode.minecraft.redisbungee.api.config;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.net.InetAddresses; import com.google.common.net.InetAddresses;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.List; import java.util.List;
@ -29,8 +27,15 @@ public class RedisBungeeConfiguration {
private final CommandsConfiguration commandsConfiguration; private final CommandsConfiguration commandsConfiguration;
private final String networkId; private final String networkId;
public RedisBungeeConfiguration(
public RedisBungeeConfiguration(String networkId, String proxyId, List<String> exemptAddresses, boolean kickWhenOnline, boolean handleReconnectToLastServer, boolean handleMotd, HandleMotdOrder handleMotdOrder, CommandsConfiguration commandsConfiguration) { String networkId,
String proxyId,
List<String> exemptAddresses,
boolean kickWhenOnline,
boolean handleReconnectToLastServer,
boolean handleMotd,
HandleMotdOrder handleMotdOrder,
CommandsConfiguration commandsConfiguration) {
this.proxyId = proxyId; this.proxyId = proxyId;
ImmutableList.Builder<InetAddress> addressBuilder = ImmutableList.builder(); ImmutableList.Builder<InetAddress> addressBuilder = ImmutableList.builder();
for (String s : exemptAddresses) { for (String s : exemptAddresses) {
@ -69,19 +74,30 @@ public class RedisBungeeConfiguration {
return this.handleReconnectToLastServer; return this.handleReconnectToLastServer;
} }
public record CommandsConfiguration(boolean redisbungeeEnabled, boolean redisbungeeLegacyEnabled, public record CommandsConfiguration(
LegacySubCommandsConfiguration legacySubCommandsConfiguration) { boolean redisbungeeEnabled,
boolean redisbungeeLegacyEnabled,
LegacySubCommandsConfiguration legacySubCommandsConfiguration) {}
} public record LegacySubCommandsConfiguration(
boolean findEnabled,
public record LegacySubCommandsConfiguration(boolean findEnabled, boolean glistEnabled, boolean ipEnabled, boolean glistEnabled,
boolean lastseenEnabled, boolean plistEnabled, boolean pproxyEnabled, boolean ipEnabled,
boolean sendtoallEnabled, boolean serveridEnabled, boolean lastseenEnabled,
boolean serveridsEnabled, boolean installFind, boolean installGlist, boolean installIp, boolean plistEnabled,
boolean installLastseen, boolean installPlist, boolean installPproxy, boolean pproxyEnabled,
boolean installSendtoall, boolean installServerid, boolean sendtoallEnabled,
boolean installServerids) { boolean serveridEnabled,
} boolean serveridsEnabled,
boolean installFind,
boolean installGlist,
boolean installIp,
boolean installLastseen,
boolean installPlist,
boolean installPproxy,
boolean installSendtoall,
boolean installServerid,
boolean installServerids) {}
public CommandsConfiguration commandsConfiguration() { public CommandsConfiguration commandsConfiguration() {
return commandsConfiguration; return commandsConfiguration;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,10 +7,8 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.config.loaders; package com.imaginarycode.minecraft.redisbungee.api.config.loaders;
import com.google.common.reflect.TypeToken; import com.google.common.reflect.TypeToken;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeeMode; import com.imaginarycode.minecraft.redisbungee.api.RedisBungeeMode;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin; import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
@ -19,6 +17,9 @@ import com.imaginarycode.minecraft.redisbungee.api.config.RedisBungeeConfigurati
import com.imaginarycode.minecraft.redisbungee.api.summoners.JedisClusterSummoner; import com.imaginarycode.minecraft.redisbungee.api.summoners.JedisClusterSummoner;
import com.imaginarycode.minecraft.redisbungee.api.summoners.JedisPooledSummoner; import com.imaginarycode.minecraft.redisbungee.api.summoners.JedisPooledSummoner;
import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner; import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner;
import java.io.IOException;
import java.nio.file.Path;
import java.util.*;
import ninja.leaping.configurate.ConfigurationNode; import ninja.leaping.configurate.ConfigurationNode;
import ninja.leaping.configurate.objectmapping.ObjectMappingException; import ninja.leaping.configurate.objectmapping.ObjectMappingException;
import ninja.leaping.configurate.yaml.YAMLConfigurationLoader; import ninja.leaping.configurate.yaml.YAMLConfigurationLoader;
@ -27,17 +28,14 @@ import redis.clients.jedis.*;
import redis.clients.jedis.providers.ClusterConnectionProvider; import redis.clients.jedis.providers.ClusterConnectionProvider;
import redis.clients.jedis.providers.PooledConnectionProvider; import redis.clients.jedis.providers.PooledConnectionProvider;
import java.io.IOException;
import java.nio.file.Path;
import java.util.*;
public interface ConfigLoader extends GenericConfigLoader { public interface ConfigLoader extends GenericConfigLoader {
int CONFIG_VERSION = 2; int CONFIG_VERSION = 2;
default void loadConfig(RedisBungeePlugin<?> plugin, Path dataFolder) throws IOException { default void loadConfig(RedisBungeePlugin<?> plugin, Path dataFolder) throws IOException {
Path configFile = createConfigFile(dataFolder, "config.yml", "config.yml"); Path configFile = createConfigFile(dataFolder, "config.yml", "config.yml");
final YAMLConfigurationLoader yamlConfigurationFileLoader = YAMLConfigurationLoader.builder().setPath(configFile).build(); final YAMLConfigurationLoader yamlConfigurationFileLoader =
YAMLConfigurationLoader.builder().setPath(configFile).build();
ConfigurationNode node = yamlConfigurationFileLoader.load(); ConfigurationNode node = yamlConfigurationFileLoader.load();
if (node.getNode("config-version").getInt(0) != CONFIG_VERSION) { if (node.getNode("config-version").getInt(0) != CONFIG_VERSION) {
handleOldConfig(dataFolder, "config.yml", "config.yml"); handleOldConfig(dataFolder, "config.yml", "config.yml");
@ -69,13 +67,19 @@ public interface ConfigLoader extends GenericConfigLoader {
// env var // env var
String proxyIdFromEnv = System.getenv("REDISBUNGEE_PROXY_ID"); String proxyIdFromEnv = System.getenv("REDISBUNGEE_PROXY_ID");
if (proxyIdFromEnv != null) { if (proxyIdFromEnv != null) {
plugin.logInfo("Overriding current configured proxy id {} and been set to {} by Environment variable REDISBUNGEE_PROXY_ID", proxyId, proxyIdFromEnv); plugin.logInfo(
"Overriding current configured proxy id {} and been set to {} by Environment variable REDISBUNGEE_PROXY_ID",
proxyId,
proxyIdFromEnv);
proxyId = proxyIdFromEnv; proxyId = proxyIdFromEnv;
} }
String networkIdFromEnv = System.getenv("REDISBUNGEE_NETWORK_ID"); String networkIdFromEnv = System.getenv("REDISBUNGEE_NETWORK_ID");
if (networkIdFromEnv != null) { if (networkIdFromEnv != null) {
plugin.logInfo("Overriding current configured network id {} and been set to {} by Environment variable REDISBUNGEE_NETWORK_ID", networkId, networkIdFromEnv); plugin.logInfo(
"Overriding current configured network id {} and been set to {} by Environment variable REDISBUNGEE_NETWORK_ID",
networkId,
networkIdFromEnv);
networkId = networkIdFromEnv; networkId = networkIdFromEnv;
} }
@ -112,47 +116,107 @@ public interface ConfigLoader extends GenericConfigLoader {
try { try {
handleMotdOrder = HandleMotdOrder.valueOf(handleMotdOrderName.toUpperCase(Locale.ROOT)); handleMotdOrder = HandleMotdOrder.valueOf(handleMotdOrderName.toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
plugin.logWarn("handle motd order value '{}' is unsupported (allowed: {})", handleMotdOrderName, HandleMotdOrder.values()); plugin.logWarn(
"handle motd order value '{}' is unsupported (allowed: {})",
handleMotdOrderName,
HandleMotdOrder.values());
} }
} }
plugin.logInfo("handle motd order: {}", handleMotdOrder); plugin.logInfo("handle motd order: {}", handleMotdOrder);
// commands // commands
boolean redisBungeeEnabled = node.getNode("commands", "redisbungee", "enabled").getBoolean(true); boolean redisBungeeEnabled =
boolean redisBungeeLegacyEnabled =node.getNode("commands", "redisbungee-legacy", "enabled").getBoolean(false); node.getNode("commands", "redisbungee", "enabled").getBoolean(true);
boolean redisBungeeLegacyEnabled =
node.getNode("commands", "redisbungee-legacy", "enabled").getBoolean(false);
boolean glistEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "glist", "enabled").getBoolean(false); boolean glistEnabled =
boolean findEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "find", "enabled").getBoolean(false); node.getNode("commands", "redisbungee-legacy", "subcommands", "glist", "enabled")
boolean lastseenEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "lastseen", "enabled").getBoolean(false); .getBoolean(false);
boolean ipEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "ip", "enabled").getBoolean(false); boolean findEnabled =
boolean pproxyEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "pproxy", "enabled").getBoolean(false); node.getNode("commands", "redisbungee-legacy", "subcommands", "find", "enabled")
boolean sendToAllEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "sendtoall", "enabled").getBoolean(false); .getBoolean(false);
boolean serverIdEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "serverid", "enabled").getBoolean(false); boolean lastseenEnabled =
boolean serverIdsEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "serverids", "enabled").getBoolean(false); node.getNode("commands", "redisbungee-legacy", "subcommands", "lastseen", "enabled")
boolean pListEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "plist", "enabled").getBoolean(false); .getBoolean(false);
boolean ipEnabled =
node.getNode("commands", "redisbungee-legacy", "subcommands", "ip", "enabled")
.getBoolean(false);
boolean pproxyEnabled =
node.getNode("commands", "redisbungee-legacy", "subcommands", "pproxy", "enabled")
.getBoolean(false);
boolean sendToAllEnabled =
node.getNode("commands", "redisbungee-legacy", "subcommands", "sendtoall", "enabled")
.getBoolean(false);
boolean serverIdEnabled =
node.getNode("commands", "redisbungee-legacy", "subcommands", "serverid", "enabled")
.getBoolean(false);
boolean serverIdsEnabled =
node.getNode("commands", "redisbungee-legacy", "subcommands", "serverids", "enabled")
.getBoolean(false);
boolean pListEnabled =
node.getNode("commands", "redisbungee-legacy", "subcommands", "plist", "enabled")
.getBoolean(false);
boolean installGlist = node.getNode("commands", "redisbungee-legacy", "subcommands", "glist", "install").getBoolean(false); boolean installGlist =
boolean installFind = node.getNode("commands", "redisbungee-legacy", "subcommands", "find", "install").getBoolean(false); node.getNode("commands", "redisbungee-legacy", "subcommands", "glist", "install")
boolean installLastseen = node.getNode("commands", "redisbungee-legacy", "subcommands", "lastseen", "install").getBoolean(false); .getBoolean(false);
boolean installIp = node.getNode("commands", "redisbungee-legacy", "subcommands", "ip", "install").getBoolean(false); boolean installFind =
boolean installPproxy = node.getNode("commands", "redisbungee-legacy", "subcommands", "pproxy", "install").getBoolean(false); node.getNode("commands", "redisbungee-legacy", "subcommands", "find", "install")
boolean installSendToAll = node.getNode("commands", "redisbungee-legacy", "subcommands", "sendtoall", "install").getBoolean(false); .getBoolean(false);
boolean installServerid = node.getNode("commands", "redisbungee-legacy", "subcommands", "serverid", "install").getBoolean(false); boolean installLastseen =
boolean installServerIds = node.getNode("commands", "redisbungee-legacy", "subcommands", "serverids", "install").getBoolean(false); node.getNode("commands", "redisbungee-legacy", "subcommands", "lastseen", "install")
boolean installPlist = node.getNode("commands", "redisbungee-legacy", "subcommands", "plist", "install").getBoolean(false); .getBoolean(false);
boolean installIp =
node.getNode("commands", "redisbungee-legacy", "subcommands", "ip", "install")
.getBoolean(false);
boolean installPproxy =
node.getNode("commands", "redisbungee-legacy", "subcommands", "pproxy", "install")
.getBoolean(false);
boolean installSendToAll =
node.getNode("commands", "redisbungee-legacy", "subcommands", "sendtoall", "install")
.getBoolean(false);
boolean installServerid =
node.getNode("commands", "redisbungee-legacy", "subcommands", "serverid", "install")
.getBoolean(false);
boolean installServerIds =
node.getNode("commands", "redisbungee-legacy", "subcommands", "serverids", "install")
.getBoolean(false);
boolean installPlist =
node.getNode("commands", "redisbungee-legacy", "subcommands", "plist", "install")
.getBoolean(false);
RedisBungeeConfiguration configuration =
RedisBungeeConfiguration configuration = new RedisBungeeConfiguration(networkId, proxyId, exemptAddresses, kickWhenOnline, reconnectToLastServer, handleMotd, handleMotdOrder, new RedisBungeeConfiguration(
networkId,
proxyId,
exemptAddresses,
kickWhenOnline,
reconnectToLastServer,
handleMotd,
handleMotdOrder,
new RedisBungeeConfiguration.CommandsConfiguration( new RedisBungeeConfiguration.CommandsConfiguration(
redisBungeeEnabled, redisBungeeLegacyEnabled, redisBungeeEnabled,
redisBungeeLegacyEnabled,
new RedisBungeeConfiguration.LegacySubCommandsConfiguration( new RedisBungeeConfiguration.LegacySubCommandsConfiguration(
findEnabled, glistEnabled, ipEnabled, findEnabled,
lastseenEnabled, pListEnabled, pproxyEnabled, glistEnabled,
sendToAllEnabled, serverIdEnabled, serverIdsEnabled, ipEnabled,
installFind, installGlist, installIp, lastseenEnabled,
installLastseen, installPlist, installPproxy, pListEnabled,
installSendToAll, installServerid, installServerIds) pproxyEnabled,
)); sendToAllEnabled,
serverIdEnabled,
serverIdsEnabled,
installFind,
installGlist,
installIp,
installLastseen,
installPlist,
installPproxy,
installSendToAll,
installServerid,
installServerIds)));
Summoner<?> summoner; Summoner<?> summoner;
RedisBungeeMode redisBungeeMode; RedisBungeeMode redisBungeeMode;
if (useSSL) { if (useSSL) {
@ -164,7 +228,10 @@ public interface ConfigLoader extends GenericConfigLoader {
GenericObjectPoolConfig<Connection> poolConfig = new GenericObjectPoolConfig<>(); GenericObjectPoolConfig<Connection> poolConfig = new GenericObjectPoolConfig<>();
poolConfig.setMaxTotal(maxConnections); poolConfig.setMaxTotal(maxConnections);
poolConfig.setBlockWhenExhausted(true); poolConfig.setBlockWhenExhausted(true);
node.getNode("redis-cluster-servers").getChildrenList().forEach((childNode) -> { node.getNode("redis-cluster-servers")
.getChildrenList()
.forEach(
(childNode) -> {
Map<Object, ? extends ConfigurationNode> hostAndPort = childNode.getChildrenMap(); Map<Object, ? extends ConfigurationNode> hostAndPort = childNode.getChildrenMap();
String host = hostAndPort.get("host").getString(); String host = hostAndPort.get("host").getString();
int port = hostAndPort.get("port").getInt(); int port = hostAndPort.get("port").getInt();
@ -174,7 +241,18 @@ public interface ConfigLoader extends GenericConfigLoader {
if (hostAndPortSet.isEmpty()) { if (hostAndPortSet.isEmpty()) {
throw new RuntimeException("No redis cluster servers specified"); throw new RuntimeException("No redis cluster servers specified");
} }
summoner = new JedisClusterSummoner(new ClusterConnectionProvider(hostAndPortSet, DefaultJedisClientConfig.builder().user(redisUsername).password(redisPassword).ssl(useSSL).socketTimeoutMillis(5000).timeoutMillis(10000).build(), poolConfig)); summoner =
new JedisClusterSummoner(
new ClusterConnectionProvider(
hostAndPortSet,
DefaultJedisClientConfig.builder()
.user(redisUsername)
.password(redisPassword)
.ssl(useSSL)
.socketTimeoutMillis(5000)
.timeoutMillis(10000)
.build(),
poolConfig));
redisBungeeMode = RedisBungeeMode.CLUSTER; redisBungeeMode = RedisBungeeMode.CLUSTER;
} else { } else {
plugin.logInfo("RedisBungee MODE: SINGLE"); plugin.logInfo("RedisBungee MODE: SINGLE");
@ -188,20 +266,33 @@ public interface ConfigLoader extends GenericConfigLoader {
JedisPoolConfig config = new JedisPoolConfig(); JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(node.getNode("compatibility-max-connections").getInt(3)); config.setMaxTotal(node.getNode("compatibility-max-connections").getInt(3));
config.setBlockWhenExhausted(true); config.setBlockWhenExhausted(true);
jedisPool = new JedisPool(config, redisServer, redisPort, 5000, redisUsername, redisPassword, useSSL); jedisPool =
new JedisPool(
config, redisServer, redisPort, 5000, redisUsername, redisPassword, useSSL);
plugin.logInfo("Compatibility JedisPool was created"); plugin.logInfo("Compatibility JedisPool was created");
} }
GenericObjectPoolConfig<Connection> poolConfig = new GenericObjectPoolConfig<>(); GenericObjectPoolConfig<Connection> poolConfig = new GenericObjectPoolConfig<>();
poolConfig.setMaxTotal(maxConnections); poolConfig.setMaxTotal(maxConnections);
poolConfig.setBlockWhenExhausted(true); poolConfig.setBlockWhenExhausted(true);
summoner = new JedisPooledSummoner(new PooledConnectionProvider(new ConnectionFactory(new HostAndPort(redisServer, redisPort), DefaultJedisClientConfig.builder().user(redisUsername).timeoutMillis(5000).ssl(useSSL).password(redisPassword).build()), poolConfig), jedisPool); summoner =
new JedisPooledSummoner(
new PooledConnectionProvider(
new ConnectionFactory(
new HostAndPort(redisServer, redisPort),
DefaultJedisClientConfig.builder()
.user(redisUsername)
.timeoutMillis(5000)
.ssl(useSSL)
.password(redisPassword)
.build()),
poolConfig),
jedisPool);
redisBungeeMode = RedisBungeeMode.SINGLE; redisBungeeMode = RedisBungeeMode.SINGLE;
} }
plugin.logInfo("Successfully connected to Redis."); plugin.logInfo("Successfully connected to Redis.");
onConfigLoad(configuration, summoner, redisBungeeMode); onConfigLoad(configuration, summoner, redisBungeeMode);
} }
void onConfigLoad(RedisBungeeConfiguration configuration, Summoner<?> summoner, RedisBungeeMode mode); void onConfigLoad(
RedisBungeeConfiguration configuration, Summoner<?> summoner, RedisBungeeMode mode);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,25 +7,23 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.config.loaders; package com.imaginarycode.minecraft.redisbungee.api.config.loaders;
import org.jetbrains.annotations.Nullable;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.time.Instant; import java.time.Instant;
import org.jetbrains.annotations.Nullable;
public interface GenericConfigLoader { public interface GenericConfigLoader {
// CHANGES on every reboot // CHANGES on every reboot
String RANDOM_OLD = "backup-" + Instant.now().getEpochSecond(); String RANDOM_OLD = "backup-" + Instant.now().getEpochSecond();
default Path createConfigFile(Path dataFolder, String configFile, @Nullable String defaultResourceID) throws IOException { default Path createConfigFile(
Path dataFolder, String configFile, @Nullable String defaultResourceID) throws IOException {
if (Files.notExists(dataFolder)) { if (Files.notExists(dataFolder)) {
Files.createDirectory(dataFolder); Files.createDirectory(dataFolder);
} }
@ -40,7 +38,8 @@ public interface GenericConfigLoader {
return file; return file;
} }
default void handleOldConfig(Path dataFolder, String configFile, @Nullable String defaultResourceID) throws IOException { default void handleOldConfig(
Path dataFolder, String configFile, @Nullable String defaultResourceID) throws IOException {
Path oldConfigFolder = dataFolder.resolve("old_config"); Path oldConfigFolder = dataFolder.resolve("old_config");
if (Files.notExists(oldConfigFolder)) { if (Files.notExists(oldConfigFolder)) {
Files.createDirectory(oldConfigFolder); Files.createDirectory(oldConfigFolder);
@ -54,5 +53,4 @@ public interface GenericConfigLoader {
Files.move(oldConfigPath, randomStoreConfigDirectory.resolve(configFile)); Files.move(oldConfigPath, randomStoreConfigDirectory.resolve(configFile));
createConfigFile(dataFolder, configFile, defaultResourceID); createConfigFile(dataFolder, configFile, defaultResourceID);
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,20 +7,21 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.events; package com.imaginarycode.minecraft.redisbungee.api.events;
import java.util.UUID; import java.util.UUID;
/** /**
* Since each platform have their own events' implementation for example Bungeecord events extends Event while velocity don't * Since each platform have their own events' implementation for example Bungeecord events extends
* Event while velocity don't
* *
* @author Ham1255 * @author Ham1255
* @since 0.7.0 * @since 0.7.0
*/ */
public interface EventsPlatform { public interface EventsPlatform {
IPlayerChangedServerNetworkEvent createPlayerChangedServerNetworkEvent(UUID uuid, String previousServer, String server); IPlayerChangedServerNetworkEvent createPlayerChangedServerNetworkEvent(
UUID uuid, String previousServer, String server);
IPlayerJoinedNetworkEvent createPlayerJoinedNetworkEvent(UUID uuid); IPlayerJoinedNetworkEvent createPlayerJoinedNetworkEvent(UUID uuid);
@ -29,5 +30,4 @@ public interface EventsPlatform {
IPubSubMessageEvent createPubSubEvent(String channel, String message); IPubSubMessageEvent createPubSubEvent(String channel, String message);
void fireEvent(Object event); void fireEvent(Object event);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.events; package com.imaginarycode.minecraft.redisbungee.api.events;
import java.util.UUID; import java.util.UUID;
@ -19,5 +18,4 @@ public interface IPlayerChangedServerNetworkEvent extends RedisBungeeEvent {
String getServer(); String getServer();
String getPreviousServer(); String getPreviousServer();
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.events; package com.imaginarycode.minecraft.redisbungee.api.events;
import java.util.UUID; import java.util.UUID;
@ -15,5 +14,4 @@ import java.util.UUID;
public interface IPlayerJoinedNetworkEvent extends RedisBungeeEvent { public interface IPlayerJoinedNetworkEvent extends RedisBungeeEvent {
UUID getUuid(); UUID getUuid();
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.events; package com.imaginarycode.minecraft.redisbungee.api.events;
import java.util.UUID; import java.util.UUID;
@ -15,5 +14,4 @@ import java.util.UUID;
public interface IPlayerLeftNetworkEvent extends RedisBungeeEvent { public interface IPlayerLeftNetworkEvent extends RedisBungeeEvent {
UUID getUuid(); UUID getUuid();
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.events; package com.imaginarycode.minecraft.redisbungee.api.events;
public interface IPubSubMessageEvent extends RedisBungeeEvent { public interface IPubSubMessageEvent extends RedisBungeeEvent {
@ -15,6 +14,4 @@ public interface IPubSubMessageEvent extends RedisBungeeEvent {
String getChannel(); String getChannel();
String getMessage(); String getMessage();
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,8 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.events; package com.imaginarycode.minecraft.redisbungee.api.events;
interface RedisBungeeEvent { interface RedisBungeeEvent {}
}

View File

@ -1,4 +1,12 @@
/*
* Copyright (c) 2026 RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.payloads; package com.imaginarycode.minecraft.redisbungee.api.payloads;
public abstract class AbstractPayload { public abstract class AbstractPayload {
@ -20,5 +28,4 @@ public abstract class AbstractPayload {
public String getClassName() { public String getClassName() {
return getClass().getName(); return getClass().getName();
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,26 +7,27 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.payloads.gson; package com.imaginarycode.minecraft.redisbungee.api.payloads.gson;
import com.google.gson.*; import com.google.gson.*;
import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload; import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload;
import java.lang.reflect.Type; import java.lang.reflect.Type;
public class AbstractPayloadSerializer implements JsonSerializer<AbstractPayload>, JsonDeserializer<AbstractPayload> { public class AbstractPayloadSerializer
implements JsonSerializer<AbstractPayload>, JsonDeserializer<AbstractPayload> {
@Override @Override
public AbstractPayload deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { public AbstractPayload deserialize(
JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject(); JsonObject jsonObject = json.getAsJsonObject();
return new AbstractPayload(jsonObject.get("proxy").getAsString(), jsonObject.get("class").getAsString()) { return new AbstractPayload(
}; jsonObject.get("proxy").getAsString(), jsonObject.get("class").getAsString()) {};
} }
@Override @Override
public JsonElement serialize(AbstractPayload src, Type typeOfSrc, JsonSerializationContext context) { public JsonElement serialize(
AbstractPayload src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject(); JsonObject jsonObject = new JsonObject();
jsonObject.add("proxy", new JsonPrimitive(src.senderProxy())); jsonObject.add("proxy", new JsonPrimitive(src.senderProxy()));
return jsonObject; return jsonObject;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy; package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy;
import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload; import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,16 +7,13 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy; package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy;
import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload; import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload;
public class HeartbeatPayload extends AbstractPayload { public class HeartbeatPayload extends AbstractPayload {
public record HeartbeatData(long heartbeat, int players) { public record HeartbeatData(long heartbeat, int players) {}
}
private final HeartbeatData data; private final HeartbeatData data;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy; package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy;
import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload; import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload;
@ -17,7 +16,6 @@ public class PubSubPayload extends AbstractPayload {
private final String channel; private final String channel;
private final String message; private final String message;
public PubSubPayload(String proxyId, String channel, String message) { public PubSubPayload(String proxyId, String channel, String message) {
super(proxyId); super(proxyId);
this.channel = channel; this.channel = channel;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,19 +7,16 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy; package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy;
import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload; import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload;
public class RunCommandPayload extends AbstractPayload { public class RunCommandPayload extends AbstractPayload {
private final String proxyToRun; private final String proxyToRun;
private final String command; private final String command;
public RunCommandPayload(String proxyId, String proxyToRun, String command) { public RunCommandPayload(String proxyId, String proxyToRun, String command) {
super(proxyId); super(proxyId);
this.proxyToRun = proxyToRun; this.proxyToRun = proxyToRun;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,21 +7,21 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson; package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson;
import com.google.gson.*; import com.google.gson.*;
import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.DeathPayload; import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.DeathPayload;
import java.lang.reflect.Type; import java.lang.reflect.Type;
public class DeathPayloadSerializer implements JsonSerializer<DeathPayload>, JsonDeserializer<DeathPayload> { public class DeathPayloadSerializer
implements JsonSerializer<DeathPayload>, JsonDeserializer<DeathPayload> {
private static final Gson gson = new Gson(); private static final Gson gson = new Gson();
@Override @Override
public DeathPayload deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { public DeathPayload deserialize(
JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject(); JsonObject jsonObject = json.getAsJsonObject();
String senderProxy = jsonObject.get("proxy").getAsString(); String senderProxy = jsonObject.get("proxy").getAsString();
return new DeathPayload(senderProxy); return new DeathPayload(senderProxy);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,28 +7,30 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson; package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson;
import com.google.gson.*; import com.google.gson.*;
import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.HeartbeatPayload; import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.HeartbeatPayload;
import java.lang.reflect.Type; import java.lang.reflect.Type;
public class HeartbeatPayloadSerializer implements JsonSerializer<HeartbeatPayload>, JsonDeserializer<HeartbeatPayload> { public class HeartbeatPayloadSerializer
implements JsonSerializer<HeartbeatPayload>, JsonDeserializer<HeartbeatPayload> {
@Override @Override
public HeartbeatPayload deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { public HeartbeatPayload deserialize(
JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject(); JsonObject jsonObject = json.getAsJsonObject();
String senderProxy = jsonObject.get("proxy").getAsString(); String senderProxy = jsonObject.get("proxy").getAsString();
long heartbeat = jsonObject.get("heartbeat").getAsLong(); long heartbeat = jsonObject.get("heartbeat").getAsLong();
int players = jsonObject.get("players").getAsInt(); int players = jsonObject.get("players").getAsInt();
return new HeartbeatPayload(senderProxy, new HeartbeatPayload.HeartbeatData(heartbeat, players)); return new HeartbeatPayload(
senderProxy, new HeartbeatPayload.HeartbeatData(heartbeat, players));
} }
@Override @Override
public JsonElement serialize(HeartbeatPayload src, Type typeOfSrc, JsonSerializationContext context) { public JsonElement serialize(
HeartbeatPayload src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject(); JsonObject jsonObject = new JsonObject();
jsonObject.add("proxy", new JsonPrimitive(src.senderProxy())); jsonObject.add("proxy", new JsonPrimitive(src.senderProxy()));
jsonObject.add("heartbeat", new JsonPrimitive(src.data().heartbeat())); jsonObject.add("heartbeat", new JsonPrimitive(src.data().heartbeat()));

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,21 +7,21 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson; package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson;
import com.google.gson.*; import com.google.gson.*;
import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.PubSubPayload; import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.PubSubPayload;
import java.lang.reflect.Type; import java.lang.reflect.Type;
public class PubSubPayloadSerializer implements JsonSerializer<PubSubPayload>, JsonDeserializer<PubSubPayload> { public class PubSubPayloadSerializer
implements JsonSerializer<PubSubPayload>, JsonDeserializer<PubSubPayload> {
private static final Gson gson = new Gson(); private static final Gson gson = new Gson();
@Override @Override
public PubSubPayload deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { public PubSubPayload deserialize(
JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject(); JsonObject jsonObject = json.getAsJsonObject();
String senderProxy = jsonObject.get("proxy").getAsString(); String senderProxy = jsonObject.get("proxy").getAsString();
String channel = jsonObject.get("channel").getAsString(); String channel = jsonObject.get("channel").getAsString();
@ -30,7 +30,8 @@ public class PubSubPayloadSerializer implements JsonSerializer<PubSubPayload>, J
} }
@Override @Override
public JsonElement serialize(PubSubPayload src, Type typeOfSrc, JsonSerializationContext context) { public JsonElement serialize(
PubSubPayload src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject(); JsonObject jsonObject = new JsonObject();
jsonObject.add("proxy", new JsonPrimitive(src.senderProxy())); jsonObject.add("proxy", new JsonPrimitive(src.senderProxy()));
jsonObject.add("channel", new JsonPrimitive(src.channel())); jsonObject.add("channel", new JsonPrimitive(src.channel()));

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,19 +7,19 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson; package com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.gson;
import com.google.gson.*; import com.google.gson.*;
import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.RunCommandPayload; import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.RunCommandPayload;
import java.lang.reflect.Type; import java.lang.reflect.Type;
public class RunCommandPayloadSerializer implements JsonSerializer<RunCommandPayload>, JsonDeserializer<RunCommandPayload> { public class RunCommandPayloadSerializer
implements JsonSerializer<RunCommandPayload>, JsonDeserializer<RunCommandPayload> {
@Override @Override
public RunCommandPayload deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { public RunCommandPayload deserialize(
JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject(); JsonObject jsonObject = json.getAsJsonObject();
String senderProxy = jsonObject.get("proxy").getAsString(); String senderProxy = jsonObject.get("proxy").getAsString();
String proxyToRun = jsonObject.get("proxy-to-run").getAsString(); String proxyToRun = jsonObject.get("proxy-to-run").getAsString();
@ -28,7 +28,8 @@ public class RunCommandPayloadSerializer implements JsonSerializer<RunCommandPay
} }
@Override @Override
public JsonElement serialize(RunCommandPayload src, Type typeOfSrc, JsonSerializationContext context) { public JsonElement serialize(
RunCommandPayload src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject(); JsonObject jsonObject = new JsonObject();
jsonObject.add("proxy", new JsonPrimitive(src.senderProxy())); jsonObject.add("proxy", new JsonPrimitive(src.senderProxy()));
jsonObject.add("proxy-to-run", new JsonPrimitive(src.proxyToRun())); jsonObject.add("proxy-to-run", new JsonPrimitive(src.proxyToRun()));

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,14 +7,12 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.summoners; package com.imaginarycode.minecraft.redisbungee.api.summoners;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.providers.ClusterConnectionProvider;
import java.io.IOException; import java.io.IOException;
import java.time.Duration; import java.time.Duration;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.providers.ClusterConnectionProvider;
public class JedisClusterSummoner implements Summoner<JedisCluster> { public class JedisClusterSummoner implements Summoner<JedisCluster> {
private final ClusterConnectionProvider clusterConnectionProvider; private final ClusterConnectionProvider clusterConnectionProvider;
@ -27,7 +25,6 @@ public class JedisClusterSummoner implements Summoner<JedisCluster> {
jedisCluster.del("random_data"); jedisCluster.del("random_data");
} }
@Override @Override
public void close() throws IOException { public void close() throws IOException {
this.clusterConnectionProvider.close(); this.clusterConnectionProvider.close();
@ -37,6 +34,4 @@ public class JedisClusterSummoner implements Summoner<JedisCluster> {
public JedisCluster obtainResource() { public JedisCluster obtainResource() {
return new NotClosableJedisCluster(this.clusterConnectionProvider, 60, Duration.ofSeconds(10)); return new NotClosableJedisCluster(this.clusterConnectionProvider, 60, Duration.ofSeconds(10));
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,16 +7,14 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.summoners; package com.imaginarycode.minecraft.redisbungee.api.summoners;
import java.io.IOException;
import redis.clients.jedis.Jedis; import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPooled; import redis.clients.jedis.JedisPooled;
import redis.clients.jedis.providers.PooledConnectionProvider; import redis.clients.jedis.providers.PooledConnectionProvider;
import java.io.IOException;
public class JedisPooledSummoner implements Summoner<JedisPooled> { public class JedisPooledSummoner implements Summoner<JedisPooled> {
private final PooledConnectionProvider connectionProvider; private final PooledConnectionProvider connectionProvider;
@ -31,12 +29,10 @@ public class JedisPooledSummoner implements Summoner<JedisPooled> {
// Test the connection to make sure configuration is right // Test the connection to make sure configuration is right
jedis.ping(); jedis.ping();
} }
} }
final JedisPooled jedisPooled = this.obtainResource(); final JedisPooled jedisPooled = this.obtainResource();
jedisPooled.set("random_data", "0"); jedisPooled.set("random_data", "0");
jedisPooled.del("random_data"); jedisPooled.del("random_data");
} }
@Override @Override
@ -55,6 +51,5 @@ public class JedisPooledSummoner implements Summoner<JedisPooled> {
this.jedisPool.close(); this.jedisPool.close();
} }
this.connectionProvider.close(); this.connectionProvider.close();
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,23 +7,19 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.summoners; package com.imaginarycode.minecraft.redisbungee.api.summoners;
import java.time.Duration;
import redis.clients.jedis.JedisCluster; import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.providers.ClusterConnectionProvider; import redis.clients.jedis.providers.ClusterConnectionProvider;
import java.time.Duration;
public class NotClosableJedisCluster extends JedisCluster { public class NotClosableJedisCluster extends JedisCluster {
NotClosableJedisCluster(ClusterConnectionProvider provider, int maxAttempts, Duration maxTotalRetriesDuration) { NotClosableJedisCluster(
ClusterConnectionProvider provider, int maxAttempts, Duration maxTotalRetriesDuration) {
super(provider, maxAttempts, maxTotalRetriesDuration); super(provider, maxAttempts, maxTotalRetriesDuration);
} }
@Override @Override
public void close() { public void close() {}
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,20 +7,16 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.summoners; package com.imaginarycode.minecraft.redisbungee.api.summoners;
import redis.clients.jedis.JedisPooled; import redis.clients.jedis.JedisPooled;
import redis.clients.jedis.providers.PooledConnectionProvider; import redis.clients.jedis.providers.PooledConnectionProvider;
public class NotClosableJedisPooled extends JedisPooled { public class NotClosableJedisPooled extends JedisPooled {
NotClosableJedisPooled(PooledConnectionProvider provider) { NotClosableJedisPooled(PooledConnectionProvider provider) {
super(provider); super(provider);
} }
@Override @Override
public void close() { public void close() {}
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,13 +7,10 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.summoners; package com.imaginarycode.minecraft.redisbungee.api.summoners;
import redis.clients.jedis.UnifiedJedis;
import java.io.Closeable; import java.io.Closeable;
import redis.clients.jedis.UnifiedJedis;
/** /**
* This class intended for future release to support redis sentinel or redis clusters * This class intended for future release to support redis sentinel or redis clusters
@ -24,5 +21,4 @@ import java.io.Closeable;
public interface Summoner<P extends UnifiedJedis> extends Closeable { public interface Summoner<P extends UnifiedJedis> extends Closeable {
P obtainResource(); P obtainResource();
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.tasks; package com.imaginarycode.minecraft.redisbungee.api.tasks;
import com.imaginarycode.minecraft.redisbungee.AbstractRedisBungeeAPI; import com.imaginarycode.minecraft.redisbungee.AbstractRedisBungeeAPI;
@ -16,7 +15,6 @@ import redis.clients.jedis.*;
public abstract class RedisPipelineTask<T> extends RedisTask<T> { public abstract class RedisPipelineTask<T> extends RedisTask<T> {
public RedisPipelineTask(AbstractRedisBungeeAPI api) { public RedisPipelineTask(AbstractRedisBungeeAPI api) {
super(api); super(api);
} }
@ -25,7 +23,6 @@ public abstract class RedisPipelineTask<T> extends RedisTask<T> {
super(plugin); super(plugin);
} }
@Override @Override
public T unifiedJedisTask(UnifiedJedis unifiedJedis) { public T unifiedJedisTask(UnifiedJedis unifiedJedis) {
if (unifiedJedis instanceof JedisPooled pooled) { if (unifiedJedis instanceof JedisPooled pooled) {
@ -44,6 +41,4 @@ public abstract class RedisPipelineTask<T> extends RedisTask<T> {
public abstract T doPooledPipeline(Pipeline pipeline); public abstract T doPooledPipeline(Pipeline pipeline);
public abstract T clusterPipeline(ClusterPipeline pipeline); public abstract T clusterPipeline(ClusterPipeline pipeline);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.tasks; package com.imaginarycode.minecraft.redisbungee.api.tasks;
import com.imaginarycode.minecraft.redisbungee.AbstractRedisBungeeAPI; import com.imaginarycode.minecraft.redisbungee.AbstractRedisBungeeAPI;
@ -16,13 +15,12 @@ import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.api.summoners.JedisClusterSummoner; import com.imaginarycode.minecraft.redisbungee.api.summoners.JedisClusterSummoner;
import com.imaginarycode.minecraft.redisbungee.api.summoners.JedisPooledSummoner; import com.imaginarycode.minecraft.redisbungee.api.summoners.JedisPooledSummoner;
import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner; import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner;
import java.util.concurrent.Callable;
import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.UnifiedJedis;
import java.util.concurrent.Callable;
/** /**
* Since Jedis now have UnifiedJedis which basically extended by cluster / single connections classes * Since Jedis now have UnifiedJedis which basically extended by cluster / single connections
* can help us to have shared code. * classes can help us to have shared code.
*/ */
public abstract class RedisTask<V> implements Runnable, Callable<V> { public abstract class RedisTask<V> implements Runnable, Callable<V> {
@ -53,7 +51,8 @@ public abstract class RedisTask<V> implements Runnable, Callable<V> {
} }
public V execute() { public V execute() {
// JedisCluster, JedisPooled in fact is just UnifiedJedis does not need new instance since its single instance anyway. // JedisCluster, JedisPooled in fact is just UnifiedJedis does not need new instance since its
// single instance anyway.
if (mode == RedisBungeeMode.SINGLE) { if (mode == RedisBungeeMode.SINGLE) {
JedisPooledSummoner jedisSummoner = (JedisPooledSummoner) summoner; JedisPooledSummoner jedisSummoner = (JedisPooledSummoner) summoner;
return this.unifiedJedisTask(jedisSummoner.obtainResource()); return this.unifiedJedisTask(jedisSummoner.obtainResource());
@ -63,5 +62,4 @@ public abstract class RedisTask<V> implements Runnable, Callable<V> {
} }
return null; return null;
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,18 +7,15 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.tasks; package com.imaginarycode.minecraft.redisbungee.api.tasks;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin; import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.api.util.uuid.CachedUUIDEntry; import com.imaginarycode.minecraft.redisbungee.api.util.uuid.CachedUUIDEntry;
import java.util.ArrayList;
import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.UnifiedJedis;
import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.exceptions.JedisException;
import java.util.ArrayList;
public class UUIDCleanupTask extends RedisTask<Void> { public class UUIDCleanupTask extends RedisTask<Void> {
private final Gson gson = new Gson(); private final Gson gson = new Gson();
@ -36,7 +33,10 @@ public class UUIDCleanupTask extends RedisTask<Void>{
final long number = unifiedJedis.hlen("uuid-cache"); final long number = unifiedJedis.hlen("uuid-cache");
plugin.logInfo("Found {} entries", number); plugin.logInfo("Found {} entries", number);
ArrayList<String> fieldsToRemove = new ArrayList<>(); ArrayList<String> fieldsToRemove = new ArrayList<>();
unifiedJedis.hgetAll("uuid-cache").forEach((field, data) -> { unifiedJedis
.hgetAll("uuid-cache")
.forEach(
(field, data) -> {
CachedUUIDEntry cachedUUIDEntry = gson.fromJson(data, CachedUUIDEntry.class); CachedUUIDEntry cachedUUIDEntry = gson.fromJson(data, CachedUUIDEntry.class);
if (cachedUUIDEntry.expired()) { if (cachedUUIDEntry.expired()) {
fieldsToRemove.add(field); fieldsToRemove.add(field);
@ -51,6 +51,4 @@ public class UUIDCleanupTask extends RedisTask<Void>{
} }
return null; return null;
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.util; package com.imaginarycode.minecraft.redisbungee.api.util;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin; import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
@ -15,7 +14,6 @@ import com.imaginarycode.minecraft.redisbungee.api.tasks.RedisTask;
import redis.clients.jedis.Protocol; import redis.clients.jedis.Protocol;
import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.UnifiedJedis;
public class InitialUtils { public class InitialUtils {
public static void checkRedisVersion(RedisBungeePlugin<?> plugin) { public static void checkRedisVersion(RedisBungeePlugin<?> plugin) {
@ -29,12 +27,20 @@ public class InitialUtils {
String version = s.split(":")[1]; String version = s.split(":")[1];
plugin.logInfo("Redis server version: " + version); plugin.logInfo("Redis server version: " + version);
if (!RedisUtil.isRedisVersionRight(version)) { if (!RedisUtil.isRedisVersionRight(version)) {
plugin.logFatal("Your version of Redis (" + version + ") is not at least version " + RedisUtil.MAJOR_VERSION + "." + RedisUtil.MINOR_VERSION + " RedisBungee requires a newer version of Redis."); plugin.logFatal(
"Your version of Redis ("
+ version
+ ") is not at least version "
+ RedisUtil.MAJOR_VERSION
+ "."
+ RedisUtil.MINOR_VERSION
+ " RedisBungee requires a newer version of Redis.");
throw new RuntimeException("Unsupported Redis version detected"); throw new RuntimeException("Unsupported Redis version detected");
} }
long uuidCacheSize = unifiedJedis.hlen("uuid-cache"); long uuidCacheSize = unifiedJedis.hlen("uuid-cache");
if (uuidCacheSize > 750000) { if (uuidCacheSize > 750000) {
plugin.logInfo("Looks like you have a really big UUID cache! Run '/rb clean' to remove expired cache entries"); plugin.logInfo(
"Looks like you have a really big UUID cache! Run '/rb clean' to remove expired cache entries");
} }
break; break;
} }
@ -43,6 +49,4 @@ public class InitialUtils {
} }
}.execute(); }.execute();
} }
} }

View File

@ -1,10 +1,19 @@
/*
* Copyright (c) 2026 RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.util; package com.imaginarycode.minecraft.redisbungee.api.util;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
@VisibleForTesting @VisibleForTesting
public class RedisUtil { public class RedisUtil {
public final static int PROXY_TIMEOUT = 30; public static final int PROXY_TIMEOUT = 30;
public static final int MAJOR_VERSION = 6; public static final int MAJOR_VERSION = 6;
public static final int MINOR_VERSION = 2; public static final int MINOR_VERSION = 2;
@ -19,7 +28,6 @@ public class RedisUtil {
if (major > MAJOR_VERSION) return true; if (major > MAJOR_VERSION) return true;
return major == MAJOR_VERSION && minor >= MINOR_VERSION; return major == MAJOR_VERSION && minor >= MINOR_VERSION;
} }
// Ham1255: i am keeping this if some plugin uses this *IF* // Ham1255: i am keeping this if some plugin uses this *IF*

View File

@ -1,9 +1,17 @@
/*
* Copyright (c) 2026 RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.api.util.serialize; package com.imaginarycode.minecraft.redisbungee.api.util.serialize;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.google.common.collect.Multiset; import com.google.common.collect.Multiset;
import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteArrayDataOutput;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
@ -18,7 +26,8 @@ public class MultiMapSerialization {
} }
@SuppressWarnings("SameParameterValue") @SuppressWarnings("SameParameterValue")
public static void serializeMultimap(Multimap<String, String> collection, boolean includeNames, ByteArrayDataOutput output) { public static void serializeMultimap(
Multimap<String, String> collection, boolean includeNames, ByteArrayDataOutput output) {
output.writeInt(collection.keySet().size()); output.writeInt(collection.keySet().size());
for (Map.Entry<String, Collection<String>> entry : collection.asMap().entrySet()) { for (Map.Entry<String, Collection<String>> entry : collection.asMap().entrySet()) {
output.writeUTF(entry.getKey()); output.writeUTF(entry.getKey());
@ -36,5 +45,4 @@ public class MultiMapSerialization {
output.writeUTF(o.toString()); output.writeUTF(o.toString());
} }
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.util.uuid; package com.imaginarycode.minecraft.redisbungee.api.util.uuid;
import java.util.Calendar; import java.util.Calendar;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,19 +7,17 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.util.uuid; package com.imaginarycode.minecraft.redisbungee.api.util.uuid;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.ResponseBody;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.ResponseBody;
public class NameFetcher { public class NameFetcher {
private static final OkHttpClient httpClient = new OkHttpClient(); private static final OkHttpClient httpClient = new OkHttpClient();
@ -33,7 +31,8 @@ public class NameFetcher {
public static String getName(UUID uuid) throws IOException { public static String getName(UUID uuid) throws IOException {
String url = "https://playerdb.co/api/player/minecraft/" + uuid.toString(); String url = "https://playerdb.co/api/player/minecraft/" + uuid.toString();
Request request = new Request.Builder() Request request =
new Request.Builder()
.addHeader("User-Agent", "RedisBungee-ProxioDev") .addHeader("User-Agent", "RedisBungee-ProxioDev")
.url(url) .url(url)
.get() .get()

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,18 +7,16 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.util.uuid; package com.imaginarycode.minecraft.redisbungee.api.util.uuid;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.gson.Gson; import com.google.gson.Gson;
import okhttp3.*;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import okhttp3.*;
/* Credits to evilmidget38 for this class. I modified it to use Gson. */ /* Credits to evilmidget38 for this class. I modified it to use Gson. */
public class UUIDFetcher implements Callable<Map<String, UUID>> { public class UUIDFetcher implements Callable<Map<String, UUID>> {
@ -40,7 +38,16 @@ public class UUIDFetcher implements Callable<Map<String, UUID>> {
} }
public static UUID getUUID(String id) { public static UUID getUUID(String id) {
return UUID.fromString(id.substring(0, 8) + "-" + id.substring(8, 12) + "-" + id.substring(12, 16) + "-" + id.substring(16, 20) + "-" + id.substring(20, 32)); return UUID.fromString(
id.substring(0, 8)
+ "-"
+ id.substring(8, 12)
+ "-"
+ id.substring(12, 16)
+ "-"
+ id.substring(16, 20)
+ "-"
+ id.substring(20, 32));
} }
public Map<String, UUID> call() throws Exception { public Map<String, UUID> call() throws Exception {
@ -48,7 +55,8 @@ public class UUIDFetcher implements Callable<Map<String, UUID>> {
int requests = (int) Math.ceil(names.size() / PROFILES_PER_REQUEST); int requests = (int) Math.ceil(names.size() / PROFILES_PER_REQUEST);
for (int i = 0; i < requests; i++) { for (int i = 0; i < requests; i++) {
String body = gson.toJson(names.subList(i * 100, Math.min((i + 1) * 100, names.size()))); String body = gson.toJson(names.subList(i * 100, Math.min((i + 1) * 100, names.size())));
Request request = new Request.Builder().url(PROFILE_URL).post(RequestBody.create(JSON, body)).build(); Request request =
new Request.Builder().url(PROFILE_URL).post(RequestBody.create(JSON, body)).build();
ResponseBody responseBody = httpClient.newCall(request).execute().body(); ResponseBody responseBody = httpClient.newCall(request).execute().body();
String response = responseBody.string(); String response = responseBody.string();
responseBody.close(); responseBody.close();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.api.util.uuid; package com.imaginarycode.minecraft.redisbungee.api.util.uuid;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
@ -15,19 +14,20 @@ import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin; import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.api.tasks.RedisTask; import com.imaginarycode.minecraft.redisbungee.api.tasks.RedisTask;
import org.checkerframework.checker.nullness.qual.NonNull;
import redis.clients.jedis.UnifiedJedis;
import redis.clients.jedis.exceptions.JedisException;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.checkerframework.checker.nullness.qual.NonNull;
import redis.clients.jedis.UnifiedJedis;
import redis.clients.jedis.exceptions.JedisException;
public final class UUIDTranslator { public final class UUIDTranslator {
private static final Pattern UUID_PATTERN = Pattern.compile("[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"); private static final Pattern UUID_PATTERN =
Pattern.compile(
"[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}");
private static final Pattern MOJANGIAN_UUID_PATTERN = Pattern.compile("[a-fA-F0-9]{32}"); private static final Pattern MOJANGIAN_UUID_PATTERN = Pattern.compile("[a-fA-F0-9]{32}");
private final RedisBungeePlugin<?> plugin; private final RedisBungeePlugin<?> plugin;
private final Map<String, CachedUUIDEntry> nameToUuidMap = new ConcurrentHashMap<>(128, 0.5f, 4); private final Map<String, CachedUUIDEntry> nameToUuidMap = new ConcurrentHashMap<>(128, 0.5f, 4);
@ -54,16 +54,13 @@ public final class UUIDTranslator {
public UUID getTranslatedUuid(@NonNull String player, boolean expensiveLookups) { public UUID getTranslatedUuid(@NonNull String player, boolean expensiveLookups) {
// If the player is online, give them their UUID. // If the player is online, give them their UUID.
// Remember, local data > remote data. // Remember, local data > remote data.
if (plugin.getPlayer(player) != null) if (plugin.getPlayer(player) != null) return plugin.getPlayerUUID(player);
return plugin.getPlayerUUID(player);
// Check if it exists in the map // Check if it exists in the map
CachedUUIDEntry cachedUUIDEntry = nameToUuidMap.get(player.toLowerCase()); CachedUUIDEntry cachedUUIDEntry = nameToUuidMap.get(player.toLowerCase());
if (cachedUUIDEntry != null) { if (cachedUUIDEntry != null) {
if (!cachedUUIDEntry.expired()) if (!cachedUUIDEntry.expired()) return cachedUUIDEntry.uuid();
return cachedUUIDEntry.uuid(); else nameToUuidMap.remove(player);
else
nameToUuidMap.remove(player);
} }
// Check if we can exit early // Check if we can exit early
@ -81,7 +78,8 @@ public final class UUIDTranslator {
if (!plugin.isOnlineMode()) { if (!plugin.isOnlineMode()) {
return UUID.nameUUIDFromBytes(("OfflinePlayer:" + player).getBytes(Charsets.UTF_8)); return UUID.nameUUIDFromBytes(("OfflinePlayer:" + player).getBytes(Charsets.UTF_8));
} }
RedisTask<UUID> redisTask = new RedisTask<UUID>(plugin) { RedisTask<UUID> redisTask =
new RedisTask<UUID>(plugin) {
@Override @Override
public UUID unifiedJedisTask(UnifiedJedis unifiedJedis) { public UUID unifiedJedisTask(UnifiedJedis unifiedJedis) {
String stored = unifiedJedis.hget("uuid-cache", player.toLowerCase()); String stored = unifiedJedis.hget("uuid-cache", player.toLowerCase());
@ -102,8 +100,7 @@ public final class UUIDTranslator {
} }
// That didn't work. Let's ask Mojang. // That didn't work. Let's ask Mojang.
if (!expensiveLookups || !plugin.isOnlineMode()) if (!expensiveLookups || !plugin.isOnlineMode()) return null;
return null;
Map<String, UUID> uuidMap1; Map<String, UUID> uuidMap1;
try { try {
@ -134,19 +131,17 @@ public final class UUIDTranslator {
public String getNameFromUuid(@NonNull UUID player, boolean expensiveLookups) { public String getNameFromUuid(@NonNull UUID player, boolean expensiveLookups) {
// If the player is online, give them their UUID. // If the player is online, give them their UUID.
// Remember, local data > remote data. // Remember, local data > remote data.
if (plugin.getPlayer(player) != null) if (plugin.getPlayer(player) != null) return plugin.getPlayerName(player);
return plugin.getPlayerName(player);
// Check if it exists in the map // Check if it exists in the map
CachedUUIDEntry cachedUUIDEntry = uuidToNameMap.get(player); CachedUUIDEntry cachedUUIDEntry = uuidToNameMap.get(player);
if (cachedUUIDEntry != null) { if (cachedUUIDEntry != null) {
if (!cachedUUIDEntry.expired()) if (!cachedUUIDEntry.expired()) return cachedUUIDEntry.name();
return cachedUUIDEntry.name(); else uuidToNameMap.remove(player);
else
uuidToNameMap.remove(player);
} }
RedisTask<String> redisTask = new RedisTask<String>(plugin) { RedisTask<String> redisTask =
new RedisTask<String>(plugin) {
@Override @Override
public String unifiedJedisTask(UnifiedJedis unifiedJedis) { public String unifiedJedisTask(UnifiedJedis unifiedJedis) {
String stored = unifiedJedis.hget("uuid-cache", player.toString()); String stored = unifiedJedis.hget("uuid-cache", player.toString());
@ -158,7 +153,8 @@ public final class UUIDTranslator {
if (entry.expired()) { if (entry.expired()) {
unifiedJedis.hdel("uuid-cache", player.toString()); unifiedJedis.hdel("uuid-cache", player.toString());
// Doesn't hurt to also remove the named entry as well. // Doesn't hurt to also remove the named entry as well.
// TODO: Since UUIDs are fixed, we could look up the name and see if the UUID matches. // TODO: Since UUIDs are fixed, we could look up the name and see if the UUID
// matches.
unifiedJedis.hdel("uuid-cache", entry.name()); unifiedJedis.hdel("uuid-cache", entry.name());
} else { } else {
nameToUuidMap.put(entry.name().toLowerCase(), entry); nameToUuidMap.put(entry.name().toLowerCase(), entry);
@ -167,8 +163,7 @@ public final class UUIDTranslator {
} }
} }
if (!expensiveLookups || !plugin.isOnlineMode()) if (!expensiveLookups || !plugin.isOnlineMode()) return null;
return null;
// That didn't work. Let's ask PlayerDB. // That didn't work. Let's ask PlayerDB.
String name; String name;
@ -188,7 +183,6 @@ public final class UUIDTranslator {
} }
}; };
// Okay, it wasn't locally cached. Let's try Redis. // Okay, it wasn't locally cached. Let's try Redis.
try { try {
return redisTask.execute(); return redisTask.execute();
@ -201,8 +195,7 @@ public final class UUIDTranslator {
public void persistInfo(String name, UUID uuid, UnifiedJedis unifiedJedis) { public void persistInfo(String name, UUID uuid, UnifiedJedis unifiedJedis) {
addToMaps(name, uuid); addToMaps(name, uuid);
String json = gson.toJson(uuidToNameMap.get(uuid)); String json = gson.toJson(uuidToNameMap.get(uuid));
unifiedJedis.hset("uuid-cache", ImmutableMap.of(name.toLowerCase(), json, uuid.toString(), json)); unifiedJedis.hset(
"uuid-cache", ImmutableMap.of(name.toLowerCase(), json, uuid.toString(), json));
} }
} }

View File

@ -2,6 +2,9 @@ plugins {
`java-library` `java-library`
} }
version = property("redisbungee-version").toString()
group = property("redisbungee-group").toString()
dependencies { dependencies {
compileOnly(project(":RedisBungee-API")) compileOnly(project(":RedisBungee-API"))
implementation(libs.acf.core) implementation(libs.acf.core)

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,21 +7,19 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.commands; package com.imaginarycode.minecraft.redisbungee.commands;
import co.aikar.commands.CommandContexts; import co.aikar.commands.CommandContexts;
import co.aikar.commands.CommandManager; import co.aikar.commands.CommandManager;
import co.aikar.commands.InvalidCommandArgument; import co.aikar.commands.InvalidCommandArgument;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin; import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.commands.legacy.LegacyRedisBungeeCommands; import com.imaginarycode.minecraft.redisbungee.commands.legacy.LegacyRedisBungeeCommands;
import java.util.UUID; import java.util.UUID;
public class CommandLoader { public class CommandLoader {
public static void initCommands(CommandManager<?, ?, ?, ?, ?, ?> commandManager, RedisBungeePlugin<?> plugin) { public static void initCommands(
CommandManager<?, ?, ?, ?, ?, ?> commandManager, RedisBungeePlugin<?> plugin) {
registerContexts(commandManager); registerContexts(commandManager);
var commandsConfiguration = plugin.configuration().commandsConfiguration(); var commandsConfiguration = plugin.configuration().commandsConfiguration();
if (commandsConfiguration.redisbungeeEnabled()) { if (commandsConfiguration.redisbungeeEnabled()) {
@ -32,12 +30,13 @@ public class CommandLoader {
} }
commandManager.registerCommand(new CommandRedisBungeeDebug(plugin)); commandManager.registerCommand(new CommandRedisBungeeDebug(plugin));
} }
private static void registerContexts(CommandManager<?, ?, ?, ?, ?, ?> commandManager) { private static void registerContexts(CommandManager<?, ?, ?, ?, ?, ?> commandManager) {
CommandContexts<?> commandContexts = commandManager.getCommandContexts(); CommandContexts<?> commandContexts = commandManager.getCommandContexts();
commandContexts.registerContext(UUID.class, c -> { commandContexts.registerContext(
UUID.class,
c -> {
String uuidString = c.popFirstArg(); String uuidString = c.popFirstArg();
try { try {
return UUID.fromString(uuidString); return UUID.fromString(uuidString);
@ -46,5 +45,4 @@ public class CommandLoader {
} }
}); });
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,17 +7,18 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.commands; package com.imaginarycode.minecraft.redisbungee.commands;
import co.aikar.commands.CommandIssuer; import co.aikar.commands.CommandIssuer;
import co.aikar.commands.RegisteredCommand;
import co.aikar.commands.annotation.*; import co.aikar.commands.annotation.*;
import com.google.common.primitives.Ints; import com.google.common.primitives.Ints;
import com.imaginarycode.minecraft.redisbungee.Constants; import com.imaginarycode.minecraft.redisbungee.Constants;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin; import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand; import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
import com.imaginarycode.minecraft.redisbungee.commands.utils.StopperUUIDCleanupTask; import com.imaginarycode.minecraft.redisbungee.commands.utils.StopperUUIDCleanupTask;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.event.ClickEvent;
@ -26,10 +27,6 @@ import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@CommandAlias("rb|redisbungee") @CommandAlias("rb|redisbungee")
@CommandPermission("redisbungee.command.use") @CommandPermission("redisbungee.command.use")
@Description("Main command") @Description("Main command")
@ -45,7 +42,8 @@ public class CommandRedisBungee extends AdventureBaseCommand {
@Subcommand("info|version|git") @Subcommand("info|version|git")
@Description("information about current redisbungee build") @Description("information about current redisbungee build")
public void info(CommandIssuer issuer) { public void info(CommandIssuer issuer) {
final String message = """ final String message =
"""
<color:aqua>This proxy is running RedisBungee Limework's fork <color:aqua>This proxy is running RedisBungee Limework's fork
<color:gold>======================================== <color:gold>========================================
<color:aqua>RedisBungee version: <color:green><version> <color:aqua>RedisBungee version: <color:green><version>
@ -61,10 +59,15 @@ public class CommandRedisBungee extends AdventureBaseCommand {
Placeholder.component( Placeholder.component(
"commit", "commit",
Component.text(Constants.GIT_COMMIT.substring(0, 8)) Component.text(Constants.GIT_COMMIT.substring(0, 8))
.clickEvent(ClickEvent.clickEvent(ClickEvent.Action.OPEN_URL, Constants.getGithubCommitLink())) .clickEvent(
.hoverEvent(HoverEvent.showText(Component.text("Click me to open: " + Constants.getGithubCommitLink()))) ClickEvent.clickEvent(
))); ClickEvent.Action.OPEN_URL, Constants.getGithubCommitLink()))
.hoverEvent(
HoverEvent.showText(
Component.text(
"Click me to open: " + Constants.getGithubCommitLink()))))));
} }
// <color:aqua>......: <color:green>...... // <color:aqua>......: <color:green>......
@HelpCommand @HelpCommand
@Description("shows the help page") @Description("shows the help page")
@ -75,12 +78,22 @@ public class CommandRedisBungee extends AdventureBaseCommand {
TextComponent.Builder message = Component.text(); TextComponent.Builder message = Component.text();
message.append(MiniMessage.miniMessage().deserialize(barFormat)); message.append(MiniMessage.miniMessage().deserialize(barFormat));
getSubCommands().forEach((subCommand, registeredCommand) -> { getSubCommands()
.forEach(
(subCommand, registeredCommand) -> {
String[] split = registeredCommand.getCommand().split(" "); String[] split = registeredCommand.getCommand().split(" ");
if (split.length > 1 && subCommand.equalsIgnoreCase(split[1])) { if (split.length > 1 && subCommand.equalsIgnoreCase(split[1])) {
message.appendNewline().append(MiniMessage.miniMessage().deserialize(commandFormat, Placeholder.component("sub-command", Component.text(subCommand)), message
Placeholder.component("description", MiniMessage.miniMessage().deserialize(registeredCommand.getHelpText())) .appendNewline()
)); .append(
MiniMessage.miniMessage()
.deserialize(
commandFormat,
Placeholder.component("sub-command", Component.text(subCommand)),
Placeholder.component(
"description",
MiniMessage.miniMessage()
.deserialize(registeredCommand.getHelpText()))));
} }
}); });
@ -88,31 +101,37 @@ public class CommandRedisBungee extends AdventureBaseCommand {
sendMessage(issuer, message.build()); sendMessage(issuer, message.build());
} }
@Subcommand("clean") @Subcommand("clean")
@Description("cleans up the uuid cache<color:red> <bold>WARNING...</bold> <color:white>command above could cause performance issues") @Description(
"cleans up the uuid cache<color:red> <bold>WARNING...</bold> <color:white>command above could cause performance issues")
@Private @Private
public void cleanUp(CommandIssuer issuer) { public void cleanUp(CommandIssuer issuer) {
if (StopperUUIDCleanupTask.isRunning) { if (StopperUUIDCleanupTask.isRunning) {
sendMessage(issuer, sendMessage(
Component.text("cleanup is currently running!").color(NamedTextColor.RED)); issuer, Component.text("cleanup is currently running!").color(NamedTextColor.RED));
return; return;
} }
sendMessage(issuer, sendMessage(
Component.text("cleanup is Starting, you should see the output status in the proxy console").color(NamedTextColor.GOLD)); issuer,
Component.text("cleanup is Starting, you should see the output status in the proxy console")
.color(NamedTextColor.GOLD));
plugin.executeAsync(new StopperUUIDCleanupTask(plugin)); plugin.executeAsync(new StopperUUIDCleanupTask(plugin));
} }
private List<Map.Entry<String, Integer>> subListProxies(
List<Map.Entry<String, Integer>> data, final int currentPage, final int pageSize) {
private List<Map.Entry<String, Integer>> subListProxies(List<Map.Entry<String, Integer>> data, final int currentPage, final int pageSize) { return data.subList(
return data.subList(((currentPage * pageSize) - pageSize), Ints.constrainToRange(currentPage * pageSize, 0, data.size())); ((currentPage * pageSize) - pageSize),
Ints.constrainToRange(currentPage * pageSize, 0, data.size()));
} }
@Subcommand("show") @Subcommand("show")
@Description("Shows proxies in this network") @Description("Shows proxies in this network")
public void showProxies(CommandIssuer issuer, String[] args) { public void showProxies(CommandIssuer issuer, String[] args) {
final String closer = "<color:gold>========================================"; final String closer = "<color:gold>========================================";
final String pageTop = "<color:yellow>Page: <color:green><current>/<max> <color:yellow>Network ID: <color:green><network> <color:yellow>Proxies online: <color:green><proxies>"; final String pageTop =
"<color:yellow>Page: <color:green><current>/<max> <color:yellow>Network ID: <color:green><network> <color:yellow>Proxies online: <color:green><proxies>";
final String proxy = "<color:yellow><proxy><here> : <color:green><players> online"; final String proxy = "<color:yellow><proxy><here> : <color:green><players> online";
final String proxyHere = " (#) "; final String proxyHere = " (#) ";
final String nextPage = ">>>>>"; final String nextPage = ">>>>>";
@ -146,23 +165,33 @@ public class CommandRedisBungee extends AdventureBaseCommand {
var subList = subListProxies(data, currentPage, pageSize); var subList = subListProxies(data, currentPage, pageSize);
TextComponent.Builder builder = Component.text(); TextComponent.Builder builder = Component.text();
builder.append(MiniMessage.miniMessage().deserialize(closer)).appendNewline(); builder.append(MiniMessage.miniMessage().deserialize(closer)).appendNewline();
builder.append(MiniMessage.miniMessage().deserialize(pageTop, builder
.append(
MiniMessage.miniMessage()
.deserialize(
pageTop,
Placeholder.component("current", Component.text(currentPage)), Placeholder.component("current", Component.text(currentPage)),
Placeholder.component("max", Component.text(maxPages)), Placeholder.component("max", Component.text(maxPages)),
Placeholder.component("network", Component.text(plugin.proxyDataManager().networkId())), Placeholder.component(
Placeholder.component("proxies", Component.text(data.size())) "network", Component.text(plugin.proxyDataManager().networkId())),
Placeholder.component("proxies", Component.text(data.size()))))
.appendNewline();
)).appendNewline();
int left = pageSize; int left = pageSize;
for (Map.Entry<String, Integer> entrySet : subList) { for (Map.Entry<String, Integer> entrySet : subList) {
builder.append(MiniMessage.miniMessage().deserialize(proxy, builder
.append(
MiniMessage.miniMessage()
.deserialize(
proxy,
Placeholder.component("proxy", Component.text(entrySet.getKey())), Placeholder.component("proxy", Component.text(entrySet.getKey())),
Placeholder.component("here", Component.text(plugin.proxyDataManager().proxyId().equals(entrySet.getKey()) ? proxyHere : "")), Placeholder.component(
Placeholder.component("players", Component.text(entrySet.getValue())) "here",
Component.text(
)).appendNewline(); plugin.proxyDataManager().proxyId().equals(entrySet.getKey())
? proxyHere
: "")),
Placeholder.component("players", Component.text(entrySet.getValue()))))
.appendNewline();
left--; left--;
} }
while (left > 0) { while (left > 0) {
@ -170,20 +199,26 @@ public class CommandRedisBungee extends AdventureBaseCommand {
left--; left--;
} }
if (currentPage > 1) { if (currentPage > 1) {
builder.append(MiniMessage.miniMessage().deserialize(previousPage) builder.append(
.color(NamedTextColor.WHITE).clickEvent(ClickEvent.runCommand("/rb show " + (currentPage - 1)))); MiniMessage.miniMessage()
.deserialize(previousPage)
.color(NamedTextColor.WHITE)
.clickEvent(ClickEvent.runCommand("/rb show " + (currentPage - 1))));
} else { } else {
builder.append(MiniMessage.miniMessage().deserialize(previousPage).color(NamedTextColor.GRAY)); builder.append(
MiniMessage.miniMessage().deserialize(previousPage).color(NamedTextColor.GRAY));
} }
if (subList.size() == pageSize && !subListProxies(data, currentPage + 1, pageSize).isEmpty()) { if (subList.size() == pageSize && !subListProxies(data, currentPage + 1, pageSize).isEmpty()) {
builder.append(MiniMessage.miniMessage().deserialize(nextPage) builder.append(
.color(NamedTextColor.WHITE).clickEvent(ClickEvent.runCommand("/rb show " + (currentPage + 1)))); MiniMessage.miniMessage()
.deserialize(nextPage)
.color(NamedTextColor.WHITE)
.clickEvent(ClickEvent.runCommand("/rb show " + (currentPage + 1))));
} else { } else {
builder.append(MiniMessage.miniMessage().deserialize(nextPage).color(NamedTextColor.GRAY)); builder.append(MiniMessage.miniMessage().deserialize(nextPage).color(NamedTextColor.GRAY));
} }
builder.appendNewline(); builder.appendNewline();
builder.append(MiniMessage.miniMessage().deserialize(closer)); builder.append(MiniMessage.miniMessage().deserialize(closer));
sendMessage(issuer, builder.build()); sendMessage(issuer, builder.build());
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,14 +7,12 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.commands; package com.imaginarycode.minecraft.redisbungee.commands;
import co.aikar.commands.CommandIssuer; import co.aikar.commands.CommandIssuer;
import co.aikar.commands.annotation.*; import co.aikar.commands.annotation.*;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin; import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand; import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
import java.util.UUID; import java.util.UUID;
@CommandAlias("rbd|redisbungeedebug") @CommandAlias("rbd|redisbungeedebug")
@ -32,15 +30,19 @@ public class CommandRedisBungeeDebug extends AdventureBaseCommand {
@Description("kicks a player from the network by name") @Description("kicks a player from the network by name")
@Private @Private
public void kick(CommandIssuer issuer, String playerName) { public void kick(CommandIssuer issuer, String playerName) {
plugin.playerDataManager().serializedPlayerKick(plugin.getUuidTranslator().getTranslatedUuid(playerName, false), "kicked using redisbungee api using name"); plugin
.playerDataManager()
.serializedPlayerKick(
plugin.getUuidTranslator().getTranslatedUuid(playerName, false),
"kicked using redisbungee api using name");
} }
@Subcommand("kickByUUID") @Subcommand("kickByUUID")
@Description("kicks a player from the network by UUID") @Description("kicks a player from the network by UUID")
@Private @Private
public void kick(CommandIssuer issuer, UUID uuid) { public void kick(CommandIssuer issuer, UUID uuid) {
plugin.playerDataManager().serializedPlayerKick(uuid, "kicked using redisbungee api using uuid"); plugin
.playerDataManager()
.serializedPlayerKick(uuid, "kicked using redisbungee api using uuid");
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.commands.legacy; package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer; import co.aikar.commands.CommandIssuer;
@ -30,5 +29,4 @@ public class CommandFind extends AdventureBaseCommand {
public void find(CommandIssuer issuer, String[] args) { public void find(CommandIssuer issuer, String[] args) {
rootCommand.find(issuer, args); rootCommand.find(issuer, args);
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.commands.legacy; package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer; import co.aikar.commands.CommandIssuer;
@ -30,5 +29,4 @@ public class CommandGList extends AdventureBaseCommand {
public void gList(CommandIssuer issuer, String[] args) { public void gList(CommandIssuer issuer, String[] args) {
rootCommand.gList(issuer, args); rootCommand.gList(issuer, args);
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.commands.legacy; package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer; import co.aikar.commands.CommandIssuer;
@ -26,7 +25,6 @@ public class CommandIp extends AdventureBaseCommand {
this.rootCommand = rootCommand; this.rootCommand = rootCommand;
} }
@Default @Default
public void ip(CommandIssuer issuer, String[] args) { public void ip(CommandIssuer issuer, String[] args) {
this.rootCommand.ip(issuer, args); this.rootCommand.ip(issuer, args);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.commands.legacy; package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer; import co.aikar.commands.CommandIssuer;
@ -20,7 +19,6 @@ import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseComma
@CommandPermission("redisbungee.command.lastseen") @CommandPermission("redisbungee.command.lastseen")
public class CommandLastSeen extends AdventureBaseCommand { public class CommandLastSeen extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand; private final LegacyRedisBungeeCommands rootCommand;
public CommandLastSeen(LegacyRedisBungeeCommands rootCommand) { public CommandLastSeen(LegacyRedisBungeeCommands rootCommand) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.commands.legacy; package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer; import co.aikar.commands.CommandIssuer;
@ -29,5 +28,4 @@ public class CommandPProxy extends AdventureBaseCommand {
public void playerProxy(CommandIssuer issuer, String[] args) { public void playerProxy(CommandIssuer issuer, String[] args) {
this.rootCommand.playerProxy(issuer, args); this.rootCommand.playerProxy(issuer, args);
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.commands.legacy; package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer; import co.aikar.commands.CommandIssuer;
@ -20,7 +19,6 @@ import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseComma
@CommandPermission("redisbungee.command.plist") @CommandPermission("redisbungee.command.plist")
public class CommandPlist extends AdventureBaseCommand { public class CommandPlist extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand; private final LegacyRedisBungeeCommands rootCommand;
public CommandPlist(LegacyRedisBungeeCommands rootCommand) { public CommandPlist(LegacyRedisBungeeCommands rootCommand) {
@ -31,5 +29,4 @@ public class CommandPlist extends AdventureBaseCommand {
public void playerList(CommandIssuer issuer, String[] args) { public void playerList(CommandIssuer issuer, String[] args) {
this.rootCommand.playerList(issuer, args); this.rootCommand.playerList(issuer, args);
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.commands.legacy; package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer; import co.aikar.commands.CommandIssuer;
@ -20,12 +19,12 @@ import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseComma
@CommandPermission("redisbungee.command.sendtoall") @CommandPermission("redisbungee.command.sendtoall")
public class CommandSendToAll extends AdventureBaseCommand { public class CommandSendToAll extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand; private final LegacyRedisBungeeCommands rootCommand;
public CommandSendToAll(LegacyRedisBungeeCommands rootCommand) { public CommandSendToAll(LegacyRedisBungeeCommands rootCommand) {
this.rootCommand = rootCommand; this.rootCommand = rootCommand;
} }
@Default @Default
public void sendToAll(CommandIssuer issuer, String[] args) { public void sendToAll(CommandIssuer issuer, String[] args) {
this.rootCommand.sendToAll(issuer, args); this.rootCommand.sendToAll(issuer, args);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.commands.legacy; package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer; import co.aikar.commands.CommandIssuer;
@ -20,12 +19,12 @@ import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseComma
@CommandPermission("redisbungee.command.serverid") @CommandPermission("redisbungee.command.serverid")
public class CommandServerId extends AdventureBaseCommand { public class CommandServerId extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand; private final LegacyRedisBungeeCommands rootCommand;
public CommandServerId(LegacyRedisBungeeCommands rootCommand) { public CommandServerId(LegacyRedisBungeeCommands rootCommand) {
this.rootCommand = rootCommand; this.rootCommand = rootCommand;
} }
@Default @Default
public void serverId(CommandIssuer issuer) { public void serverId(CommandIssuer issuer) {
this.rootCommand.serverId(issuer); this.rootCommand.serverId(issuer);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.commands.legacy; package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer; import co.aikar.commands.CommandIssuer;
@ -20,7 +19,6 @@ import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseComma
@CommandPermission("redisbungee.command.serverids") @CommandPermission("redisbungee.command.serverids")
public class CommandServerIds extends AdventureBaseCommand { public class CommandServerIds extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand; private final LegacyRedisBungeeCommands rootCommand;
public CommandServerIds(LegacyRedisBungeeCommands rootCommand) { public CommandServerIds(LegacyRedisBungeeCommands rootCommand) {
@ -31,6 +29,4 @@ public class CommandServerIds extends AdventureBaseCommand {
public void serverIds(CommandIssuer issuer) { public void serverIds(CommandIssuer issuer) {
this.rootCommand.serverIds(issuer); this.rootCommand.serverIds(issuer);
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.commands.legacy; package com.imaginarycode.minecraft.redisbungee.commands.legacy;
import co.aikar.commands.CommandIssuer; import co.aikar.commands.CommandIssuer;
@ -20,15 +19,14 @@ import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin; import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand; import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor;
import java.net.InetAddress; import java.net.InetAddress;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.UUID; import java.util.UUID;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor;
@CommandAlias("rbl|redisbungeelegacy") @CommandAlias("rbl|redisbungeelegacy")
@CommandPermission("redisbungee.legacy.use") @CommandPermission("redisbungee.legacy.use")
@ -36,10 +34,12 @@ public class LegacyRedisBungeeCommands extends AdventureBaseCommand {
private final RedisBungeePlugin<?> plugin; private final RedisBungeePlugin<?> plugin;
public LegacyRedisBungeeCommands(CommandManager<?, ?, ?, ?, ?, ?> commandManager, RedisBungeePlugin<?> plugin) { public LegacyRedisBungeeCommands(
CommandManager<?, ?, ?, ?, ?, ?> commandManager, RedisBungeePlugin<?> plugin) {
this.plugin = plugin; this.plugin = plugin;
var commands = plugin.configuration().commandsConfiguration().legacySubCommandsConfiguration(); var commands = plugin.configuration().commandsConfiguration().legacySubCommandsConfiguration();
if (!plugin.configuration().commandsConfiguration().redisbungeeLegacyEnabled()) throw new IllegalStateException("someone tried to init me while disabled!"); if (!plugin.configuration().commandsConfiguration().redisbungeeLegacyEnabled())
throw new IllegalStateException("someone tried to init me while disabled!");
if (commands == null) throw new NullPointerException("commands config is null!!"); if (commands == null) throw new NullPointerException("commands config is null!!");
if (commands.installGlist()) commandManager.registerCommand(new CommandGList(this)); if (commands.installGlist()) commandManager.registerCommand(new CommandGList(this));
@ -67,36 +67,47 @@ public class LegacyRedisBungeeCommands extends AdventureBaseCommand {
@Subcommand("glist") @Subcommand("glist")
@CommandPermission("redisbungee.command.glist") @CommandPermission("redisbungee.command.glist")
public void gList(CommandIssuer issuer, String[] args) { public void gList(CommandIssuer issuer, String[] args) {
plugin.executeAsync(() -> { plugin.executeAsync(
() -> {
int count = plugin.getAbstractRedisBungeeApi().getPlayerCount(); int count = plugin.getAbstractRedisBungeeApi().getPlayerCount();
Component playersOnline = Component.text(playerPlural(count) + " currently online.", NamedTextColor.YELLOW); Component playersOnline =
Component.text(playerPlural(count) + " currently online.", NamedTextColor.YELLOW);
if (args.length > 0 && args[0].equals("showall")) { if (args.length > 0 && args[0].equals("showall")) {
Multimap<String, UUID> serverToPlayers = plugin.getAbstractRedisBungeeApi().getServerToPlayers(); Multimap<String, UUID> serverToPlayers =
plugin.getAbstractRedisBungeeApi().getServerToPlayers();
Multimap<String, String> human = HashMultimap.create(); Multimap<String, String> human = HashMultimap.create();
serverToPlayers.forEach((key, value) -> { serverToPlayers.forEach(
// if for any reason UUID translation fails just return the uuid as name, to make command finish executing. (key, value) -> {
// if for any reason UUID translation fails just return the uuid as name, to make
// command finish executing.
String playerName = plugin.getUuidTranslator().getNameFromUuid(value, false); String playerName = plugin.getUuidTranslator().getNameFromUuid(value, false);
human.put(key, playerName != null ? playerName : value.toString()); human.put(key, playerName != null ? playerName : value.toString());
}); });
for (String server : new TreeSet<>(serverToPlayers.keySet())) { for (String server : new TreeSet<>(serverToPlayers.keySet())) {
Component serverName = Component.text("[" + server + "] ", NamedTextColor.GREEN); Component serverName = Component.text("[" + server + "] ", NamedTextColor.GREEN);
Component serverCount = Component.text("(" + serverToPlayers.get(server).size() + "): ", NamedTextColor.YELLOW); Component serverCount =
Component serverPlayers = Component.text(Joiner.on(", ").join(human.get(server)), NamedTextColor.WHITE); Component.text(
"(" + serverToPlayers.get(server).size() + "): ", NamedTextColor.YELLOW);
Component serverPlayers =
Component.text(Joiner.on(", ").join(human.get(server)), NamedTextColor.WHITE);
sendMessage(issuer, Component.textOfChildren(serverName, serverCount, serverPlayers)); sendMessage(issuer, Component.textOfChildren(serverName, serverCount, serverPlayers));
} }
sendMessage(issuer, playersOnline); sendMessage(issuer, playersOnline);
} else { } else {
sendMessage(issuer, playersOnline); sendMessage(issuer, playersOnline);
sendMessage(issuer, Component.text("To see all players online, use /glist showall.", NamedTextColor.YELLOW)); sendMessage(
issuer,
Component.text(
"To see all players online, use /glist showall.", NamedTextColor.YELLOW));
} }
}); });
} }
@Subcommand("find") @Subcommand("find")
@CommandPermission("redisbungee.command.find") @CommandPermission("redisbungee.command.find")
public void find(CommandIssuer issuer, String[] args) { public void find(CommandIssuer issuer, String[] args) {
plugin.executeAsync(() -> { plugin.executeAsync(
() -> {
if (args.length > 0) { if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true); UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) { if (uuid == null) {
@ -106,7 +117,10 @@ public class LegacyRedisBungeeCommands extends AdventureBaseCommand {
String proxyId = plugin.playerDataManager().getProxyFor(uuid); String proxyId = plugin.playerDataManager().getProxyFor(uuid);
if (proxyId != null) { if (proxyId != null) {
String serverId = plugin.playerDataManager().getServerFor(uuid); String serverId = plugin.playerDataManager().getServerFor(uuid);
Component message = Component.text(args[0] + " is on proxy " + proxyId + " on server " + serverId +".", NamedTextColor.BLUE); Component message =
Component.text(
args[0] + " is on proxy " + proxyId + " on server " + serverId + ".",
NamedTextColor.BLUE);
sendMessage(issuer, message); sendMessage(issuer, message);
} else { } else {
sendMessage(issuer, PLAYER_NOT_FOUND); sendMessage(issuer, PLAYER_NOT_FOUND);
@ -115,13 +129,13 @@ public class LegacyRedisBungeeCommands extends AdventureBaseCommand {
sendMessage(issuer, NO_PLAYER_SPECIFIED); sendMessage(issuer, NO_PLAYER_SPECIFIED);
} }
}); });
} }
@Subcommand("lastseen") @Subcommand("lastseen")
@CommandPermission("redisbungee.command.lastseen") @CommandPermission("redisbungee.command.lastseen")
public void lastSeen(CommandIssuer issuer, String[] args) { public void lastSeen(CommandIssuer issuer, String[] args) {
plugin.executeAsync(() -> { plugin.executeAsync(
() -> {
if (args.length > 0) { if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true); UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) { if (uuid == null) {
@ -135,7 +149,8 @@ public class LegacyRedisBungeeCommands extends AdventureBaseCommand {
message.content(args[0] + " is currently online."); message.content(args[0] + " is currently online.");
} else if (secs != -1) { } else if (secs != -1) {
message.color(NamedTextColor.BLUE); message.color(NamedTextColor.BLUE);
message.content(args[0] + " was last online on " + new SimpleDateFormat().format(secs) + "."); message.content(
args[0] + " was last online on " + new SimpleDateFormat().format(secs) + ".");
} else { } else {
message.color(NamedTextColor.RED); message.color(NamedTextColor.RED);
message.content(args[0] + " has never been online."); message.content(args[0] + " has never been online.");
@ -144,15 +159,14 @@ public class LegacyRedisBungeeCommands extends AdventureBaseCommand {
} else { } else {
sendMessage(issuer, NO_PLAYER_SPECIFIED); sendMessage(issuer, NO_PLAYER_SPECIFIED);
} }
}); });
} }
@Subcommand("ip") @Subcommand("ip")
@CommandPermission("redisbungee.command.ip") @CommandPermission("redisbungee.command.ip")
public void ip(CommandIssuer issuer, String[] args) { public void ip(CommandIssuer issuer, String[] args) {
plugin.executeAsync(() -> { plugin.executeAsync(
() -> {
if (args.length > 0) { if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true); UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) { if (uuid == null) {
@ -161,7 +175,9 @@ public class LegacyRedisBungeeCommands extends AdventureBaseCommand {
} }
InetAddress ia = plugin.getAbstractRedisBungeeApi().getPlayerIp(uuid); InetAddress ia = plugin.getAbstractRedisBungeeApi().getPlayerIp(uuid);
if (ia != null) { if (ia != null) {
TextComponent message = Component.text(args[0] + " is connected from " + ia.toString() + ".", NamedTextColor.GREEN); TextComponent message =
Component.text(
args[0] + " is connected from " + ia.toString() + ".", NamedTextColor.GREEN);
sendMessage(issuer, message); sendMessage(issuer, message);
} else { } else {
sendMessage(issuer, PLAYER_NOT_FOUND); sendMessage(issuer, PLAYER_NOT_FOUND);
@ -175,7 +191,8 @@ public class LegacyRedisBungeeCommands extends AdventureBaseCommand {
@Subcommand("pproxy") @Subcommand("pproxy")
@CommandPermission("redisbungee.command.pproxy") @CommandPermission("redisbungee.command.pproxy")
public void playerProxy(CommandIssuer issuer, String[] args) { public void playerProxy(CommandIssuer issuer, String[] args) {
plugin.executeAsync(() -> { plugin.executeAsync(
() -> {
if (args.length > 0) { if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true); UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) { if (uuid == null) {
@ -184,7 +201,8 @@ public class LegacyRedisBungeeCommands extends AdventureBaseCommand {
} }
String proxy = plugin.getAbstractRedisBungeeApi().getProxy(uuid); String proxy = plugin.getAbstractRedisBungeeApi().getProxy(uuid);
if (proxy != null) { if (proxy != null) {
TextComponent message = Component.text(args[0] + " is connected to " + proxy + ".", NamedTextColor.GREEN); TextComponent message =
Component.text(args[0] + " is connected to " + proxy + ".", NamedTextColor.GREEN);
sendMessage(issuer, message); sendMessage(issuer, message);
} else { } else {
sendMessage(issuer, PLAYER_NOT_FOUND); sendMessage(issuer, PLAYER_NOT_FOUND);
@ -193,7 +211,6 @@ public class LegacyRedisBungeeCommands extends AdventureBaseCommand {
sendMessage(issuer, NO_PLAYER_SPECIFIED); sendMessage(issuer, NO_PLAYER_SPECIFIED);
} }
}); });
} }
@Subcommand("sendtoall") @Subcommand("sendtoall")
@ -202,59 +219,81 @@ public class LegacyRedisBungeeCommands extends AdventureBaseCommand {
if (args.length > 0) { if (args.length > 0) {
String command = Joiner.on(" ").skipNulls().join(args); String command = Joiner.on(" ").skipNulls().join(args);
plugin.getAbstractRedisBungeeApi().sendProxyCommand(command); plugin.getAbstractRedisBungeeApi().sendProxyCommand(command);
TextComponent message = Component.text("Sent the command /" + command + " to all proxies.", NamedTextColor.GREEN); TextComponent message =
Component.text("Sent the command /" + command + " to all proxies.", NamedTextColor.GREEN);
sendMessage(issuer, message); sendMessage(issuer, message);
} else { } else {
sendMessage(issuer, NO_COMMAND_SPECIFIED); sendMessage(issuer, NO_COMMAND_SPECIFIED);
} }
} }
@Subcommand("serverid") @Subcommand("serverid")
@CommandPermission("redisbungee.command.serverid") @CommandPermission("redisbungee.command.serverid")
public void serverId(CommandIssuer issuer) { public void serverId(CommandIssuer issuer) {
sendMessage(issuer, Component.text("You are on " + plugin.getAbstractRedisBungeeApi().getProxyId() + ".", NamedTextColor.YELLOW)); sendMessage(
issuer,
Component.text(
"You are on " + plugin.getAbstractRedisBungeeApi().getProxyId() + ".",
NamedTextColor.YELLOW));
} }
@Subcommand("serverids") @Subcommand("serverids")
@CommandPermission("redisbungee.command.serverids") @CommandPermission("redisbungee.command.serverids")
public void serverIds(CommandIssuer issuer) { public void serverIds(CommandIssuer issuer) {
sendMessage(issuer, Component.text("All Proxies IDs: " + Joiner.on(", ").join(plugin.getAbstractRedisBungeeApi().getAllProxies()), NamedTextColor.YELLOW)); sendMessage(
issuer,
Component.text(
"All Proxies IDs: "
+ Joiner.on(", ").join(plugin.getAbstractRedisBungeeApi().getAllProxies()),
NamedTextColor.YELLOW));
} }
@Subcommand("plist") @Subcommand("plist")
@CommandPermission("redisbungee.command.plist") @CommandPermission("redisbungee.command.plist")
public void playerList(CommandIssuer issuer, String[] args) { public void playerList(CommandIssuer issuer, String[] args) {
plugin.executeAsync(() -> { plugin.executeAsync(
() -> {
String proxy = args.length >= 1 ? args[0] : plugin.configuration().getProxyId(); String proxy = args.length >= 1 ? args[0] : plugin.configuration().getProxyId();
if (!plugin.proxyDataManager().proxiesIds().contains(proxy)) { if (!plugin.proxyDataManager().proxiesIds().contains(proxy)) {
sendMessage(issuer, Component.text(proxy + " is not a valid proxy. See /serverids for valid proxies.", NamedTextColor.RED)); sendMessage(
issuer,
Component.text(
proxy + " is not a valid proxy. See /serverids for valid proxies.",
NamedTextColor.RED));
return; return;
} }
Set<UUID> players = plugin.getAbstractRedisBungeeApi().getPlayersOnProxy(proxy); Set<UUID> players = plugin.getAbstractRedisBungeeApi().getPlayersOnProxy(proxy);
Component playersOnline = Component.text(playerPlural(players.size()) + " currently on proxy " + proxy + ".", NamedTextColor.YELLOW); Component playersOnline =
Component.text(
playerPlural(players.size()) + " currently on proxy " + proxy + ".",
NamedTextColor.YELLOW);
if (args.length >= 2 && args[1].equals("showall")) { if (args.length >= 2 && args[1].equals("showall")) {
Multimap<String, UUID> serverToPlayers = plugin.getAbstractRedisBungeeApi().getServerToPlayers(); Multimap<String, UUID> serverToPlayers =
plugin.getAbstractRedisBungeeApi().getServerToPlayers();
Multimap<String, String> human = HashMultimap.create(); Multimap<String, String> human = HashMultimap.create();
serverToPlayers.forEach((key, value) -> { serverToPlayers.forEach(
(key, value) -> {
if (players.contains(value)) { if (players.contains(value)) {
human.put(key, plugin.getUuidTranslator().getNameFromUuid(value, false)); human.put(key, plugin.getUuidTranslator().getNameFromUuid(value, false));
} }
}); });
for (String server : new TreeSet<>(human.keySet())) { for (String server : new TreeSet<>(human.keySet())) {
TextComponent serverName = Component.text("[" + server + "] ", NamedTextColor.RED); TextComponent serverName = Component.text("[" + server + "] ", NamedTextColor.RED);
TextComponent serverCount = Component.text("(" + human.get(server).size() + "): ", NamedTextColor.YELLOW); TextComponent serverCount =
TextComponent serverPlayers = Component.text(Joiner.on(", ").join(human.get(server)), NamedTextColor.WHITE); Component.text("(" + human.get(server).size() + "): ", NamedTextColor.YELLOW);
TextComponent serverPlayers =
Component.text(Joiner.on(", ").join(human.get(server)), NamedTextColor.WHITE);
sendMessage(issuer, Component.textOfChildren(serverName, serverCount, serverPlayers)); sendMessage(issuer, Component.textOfChildren(serverName, serverCount, serverPlayers));
} }
sendMessage(issuer, playersOnline); sendMessage(issuer, playersOnline);
} else { } else {
sendMessage(issuer, playersOnline); sendMessage(issuer, playersOnline);
sendMessage(issuer, Component.text("To see all players online, use /plist " + proxy + " showall.", NamedTextColor.YELLOW)); sendMessage(
issuer,
Component.text(
"To see all players online, use /plist " + proxy + " showall.",
NamedTextColor.YELLOW));
} }
}); });
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,20 +7,16 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.commands.utils; package com.imaginarycode.minecraft.redisbungee.commands.utils;
import co.aikar.commands.BaseCommand; import co.aikar.commands.BaseCommand;
import co.aikar.commands.CommandIssuer; import co.aikar.commands.CommandIssuer;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
/** /** this just dumb class that wraps the adventure stuff into base command */
* this just dumb class that wraps the adventure stuff into base command
*/
public abstract class AdventureBaseCommand extends BaseCommand { public abstract class AdventureBaseCommand extends BaseCommand {
protected void sendMessage(CommandIssuer issuer, Component component) { protected void sendMessage(CommandIssuer issuer, Component component) {
CommandPlatformHelper.getPlatformHelper().sendMessage(issuer, component); CommandPlatformHelper.getPlatformHelper().sendMessage(issuer, component);
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,14 +7,11 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.commands.utils; package com.imaginarycode.minecraft.redisbungee.commands.utils;
import co.aikar.commands.CommandIssuer; import co.aikar.commands.CommandIssuer;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
public abstract class CommandPlatformHelper { public abstract class CommandPlatformHelper {
private static CommandPlatformHelper SINGLETON; private static CommandPlatformHelper SINGLETON;
@ -28,9 +25,7 @@ public abstract class CommandPlatformHelper {
SINGLETON = platformHelper; SINGLETON = platformHelper;
} }
public static CommandPlatformHelper getPlatformHelper() { public static CommandPlatformHelper getPlatformHelper() {
return SINGLETON; return SINGLETON;
} }
} }

View File

@ -1,3 +1,12 @@
/*
* Copyright (c) 2026 RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee.commands.utils; package com.imaginarycode.minecraft.redisbungee.commands.utils;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin; import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
@ -12,13 +21,13 @@ public class StopperUUIDCleanupTask extends UUIDCleanupTask {
super(plugin); super(plugin);
} }
@Override @Override
public Void unifiedJedisTask(UnifiedJedis unifiedJedis) { public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
isRunning = true; isRunning = true;
try { try {
super.unifiedJedisTask(unifiedJedis); super.unifiedJedisTask(unifiedJedis);
} catch (Exception ignored) {} } catch (Exception ignored) {
}
isRunning = false; isRunning = false;
return null; return null;
} }

View File

@ -1,7 +1,9 @@
Copyright (c) 2013-present RedisBungee contributors /*
* Copyright (c) 2026 RedisBungee contributors
All rights reserved. This program and the accompanying materials *
are made available under the terms of the Eclipse Public License v1.0 * All rights reserved. This program and the accompanying materials
which accompanies this distribution, and is available at * are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html *
* http://www.eclipse.org/legal/epl-v10.html
*/

View File

@ -3,6 +3,9 @@ plugins {
`maven-publish` `maven-publish`
} }
version = property("redisbungee-version").toString()
group = property("redisbungee-group").toString()
dependencies { dependencies {
compileOnly(project(":RedisBungee-API")) compileOnly(project(":RedisBungee-API"))
compileOnly(libs.adventure.api) compileOnly(libs.adventure.api)

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,27 +7,26 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package net.limework.valiobungee.config.lang; package net.limework.valiobungee.config.lang;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin; import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.api.config.loaders.GenericConfigLoader; import com.imaginarycode.minecraft.redisbungee.api.config.loaders.GenericConfigLoader;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Locale;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.MiniMessage;
import ninja.leaping.configurate.ConfigurationNode; import ninja.leaping.configurate.ConfigurationNode;
import ninja.leaping.configurate.yaml.YAMLConfigurationLoader; import ninja.leaping.configurate.yaml.YAMLConfigurationLoader;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Locale;
public interface LangConfigLoader extends GenericConfigLoader { public interface LangConfigLoader extends GenericConfigLoader {
int CONFIG_VERSION = 1; int CONFIG_VERSION = 1;
default void loadLangConfig(RedisBungeePlugin<?> plugin, Path dataFolder) throws IOException { default void loadLangConfig(RedisBungeePlugin<?> plugin, Path dataFolder) throws IOException {
Path configFile = createConfigFile(dataFolder, "lang.yml", "lang.yml"); Path configFile = createConfigFile(dataFolder, "lang.yml", "lang.yml");
final YAMLConfigurationLoader yamlConfigurationFileLoader = YAMLConfigurationLoader.builder().setPath(configFile).build(); final YAMLConfigurationLoader yamlConfigurationFileLoader =
YAMLConfigurationLoader.builder().setPath(configFile).build();
ConfigurationNode node = yamlConfigurationFileLoader.load(); ConfigurationNode node = yamlConfigurationFileLoader.load();
if (node.getNode("config-version").getInt(0) != CONFIG_VERSION) { if (node.getNode("config-version").getInt(0) != CONFIG_VERSION) {
handleOldConfig(dataFolder, "lang.yml", "lang.yml"); handleOldConfig(dataFolder, "lang.yml", "lang.yml");
@ -36,20 +35,29 @@ public interface LangConfigLoader extends GenericConfigLoader {
// MINI message serializer // MINI message serializer
MiniMessage miniMessage = MiniMessage.miniMessage(); MiniMessage miniMessage = MiniMessage.miniMessage();
Component prefix = miniMessage.deserialize(node.getNode("prefix").getString("<color:red>[<color:yellow>Redis<color:red>Bungee]")); Component prefix =
miniMessage.deserialize(
node.getNode("prefix").getString("<color:red>[<color:yellow>Redis<color:red>Bungee]"));
Locale defaultLocale = Locale.forLanguageTag(node.getNode("default-locale").getString("en-us")); Locale defaultLocale = Locale.forLanguageTag(node.getNode("default-locale").getString("en-us"));
boolean useClientLocale = node.getNode("use-client-locale").getBoolean(true); boolean useClientLocale = node.getNode("use-client-locale").getBoolean(true);
LangConfiguration.Messages messages = new LangConfiguration.Messages(defaultLocale); LangConfiguration.Messages messages = new LangConfiguration.Messages(defaultLocale);
node.getNode("messages").getChildrenMap().forEach((key, childNode) -> childNode.getChildrenMap().forEach((childKey, childChildNode) -> { node.getNode("messages")
messages.register(key.toString(), Locale.forLanguageTag(childKey.toString()), childChildNode.getString()); .getChildrenMap()
.forEach(
(key, childNode) ->
childNode
.getChildrenMap()
.forEach(
(childKey, childChildNode) -> {
messages.register(
key.toString(),
Locale.forLanguageTag(childKey.toString()),
childChildNode.getString());
})); }));
messages.test(defaultLocale); messages.test(defaultLocale);
onLangConfigLoad(new LangConfiguration(prefix, defaultLocale, useClientLocale, messages)); onLangConfigLoad(new LangConfiguration(prefix, defaultLocale, useClientLocale, messages));
} }
void onLangConfigLoad(LangConfiguration langConfiguration); void onLangConfigLoad(LangConfiguration langConfiguration);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,21 +7,18 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package net.limework.valiobungee.config.lang; package net.limework.valiobungee.config.lang;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
/** /**
* This language support implementation is temporarily * This language support implementation is temporarily until I come up with better system but for
* until I come up with better system but for now we will use Maps instead :/ * now we will use Maps instead :/ Todo: possible usage of adventure api
* Todo: possible usage of adventure api
*/ */
public class LangConfiguration { public class LangConfiguration {
@ -32,9 +29,9 @@ public class LangConfiguration {
void test(Locale locale); void test(Locale locale);
default void throwError(Locale locale, String where) { default void throwError(Locale locale, String where) {
throw new IllegalStateException("Language system in `" + where + "` found missing entries for " + locale.toString()); throw new IllegalStateException(
"Language system in `" + where + "` found missing entries for " + locale.toString());
} }
} }
public static class Messages implements RegistrableMessages { public static class Messages implements RegistrableMessages {
@ -58,8 +55,11 @@ public class LangConfiguration {
switch (id) { switch (id) {
case "server-not-found" -> SERVER_NOT_FOUND.put(locale, miniMessage); case "server-not-found" -> SERVER_NOT_FOUND.put(locale, miniMessage);
case "server-connecting" -> SERVER_CONNECTING.put(locale, miniMessage); case "server-connecting" -> SERVER_CONNECTING.put(locale, miniMessage);
case "logged-in-other-location" -> LOGGED_IN_FROM_OTHER_LOCATION.put(locale, MiniMessage.miniMessage().deserialize(miniMessage)); case "logged-in-other-location" ->
case "already-logged-in" -> ALREADY_LOGGED_IN.put(locale, MiniMessage.miniMessage().deserialize(miniMessage)); LOGGED_IN_FROM_OTHER_LOCATION.put(
locale, MiniMessage.miniMessage().deserialize(miniMessage));
case "already-logged-in" ->
ALREADY_LOGGED_IN.put(locale, MiniMessage.miniMessage().deserialize(miniMessage));
} }
} }
@ -68,17 +68,20 @@ public class LangConfiguration {
return ALREADY_LOGGED_IN.get(defaultLocale); return ALREADY_LOGGED_IN.get(defaultLocale);
} }
// there is no way to know whats client locale during login so just default to use default locale MESSAGES. // there is no way to know whats client locale during login so just default to use default
// locale MESSAGES.
public Component alreadyLoggedIn() { public Component alreadyLoggedIn() {
return this.alreadyLoggedIn(this.defaultLocale); return this.alreadyLoggedIn(this.defaultLocale);
} }
public Component loggedInFromOtherLocation(Locale locale) { public Component loggedInFromOtherLocation(Locale locale) {
if (LOGGED_IN_FROM_OTHER_LOCATION.containsKey(locale)) return LOGGED_IN_FROM_OTHER_LOCATION.get(locale); if (LOGGED_IN_FROM_OTHER_LOCATION.containsKey(locale))
return LOGGED_IN_FROM_OTHER_LOCATION.get(locale);
return LOGGED_IN_FROM_OTHER_LOCATION.get(defaultLocale); return LOGGED_IN_FROM_OTHER_LOCATION.get(defaultLocale);
} }
// there is no way to know what's client locale during login so just default to use default locale MESSAGES. // there is no way to know what's client locale during login so just default to use default
// locale MESSAGES.
public Component loggedInFromOtherLocation() { public Component loggedInFromOtherLocation() {
return this.loggedInFromOtherLocation(this.defaultLocale); return this.loggedInFromOtherLocation(this.defaultLocale);
} }
@ -90,7 +93,8 @@ public class LangConfiguration {
} else { } else {
miniMessage = SERVER_CONNECTING.get(defaultLocale); miniMessage = SERVER_CONNECTING.get(defaultLocale);
} }
return MiniMessage.miniMessage().deserialize(miniMessage, Placeholder.parsed("server", server)); return MiniMessage.miniMessage()
.deserialize(miniMessage, Placeholder.parsed("server", server));
} }
public Component serverConnecting(String server) { public Component serverConnecting(String server) {
@ -104,21 +108,23 @@ public class LangConfiguration {
} else { } else {
miniMessage = SERVER_NOT_FOUND.get(defaultLocale); miniMessage = SERVER_NOT_FOUND.get(defaultLocale);
} }
return MiniMessage.miniMessage().deserialize(miniMessage, Placeholder.parsed("server", server)); return MiniMessage.miniMessage()
.deserialize(miniMessage, Placeholder.parsed("server", server));
} }
public Component serverNotFound(String server) { public Component serverNotFound(String server) {
return this.serverNotFound(this.defaultLocale, server); return this.serverNotFound(this.defaultLocale, server);
} }
// tests locale if set CORRECTLY or just throw if not // tests locale if set CORRECTLY or just throw if not
public void test(Locale locale) { public void test(Locale locale) {
if (!(LOGGED_IN_FROM_OTHER_LOCATION.containsKey(locale) && ALREADY_LOGGED_IN.containsKey(locale) && SERVER_CONNECTING.containsKey(locale) && SERVER_NOT_FOUND.containsKey(locale))) { if (!(LOGGED_IN_FROM_OTHER_LOCATION.containsKey(locale)
&& ALREADY_LOGGED_IN.containsKey(locale)
&& SERVER_CONNECTING.containsKey(locale)
&& SERVER_NOT_FOUND.containsKey(locale))) {
throwError(locale, "messages"); throwError(locale, "messages");
} }
} }
} }
private final Component redisBungeePrefix; private final Component redisBungeePrefix;
@ -129,7 +135,11 @@ public class LangConfiguration {
private final Messages messages; private final Messages messages;
public LangConfiguration(Component redisBungeePrefix, Locale defaultLanguage, boolean useClientLanguage, Messages messages) { public LangConfiguration(
Component redisBungeePrefix,
Locale defaultLanguage,
boolean useClientLanguage,
Messages messages) {
this.redisBungeePrefix = redisBungeePrefix; this.redisBungeePrefix = redisBungeePrefix;
this.defaultLanguage = defaultLanguage; this.defaultLanguage = defaultLanguage;
this.useClientLanguage = useClientLanguage; this.useClientLanguage = useClientLanguage;
@ -151,5 +161,4 @@ public class LangConfiguration {
public Messages messages() { public Messages messages() {
return messages; return messages;
} }
} }

View File

@ -3,6 +3,9 @@ plugins {
alias(libs.plugins.shadow) alias(libs.plugins.shadow)
} }
version = property("redisbungee-version").toString()
group = property("redisbungee-group").toString()
dependencies { dependencies {
implementation(project(":RedisBungee-Bungee")) implementation(project(":RedisBungee-Bungee"))
compileOnly(libs.platform.bungeecord) compileOnly(libs.platform.bungeecord)

View File

@ -3,6 +3,9 @@ plugins {
`maven-publish` `maven-publish`
} }
version = property("redisbungee-version").toString()
group = property("redisbungee-group").toString()
dependencies { dependencies {
api(project(":RedisBungee-API")) api(project(":RedisBungee-API"))
compileOnly(libs.adventure.platforms.bungeecord) compileOnly(libs.adventure.platforms.bungeecord)

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,16 +7,13 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee; package com.imaginarycode.minecraft.redisbungee;
import net.kyori.adventure.text.Component;
import java.util.UUID; import java.util.UUID;
import net.kyori.adventure.text.Component;
// this class used to redirect calls to keep the implementation and api separate // this class used to redirect calls to keep the implementation and api separate
public interface ApiPlatformSupport { public interface ApiPlatformSupport {
void kickPlayer(UUID player, Component message); void kickPlayer(UUID player, Component message);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,10 +7,11 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee; package com.imaginarycode.minecraft.redisbungee;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin; import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import java.util.List;
import java.util.UUID;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer; import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.BaseComponent;
@ -19,12 +20,10 @@ import net.md_5.bungee.api.plugin.Plugin;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.List;
import java.util.UUID;
/** /**
* This platform class exposes some internal RedisBungee functions. You obtain an instance of this object by invoking {@link RedisBungeeAPI#getRedisBungeeApi()} * This platform class exposes some internal RedisBungee functions. You obtain an instance of this
* or somehow you got the Plugin instance by you can call the api using {@link RedisBungeePlugin#getAbstractRedisBungeeApi()}. * object by invoking {@link RedisBungeeAPI#getRedisBungeeApi()} or somehow you got the Plugin
* instance by you can call the api using {@link RedisBungeePlugin#getAbstractRedisBungeeApi()}.
* *
* @author tuxed * @author tuxed
* @since 0.2.3 | updated 0.8.0 * @since 0.2.3 | updated 0.8.0
@ -33,7 +32,8 @@ public class RedisBungeeAPI extends AbstractRedisBungeeAPI {
private static RedisBungeeAPI redisBungeeApi; private static RedisBungeeAPI redisBungeeApi;
private static final BungeeComponentSerializer BUNGEE_COMPONENT_SERIALIZER = BungeeComponentSerializer.get(); private static final BungeeComponentSerializer BUNGEE_COMPONENT_SERIALIZER =
BungeeComponentSerializer.get();
public RedisBungeeAPI(RedisBungeePlugin<?> plugin) { public RedisBungeeAPI(RedisBungeePlugin<?> plugin) {
super(plugin); super(plugin);
@ -43,8 +43,8 @@ public class RedisBungeeAPI extends AbstractRedisBungeeAPI {
} }
/** /**
* Get the server where the specified player is playing. This function also deals with the case of local players * Get the server where the specified player is playing. This function also deals with the case of
* as well, and will return local information on them. * local players as well, and will return local information on them.
* *
* @param player a player uuid * @param player a player uuid
* @return {@link ServerInfo} Can be null if proxy can't find it. * @return {@link ServerInfo} Can be null if proxy can't find it.
@ -58,8 +58,7 @@ public class RedisBungeeAPI extends AbstractRedisBungeeAPI {
} }
/** /**
* Kicks a player from the network * Kicks a player from the network calls {@link #getUuidFromName(String)} to get uuid
* calls {@link #getUuidFromName(String)} to get uuid
* *
* @param playerName player name * @param playerName player name
* @param message kick message that player will see on kick * @param message kick message that player will see on kick
@ -81,8 +80,7 @@ public class RedisBungeeAPI extends AbstractRedisBungeeAPI {
} }
/** /**
* Kicks a player from the network * Kicks a player from the network calls {@link #getUuidFromName(String)} to get uuid
* calls {@link #getUuidFromName(String)} to get uuid
* *
* @param playerName player name * @param playerName player name
* @param message kick message that player will see on kick * @param message kick message that player will see on kick
@ -137,8 +135,7 @@ public class RedisBungeeAPI extends AbstractRedisBungeeAPI {
* @deprecated No longer required * @deprecated No longer required
*/ */
@Deprecated(forRemoval = true) @Deprecated(forRemoval = true)
public final void registerPubSubChannels(String... channels) { public final void registerPubSubChannels(String... channels) {}
}
/** /**
* Unregister (a) PubSub channel(s). * Unregister (a) PubSub channel(s).
@ -148,8 +145,7 @@ public class RedisBungeeAPI extends AbstractRedisBungeeAPI {
* @deprecated No longer required * @deprecated No longer required
*/ */
@Deprecated(forRemoval = true) @Deprecated(forRemoval = true)
public final void unregisterPubSubChannels(String... channels) { public final void unregisterPubSubChannels(String... channels) {}
}
/** /**
* Api instance * Api instance

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,24 +7,23 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.events; package com.imaginarycode.minecraft.redisbungee.events;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerChangedServerNetworkEvent; import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerChangedServerNetworkEvent;
import net.md_5.bungee.api.plugin.Event;
import java.util.UUID; import java.util.UUID;
import net.md_5.bungee.api.plugin.Event;
/** /**
* This event is sent when a player connects to a new server. RedisBungee sends the event only when * This event is sent when a player connects to a new server. RedisBungee sends the event only when
* the proxy the player has been connected to is different than the local proxy. * the proxy the player has been connected to is different than the local proxy.
* <p> *
* This event corresponds to {@link net.md_5.bungee.api.event.ServerConnectedEvent}, and is fired * <p>This event corresponds to {@link net.md_5.bungee.api.event.ServerConnectedEvent}, and is fired
* asynchronously. * asynchronously.
* *
* @since 0.3.4 * @since 0.3.4
*/ */
public class PlayerChangedServerNetworkEvent extends Event implements IPlayerChangedServerNetworkEvent { public class PlayerChangedServerNetworkEvent extends Event
implements IPlayerChangedServerNetworkEvent {
private final UUID uuid; private final UUID uuid;
private final String previousServer; private final String previousServer;
private final String server; private final String server;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,19 +7,17 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.events; package com.imaginarycode.minecraft.redisbungee.events;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerJoinedNetworkEvent; import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerJoinedNetworkEvent;
import java.util.UUID;
import net.md_5.bungee.api.plugin.Event; import net.md_5.bungee.api.plugin.Event;
import java.util.UUID;
/** /**
* This event is sent when a player joins the network. RedisBungee sends the event only when * This event is sent when a player joins the network. RedisBungee sends the event only when the
* the proxy the player has been connected to is different than the local proxy. * proxy the player has been connected to is different than the local proxy.
* <p> *
* This event corresponds to {@link net.md_5.bungee.api.event.PostLoginEvent}, and is fired * <p>This event corresponds to {@link net.md_5.bungee.api.event.PostLoginEvent}, and is fired
* asynchronously. * asynchronously.
* *
* @since 0.3.4 * @since 0.3.4

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,20 +7,18 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.events; package com.imaginarycode.minecraft.redisbungee.events;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerLeftNetworkEvent; import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerLeftNetworkEvent;
import java.util.UUID;
import net.md_5.bungee.api.plugin.Event; import net.md_5.bungee.api.plugin.Event;
import java.util.UUID;
/** /**
* This event is sent when a player disconnects. RedisBungee sends the event only when * This event is sent when a player disconnects. RedisBungee sends the event only when the proxy the
* the proxy the player has been connected to is different than the local proxy. * player has been connected to is different than the local proxy.
* <p> *
* This event corresponds to {@link net.md_5.bungee.api.event.PlayerDisconnectEvent}, and is fired * <p>This event corresponds to {@link net.md_5.bungee.api.event.PlayerDisconnectEvent}, and is
* asynchronously. * fired asynchronously.
* *
* @since 0.3.4 * @since 0.3.4
*/ */

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.events; package com.imaginarycode.minecraft.redisbungee.events;
import com.imaginarycode.minecraft.redisbungee.api.events.IPubSubMessageEvent; import com.imaginarycode.minecraft.redisbungee.api.events.IPubSubMessageEvent;
@ -15,12 +14,11 @@ import net.md_5.bungee.api.plugin.Event;
/** /**
* This event is posted when a PubSub message is received. * This event is posted when a PubSub message is received.
* <p> *
* <strong>Warning</strong>: This event is fired in a separate thread! * <p><strong>Warning</strong>: This event is fired in a separate thread!
* *
* @since 0.2.6 * @since 0.2.6
*/ */
public class PubSubMessageEvent extends Event implements IPubSubMessageEvent { public class PubSubMessageEvent extends Event implements IPubSubMessageEvent {
private final String channel; private final String channel;
private final String message; private final String message;

View File

@ -1,3 +1,12 @@
/*
* Copyright (c) 2026 RedisBungee contributors
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package com.imaginarycode.minecraft.redisbungee; package com.imaginarycode.minecraft.redisbungee;
import co.aikar.commands.BungeeCommandIssuer; import co.aikar.commands.BungeeCommandIssuer;
@ -13,5 +22,4 @@ public class BungeeCommandPlatformHelper extends CommandPlatformHelper {
BungeeCommandIssuer bIssuer = (BungeeCommandIssuer) issuer; BungeeCommandIssuer bIssuer = (BungeeCommandIssuer) issuer;
bIssuer.getIssuer().sendMessage(BungeeComponentSerializer.get().serialize(component)); bIssuer.getIssuer().sendMessage(BungeeComponentSerializer.get().serialize(component));
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,19 +7,18 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee; package com.imaginarycode.minecraft.redisbungee;
import com.imaginarycode.minecraft.redisbungee.api.PlayerDataManager; import com.imaginarycode.minecraft.redisbungee.api.PlayerDataManager;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.events.PlayerChangedServerNetworkEvent; import com.imaginarycode.minecraft.redisbungee.events.PlayerChangedServerNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.events.PlayerJoinedNetworkEvent; import com.imaginarycode.minecraft.redisbungee.events.PlayerJoinedNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.events.PlayerLeftNetworkEvent; import com.imaginarycode.minecraft.redisbungee.events.PlayerLeftNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent; import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer; import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.LoginEvent; import net.md_5.bungee.api.event.LoginEvent;
import net.md_5.bungee.api.event.PlayerDisconnectEvent; import net.md_5.bungee.api.event.PlayerDisconnectEvent;
@ -29,13 +28,10 @@ import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.event.EventHandler; import net.md_5.bungee.event.EventHandler;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class BungeePlayerDataManager extends PlayerDataManager<ProxiedPlayer> implements Listener { public class BungeePlayerDataManager extends PlayerDataManager<ProxiedPlayer> implements Listener {
private final RedisBungee bPlugin; private final RedisBungee bPlugin;
public BungeePlayerDataManager(RedisBungee plugin) { public BungeePlayerDataManager(RedisBungee plugin) {
super(plugin); super(plugin);
bPlugin = plugin; bPlugin = plugin;
@ -64,13 +60,18 @@ public class BungeePlayerDataManager extends PlayerDataManager<ProxiedPlayer> im
@EventHandler @EventHandler
public void onServerConnectedEvent(ServerConnectedEvent event) { public void onServerConnectedEvent(ServerConnectedEvent event) {
final String currentServer = event.getServer().getInfo().getName(); final String currentServer = event.getServer().getInfo().getName();
final String oldServer = event.getPlayer().getServer() == null ? null : event.getPlayer().getServer().getInfo().getName(); final String oldServer =
event.getPlayer().getServer() == null
? null
: event.getPlayer().getServer().getInfo().getName();
super.playerChangedServer(event.getPlayer().getUniqueId(), oldServer, currentServer); super.playerChangedServer(event.getPlayer().getUniqueId(), oldServer, currentServer);
} }
private final BungeeComponentSerializer BUNGEE_COMPONENT_SERIALIZER = BungeeComponentSerializer.get(); private final BungeeComponentSerializer BUNGEE_COMPONENT_SERIALIZER =
BungeeComponentSerializer.get();
private static final MiniMessage MINI_MESSAGE_SERIALIZER = MiniMessage.miniMessage();
private final static MiniMessage MINI_MESSAGE_SERIALIZER = MiniMessage.miniMessage();
@Override @Override
public boolean handleSerializedKick(UUID uuid, String serializedMiniMessage) { public boolean handleSerializedKick(UUID uuid, String serializedMiniMessage) {
ProxiedPlayer player = plugin.getPlayer(uuid); ProxiedPlayer player = plugin.getPlayer(uuid);
@ -96,39 +97,46 @@ public class BungeePlayerDataManager extends PlayerDataManager<ProxiedPlayer> im
event.registerIntent((Plugin) plugin); event.registerIntent((Plugin) plugin);
// check if online // check if online
if (getLastOnline(event.getConnection().getUniqueId()) == 0) { if (getLastOnline(event.getConnection().getUniqueId()) == 0) {
// because something can go wrong and proxy somehow does not update player data correctly on shutdown // because something can go wrong and proxy somehow does not update player data correctly on
// shutdown
// we have to check proxy if it has the player // we have to check proxy if it has the player
String proxyId = getProxyFor(event.getConnection().getUniqueId()); String proxyId = getProxyFor(event.getConnection().getUniqueId());
if (proxyId == null || !plugin.proxyDataManager().isPlayerTrulyOnProxy(proxyId, event.getConnection().getUniqueId())) { if (proxyId == null
|| !plugin
.proxyDataManager()
.isPlayerTrulyOnProxy(proxyId, event.getConnection().getUniqueId())) {
event.completeIntent((Plugin) plugin); event.completeIntent((Plugin) plugin);
} else { } else {
if (plugin.configuration().kickWhenOnline()) { if (plugin.configuration().kickWhenOnline()) {
kickPlayer(event.getConnection().getUniqueId(), bPlugin.langConfiguration().messages().loggedInFromOtherLocation()); kickPlayer(
event.getConnection().getUniqueId(),
bPlugin.langConfiguration().messages().loggedInFromOtherLocation());
// wait 3 seconds before releasing the event // wait 3 seconds before releasing the event
plugin.executeAsyncAfter(() -> event.completeIntent((Plugin) plugin), TimeUnit.SECONDS, 3); plugin.executeAsyncAfter(
() -> event.completeIntent((Plugin) plugin), TimeUnit.SECONDS, 3);
} else { } else {
event.setCancelled(true); event.setCancelled(true);
event.setCancelReason(BungeeComponentSerializer.get().serialize(bPlugin.langConfiguration().messages().alreadyLoggedIn())); event.setCancelReason(
BungeeComponentSerializer.get()
.serialize(bPlugin.langConfiguration().messages().alreadyLoggedIn()));
event.completeIntent((Plugin) plugin); event.completeIntent((Plugin) plugin);
} }
} }
} else { } else {
event.completeIntent((Plugin) plugin); event.completeIntent((Plugin) plugin);
} }
} }
@EventHandler @EventHandler
public void onLoginEvent(PostLoginEvent event) { public void onLoginEvent(PostLoginEvent event) {
super.addPlayer(event.getPlayer().getUniqueId(), event.getPlayer().getName(), event.getPlayer().getAddress().getAddress()); super.addPlayer(
event.getPlayer().getUniqueId(),
event.getPlayer().getName(),
event.getPlayer().getAddress().getAddress());
} }
@EventHandler @EventHandler
public void onDisconnectEvent(PlayerDisconnectEvent event) { public void onDisconnectEvent(PlayerDisconnectEvent event) {
super.removePlayer(event.getPlayer().getUniqueId()); super.removePlayer(event.getPlayer().getUniqueId());
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee; package com.imaginarycode.minecraft.redisbungee;
import co.aikar.commands.BungeeCommandManager; import co.aikar.commands.BungeeCommandManager;
@ -15,8 +14,8 @@ import com.imaginarycode.minecraft.redisbungee.api.PlayerDataManager;
import com.imaginarycode.minecraft.redisbungee.api.ProxyDataManager; import com.imaginarycode.minecraft.redisbungee.api.ProxyDataManager;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeeMode; import com.imaginarycode.minecraft.redisbungee.api.RedisBungeeMode;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin; import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.api.config.loaders.ConfigLoader;
import com.imaginarycode.minecraft.redisbungee.api.config.RedisBungeeConfiguration; import com.imaginarycode.minecraft.redisbungee.api.config.RedisBungeeConfiguration;
import com.imaginarycode.minecraft.redisbungee.api.config.loaders.ConfigLoader;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerChangedServerNetworkEvent; import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerChangedServerNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerJoinedNetworkEvent; import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerJoinedNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerLeftNetworkEvent; import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerLeftNetworkEvent;
@ -31,6 +30,14 @@ import com.imaginarycode.minecraft.redisbungee.events.PlayerChangedServerNetwork
import com.imaginarycode.minecraft.redisbungee.events.PlayerJoinedNetworkEvent; import com.imaginarycode.minecraft.redisbungee.events.PlayerJoinedNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.events.PlayerLeftNetworkEvent; import com.imaginarycode.minecraft.redisbungee.events.PlayerLeftNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent; import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.InetAddress;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.*;
import java.util.logging.Level;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.limework.valiobungee.config.lang.LangConfigLoader; import net.limework.valiobungee.config.lang.LangConfigLoader;
import net.limework.valiobungee.config.lang.LangConfiguration; import net.limework.valiobungee.config.lang.LangConfiguration;
@ -43,17 +50,11 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPool;
import java.io.IOException; public class RedisBungee extends Plugin
import java.lang.reflect.Field; implements RedisBungeePlugin<ProxiedPlayer>,
import java.net.InetAddress; ConfigLoader,
import java.util.HashSet; LangConfigLoader,
import java.util.Set; ApiPlatformSupport {
import java.util.UUID;
import java.util.concurrent.*;
import java.util.logging.Level;
public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlayer>, ConfigLoader, LangConfigLoader, ApiPlatformSupport {
private static RedisBungeeAPI apiStatic; private static RedisBungeeAPI apiStatic;
private AbstractRedisBungeeAPI api; private AbstractRedisBungeeAPI api;
@ -70,7 +71,6 @@ public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlay
private final Logger logger = LoggerFactory.getLogger("RedisBungee"); private final Logger logger = LoggerFactory.getLogger("RedisBungee");
@Override @Override
public RedisBungeeConfiguration configuration() { public RedisBungeeConfiguration configuration() {
return this.configuration; return this.configuration;
@ -175,7 +175,6 @@ public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlay
return player.getAddress().getAddress(); return player.getAddress().getAddress();
} }
@Override @Override
public void initialize() { public void initialize() {
logInfo("Initializing RedisBungee....."); logInfo("Initializing RedisBungee.....");
@ -199,18 +198,23 @@ public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlay
throw new RuntimeException("Unable to load/save config", e); throw new RuntimeException("Unable to load/save config", e);
} }
// init the proxy data manager // init the proxy data manager
this.proxyDataManager = new ProxyDataManager(this) { this.proxyDataManager =
new ProxyDataManager(this) {
@Override @Override
public Set<UUID> getLocalOnlineUUIDs() { public Set<UUID> getLocalOnlineUUIDs() {
HashSet<UUID> uuids = new HashSet<>(); HashSet<UUID> uuids = new HashSet<>();
ProxyServer.getInstance().getPlayers().forEach((proxiedPlayer) -> uuids.add(proxiedPlayer.getUniqueId())); ProxyServer.getInstance()
.getPlayers()
.forEach((proxiedPlayer) -> uuids.add(proxiedPlayer.getUniqueId()));
return uuids; return uuids;
} }
@Override @Override
protected void handlePlatformCommandExecution(String command) { protected void handlePlatformCommandExecution(String command) {
logInfo("Dispatching {}", command); logInfo("Dispatching {}", command);
ProxyServer.getInstance().getPluginManager().dispatchCommand(RedisBungeeCommandSender.getSingleton(), command); ProxyServer.getInstance()
.getPluginManager()
.dispatchCommand(RedisBungeeCommandSender.getSingleton(), command);
} }
}; };
this.playerDataManager = new BungeePlayerDataManager(this); this.playerDataManager = new BungeePlayerDataManager(this);
@ -220,9 +224,15 @@ public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlay
// start listening // start listening
getProxy().getScheduler().runAsync(this, proxyDataManager); getProxy().getScheduler().runAsync(this, proxyDataManager);
// heartbeat // heartbeat
this.heartbeatTask = getProxy().getScheduler().schedule(this, () -> this.proxyDataManager.publishHeartbeat(), 0, 1, TimeUnit.SECONDS); this.heartbeatTask =
getProxy()
.getScheduler()
.schedule(this, () -> this.proxyDataManager.publishHeartbeat(), 0, 1, TimeUnit.SECONDS);
// cleanup // cleanup
this.cleanupTask = getProxy().getScheduler().schedule(this, () -> this.proxyDataManager.correctionTask(), 0, 60, TimeUnit.SECONDS); this.cleanupTask =
getProxy()
.getScheduler()
.schedule(this, () -> this.proxyDataManager.correctionTask(), 0, 60, TimeUnit.SECONDS);
// init the http lib // init the http lib
InitialUtils.checkRedisVersion(this); InitialUtils.checkRedisVersion(this);
uuidTranslator = new UUIDTranslator(this); uuidTranslator = new UUIDTranslator(this);
@ -301,7 +311,8 @@ public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlay
} }
@Override @Override
public IPlayerChangedServerNetworkEvent createPlayerChangedServerNetworkEvent(UUID uuid, String previousServer, String server) { public IPlayerChangedServerNetworkEvent createPlayerChangedServerNetworkEvent(
UUID uuid, String previousServer, String server) {
return new PlayerChangedServerNetworkEvent(uuid, previousServer, server); return new PlayerChangedServerNetworkEvent(uuid, previousServer, server);
} }
@ -321,7 +332,8 @@ public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlay
} }
@Override @Override
public void onConfigLoad(RedisBungeeConfiguration configuration, Summoner<?> summoner, RedisBungeeMode mode) { public void onConfigLoad(
RedisBungeeConfiguration configuration, Summoner<?> summoner, RedisBungeeMode mode) {
this.configuration = configuration; this.configuration = configuration;
this.redisBungeeMode = mode; this.redisBungeeMode = mode;
this.summoner = summoner; this.summoner = summoner;
@ -346,7 +358,8 @@ public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlay
* This returns an instance of {@link RedisBungeeAPI} * This returns an instance of {@link RedisBungeeAPI}
* *
* @return the {@link AbstractRedisBungeeAPI} object instance. * @return the {@link AbstractRedisBungeeAPI} object instance.
* @deprecated Please use {@link RedisBungeeAPI#getRedisBungeeApi()} this class intended to for old plugins that no longer updated. * @deprecated Please use {@link RedisBungeeAPI#getRedisBungeeApi()} this class intended to for
* old plugins that no longer updated.
*/ */
@Deprecated @Deprecated
public static RedisBungeeAPI getApi() { public static RedisBungeeAPI getApi() {
@ -358,12 +371,12 @@ public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlay
if (api.getMode() == RedisBungeeMode.SINGLE) { if (api.getMode() == RedisBungeeMode.SINGLE) {
JedisPool jedisPool = ((JedisPooledSummoner) getSummoner()).getCompatibilityJedisPool(); JedisPool jedisPool = ((JedisPooledSummoner) getSummoner()).getCompatibilityJedisPool();
if (jedisPool == null) { if (jedisPool == null) {
throw new IllegalStateException("JedisPool compatibility mode is disabled, Please enable it in the RedisBungee config.yml"); throw new IllegalStateException(
"JedisPool compatibility mode is disabled, Please enable it in the RedisBungee config.yml");
} }
return jedisPool; return jedisPool;
} else { } else {
throw new IllegalStateException("Mode is not " + RedisBungeeMode.SINGLE); throw new IllegalStateException("Mode is not " + RedisBungeeMode.SINGLE);
} }
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,14 +7,12 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee; package com.imaginarycode.minecraft.redisbungee;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.BaseComponent;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.BaseComponent;
public class RedisBungeeCommandSender implements CommandSender { public class RedisBungeeCommandSender implements CommandSender {
private static final RedisBungeeCommandSender singleton; private static final RedisBungeeCommandSender singleton;
@ -33,24 +31,16 @@ public class RedisBungeeCommandSender implements CommandSender {
} }
@Override @Override
public void sendMessage(String s) { public void sendMessage(String s) {}
}
@Override @Override
public void sendMessages(String... strings) { public void sendMessages(String... strings) {}
}
@Override @Override
public void sendMessage(BaseComponent... baseComponents) { public void sendMessage(BaseComponent... baseComponents) {}
}
@Override @Override
public void sendMessage(BaseComponent baseComponent) { public void sendMessage(BaseComponent baseComponent) {}
}
@Override @Override
public Collection<String> getGroups() { public Collection<String> getGroups() {
@ -58,14 +48,10 @@ public class RedisBungeeCommandSender implements CommandSender {
} }
@Override @Override
public void addGroups(String... strings) { public void addGroups(String... strings) {}
}
@Override @Override
public void removeGroups(String... strings) { public void removeGroups(String... strings) {}
}
@Override @Override
public boolean hasPermission(String s) { public boolean hasPermission(String s) {
@ -73,9 +59,7 @@ public class RedisBungeeCommandSender implements CommandSender {
} }
@Override @Override
public void setPermission(String s, boolean b) { public void setPermission(String s, boolean b) {}
}
@Override @Override
public Collection<String> getPermissions() { public Collection<String> getPermissions() {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,17 +7,18 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee; package com.imaginarycode.minecraft.redisbungee;
import static com.imaginarycode.minecraft.redisbungee.api.util.serialize.MultiMapSerialization.*;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.collect.HashMultimap; import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.google.common.io.ByteArrayDataInput; import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.api.config.HandleMotdOrder; import com.imaginarycode.minecraft.redisbungee.api.config.HandleMotdOrder;
import java.util.*;
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer; import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
import net.md_5.bungee.api.AbstractReconnectHandler; import net.md_5.bungee.api.AbstractReconnectHandler;
import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ProxyServer;
@ -31,10 +32,6 @@ import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler; import net.md_5.bungee.event.EventHandler;
import net.md_5.bungee.event.EventPriority; import net.md_5.bungee.event.EventPriority;
import java.util.*;
import static com.imaginarycode.minecraft.redisbungee.api.util.serialize.MultiMapSerialization.*;
public class RedisBungeeListener implements Listener { public class RedisBungeeListener implements Listener {
private final RedisBungee plugin; private final RedisBungee plugin;
@ -69,7 +66,10 @@ public class RedisBungeeListener implements Listener {
private void onPing0(ProxyPingEvent event) { private void onPing0(ProxyPingEvent event) {
if (!plugin.configuration().handleMotd()) return; if (!plugin.configuration().handleMotd()) return;
if (plugin.configuration().getExemptAddresses().contains(event.getConnection().getAddress().getAddress())) return; if (plugin
.configuration()
.getExemptAddresses()
.contains(event.getConnection().getAddress().getAddress())) return;
ServerInfo forced = AbstractReconnectHandler.getForcedHost(event.getConnection()); ServerInfo forced = AbstractReconnectHandler.getForcedHost(event.getConnection());
if (forced != null && event.getConnection().getListener().isPingPassthrough()) return; if (forced != null && event.getConnection().getListener().isPingPassthrough()) return;
@ -79,10 +79,12 @@ public class RedisBungeeListener implements Listener {
@SuppressWarnings("UnstableApiUsage") @SuppressWarnings("UnstableApiUsage")
@EventHandler @EventHandler
public void onPluginMessage(PluginMessageEvent event) { public void onPluginMessage(PluginMessageEvent event) {
if ((event.getTag().equals("legacy:redisbungee") || event.getTag().equals("RedisBungee")) && event.getSender() instanceof Server) { if ((event.getTag().equals("legacy:redisbungee") || event.getTag().equals("RedisBungee"))
&& event.getSender() instanceof Server) {
final String currentChannel = event.getTag(); final String currentChannel = event.getTag();
final byte[] data = Arrays.copyOf(event.getData(), event.getData().length); final byte[] data = Arrays.copyOf(event.getData(), event.getData().length);
plugin.executeAsync(() -> { plugin.executeAsync(
() -> {
ByteArrayDataInput in = ByteStreams.newDataInput(data); ByteArrayDataInput in = ByteStreams.newDataInput(data);
String subchannel = in.readUTF(); String subchannel = in.readUTF();
@ -118,7 +120,8 @@ public class RedisBungeeListener implements Listener {
} else { } else {
out.writeUTF(type); out.writeUTF(type);
try { try {
out.writeInt(plugin.getAbstractRedisBungeeApi().getPlayersOnServer(type).size()); out.writeInt(
plugin.getAbstractRedisBungeeApi().getPlayersOnServer(type).size());
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
out.writeInt(0); out.writeInt(0);
} }
@ -128,12 +131,18 @@ public class RedisBungeeListener implements Listener {
String user = in.readUTF(); String user = in.readUTF();
out.writeUTF("LastOnline"); out.writeUTF("LastOnline");
out.writeUTF(user); out.writeUTF(user);
out.writeLong(plugin.getAbstractRedisBungeeApi().getLastOnline(Objects.requireNonNull(plugin.getUuidTranslator().getTranslatedUuid(user, true)))); out.writeLong(
plugin
.getAbstractRedisBungeeApi()
.getLastOnline(
Objects.requireNonNull(
plugin.getUuidTranslator().getTranslatedUuid(user, true))));
} }
case "ServerPlayers" -> { case "ServerPlayers" -> {
String type1 = in.readUTF(); String type1 = in.readUTF();
out.writeUTF("ServerPlayers"); out.writeUTF("ServerPlayers");
Multimap<String, UUID> multimap = plugin.getAbstractRedisBungeeApi().getServerToPlayers(); Multimap<String, UUID> multimap =
plugin.getAbstractRedisBungeeApi().getServerToPlayers();
boolean includesUsers; boolean includesUsers;
switch (type1) { switch (type1) {
case "COUNT" -> includesUsers = false; case "COUNT" -> includesUsers = false;
@ -147,7 +156,9 @@ public class RedisBungeeListener implements Listener {
if (includesUsers) { if (includesUsers) {
Multimap<String, String> human = HashMultimap.create(); Multimap<String, String> human = HashMultimap.create();
for (Map.Entry<String, UUID> entry : multimap.entries()) { for (Map.Entry<String, UUID> entry : multimap.entries()) {
human.put(entry.getKey(), plugin.getUuidTranslator().getNameFromUuid(entry.getValue(), false)); human.put(
entry.getKey(),
plugin.getUuidTranslator().getNameFromUuid(entry.getValue(), false));
} }
serializeMultimap(human, true, out); serializeMultimap(human, true, out);
} else { } else {
@ -162,7 +173,12 @@ public class RedisBungeeListener implements Listener {
String username = in.readUTF(); String username = in.readUTF();
out.writeUTF("PlayerProxy"); out.writeUTF("PlayerProxy");
out.writeUTF(username); out.writeUTF(username);
out.writeUTF(plugin.getAbstractRedisBungeeApi().getProxy(Objects.requireNonNull(plugin.getUuidTranslator().getTranslatedUuid(username, true)))); out.writeUTF(
plugin
.getAbstractRedisBungeeApi()
.getProxy(
Objects.requireNonNull(
plugin.getUuidTranslator().getTranslatedUuid(username, true))));
} }
default -> { default -> {
return; return;
@ -176,14 +192,28 @@ public class RedisBungeeListener implements Listener {
@EventHandler @EventHandler
public void onServerConnectEvent(ServerConnectEvent event) { public void onServerConnectEvent(ServerConnectEvent event) {
if (event.getReason() == ServerConnectEvent.Reason.JOIN_PROXY && plugin.configuration().handleReconnectToLastServer()) { if (event.getReason() == ServerConnectEvent.Reason.JOIN_PROXY
&& plugin.configuration().handleReconnectToLastServer()) {
ProxiedPlayer player = event.getPlayer(); ProxiedPlayer player = event.getPlayer();
String lastServer = plugin.playerDataManager().getLastServerFor(event.getPlayer().getUniqueId()); String lastServer =
plugin.playerDataManager().getLastServerFor(event.getPlayer().getUniqueId());
if (lastServer == null) return; if (lastServer == null) return;
player.sendMessage(BungeeComponentSerializer.get().serialize(plugin.langConfiguration().messages().serverConnecting(player.getLocale(), lastServer))); player.sendMessage(
BungeeComponentSerializer.get()
.serialize(
plugin
.langConfiguration()
.messages()
.serverConnecting(player.getLocale(), lastServer)));
ServerInfo serverInfo = ProxyServer.getInstance().getServerInfo(lastServer); ServerInfo serverInfo = ProxyServer.getInstance().getServerInfo(lastServer);
if (serverInfo == null) { if (serverInfo == null) {
player.sendMessage(BungeeComponentSerializer.get().serialize(plugin.langConfiguration().messages().serverNotFound(player.getLocale(), lastServer))); player.sendMessage(
BungeeComponentSerializer.get()
.serialize(
plugin
.langConfiguration()
.messages()
.serverNotFound(player.getLocale(), lastServer)));
return; return;
} }
event.setTarget(serverInfo); event.setTarget(serverInfo);

View File

@ -4,6 +4,9 @@ plugins {
alias(libs.plugins.run.velocity) alias(libs.plugins.run.velocity)
} }
version = property("redisbungee-version").toString()
group = property("redisbungee-group").toString()
dependencies { dependencies {
implementation(project(":RedisBungee-Velocity")) implementation(project(":RedisBungee-Velocity"))
compileOnly(libs.platform.velocity) compileOnly(libs.platform.velocity)

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,17 +7,16 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee; package com.imaginarycode.minecraft.redisbungee;
import com.velocitypowered.api.command.CommandSource; import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.permission.Tristate; import com.velocitypowered.api.permission.Tristate;
import net.kyori.adventure.permission.PermissionChecker; import net.kyori.adventure.permission.PermissionChecker;
public class RedisBungeeCommandSource implements CommandSource { public class RedisBungeeCommandSource implements CommandSource {
private static final RedisBungeeCommandSource singleton; private static final RedisBungeeCommandSource singleton;
private final PermissionChecker permissionChecker = PermissionChecker.always(net.kyori.adventure.util.TriState.TRUE); private final PermissionChecker permissionChecker =
PermissionChecker.always(net.kyori.adventure.util.TriState.TRUE);
static { static {
singleton = new RedisBungeeCommandSource(); singleton = new RedisBungeeCommandSource();
@ -27,7 +26,6 @@ public class RedisBungeeCommandSource implements CommandSource {
return singleton; return singleton;
} }
@Override @Override
public boolean hasPermission(String permission) { public boolean hasPermission(String permission) {
return this.permissionChecker.test(permission); return this.permissionChecker.test(permission);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,16 +7,17 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee; package com.imaginarycode.minecraft.redisbungee;
import static com.imaginarycode.minecraft.redisbungee.api.util.serialize.MultiMapSerialization.serializeMultimap;
import static com.imaginarycode.minecraft.redisbungee.api.util.serialize.MultiMapSerialization.serializeMultiset;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.collect.HashMultimap; import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.google.common.io.ByteArrayDataInput; import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.api.config.HandleMotdOrder; import com.imaginarycode.minecraft.redisbungee.api.config.HandleMotdOrder;
import com.velocitypowered.api.event.PostOrder; import com.velocitypowered.api.event.PostOrder;
import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.Subscribe;
@ -27,13 +28,9 @@ import com.velocitypowered.api.proxy.Player;
import com.velocitypowered.api.proxy.ServerConnection; import com.velocitypowered.api.proxy.ServerConnection;
import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerPing; import com.velocitypowered.api.proxy.server.ServerPing;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.imaginarycode.minecraft.redisbungee.api.util.serialize.MultiMapSerialization.serializeMultimap;
import static com.imaginarycode.minecraft.redisbungee.api.util.serialize.MultiMapSerialization.serializeMultiset;
public class RedisBungeeListener { public class RedisBungeeListener {
private final RedisBungeeVelocityPlugin plugin; private final RedisBungeeVelocityPlugin plugin;
@ -68,7 +65,10 @@ public class RedisBungeeListener {
private void onPing0(ProxyPingEvent event) { private void onPing0(ProxyPingEvent event) {
if (!plugin.configuration().handleMotd()) return; if (!plugin.configuration().handleMotd()) return;
if (plugin.configuration().getExemptAddresses().contains(event.getConnection().getRemoteAddress().getAddress())) return; if (plugin
.configuration()
.getExemptAddresses()
.contains(event.getConnection().getRemoteAddress().getAddress())) return;
ServerPing.Builder ping = event.getPing().asBuilder(); ServerPing.Builder ping = event.getPing().asBuilder();
ping.onlinePlayers(plugin.proxyDataManager().totalNetworkPlayers()); ping.onlinePlayers(plugin.proxyDataManager().totalNetworkPlayers());
@ -77,13 +77,15 @@ public class RedisBungeeListener {
@Subscribe @Subscribe
public void onPluginMessage(PluginMessageEvent event) { public void onPluginMessage(PluginMessageEvent event) {
if (!(event.getSource() instanceof ServerConnection) || !RedisBungeeVelocityPlugin.IDENTIFIERS.contains(event.getIdentifier())) { if (!(event.getSource() instanceof ServerConnection)
|| !RedisBungeeVelocityPlugin.IDENTIFIERS.contains(event.getIdentifier())) {
return; return;
} }
event.setResult(PluginMessageEvent.ForwardResult.handled()); event.setResult(PluginMessageEvent.ForwardResult.handled());
plugin.executeAsync(() -> { plugin.executeAsync(
() -> {
ByteArrayDataInput in = event.dataAsDataStream(); ByteArrayDataInput in = event.dataAsDataStream();
String subchannel = in.readUTF(); String subchannel = in.readUTF();
@ -105,7 +107,8 @@ public class RedisBungeeListener {
} catch (IllegalArgumentException ignored) { } catch (IllegalArgumentException ignored) {
} }
} }
Set<String> players = original.stream() Set<String> players =
original.stream()
.map(uuid -> plugin.getUuidTranslator().getNameFromUuid(uuid, false)) .map(uuid -> plugin.getUuidTranslator().getNameFromUuid(uuid, false))
.collect(Collectors.toSet()); .collect(Collectors.toSet());
out.writeUTF(Joiner.on(',').join(players)); out.writeUTF(Joiner.on(',').join(players));
@ -129,12 +132,18 @@ public class RedisBungeeListener {
String user = in.readUTF(); String user = in.readUTF();
out.writeUTF("LastOnline"); out.writeUTF("LastOnline");
out.writeUTF(user); out.writeUTF(user);
out.writeLong(plugin.getAbstractRedisBungeeApi().getLastOnline(Objects.requireNonNull(plugin.getUuidTranslator().getTranslatedUuid(user, true)))); out.writeLong(
plugin
.getAbstractRedisBungeeApi()
.getLastOnline(
Objects.requireNonNull(
plugin.getUuidTranslator().getTranslatedUuid(user, true))));
} }
case "ServerPlayers" -> { case "ServerPlayers" -> {
String type1 = in.readUTF(); String type1 = in.readUTF();
out.writeUTF("ServerPlayers"); out.writeUTF("ServerPlayers");
Multimap<String, UUID> multimap = plugin.getAbstractRedisBungeeApi().getServerToPlayers(); Multimap<String, UUID> multimap =
plugin.getAbstractRedisBungeeApi().getServerToPlayers();
boolean includesUsers; boolean includesUsers;
switch (type1) { switch (type1) {
case "COUNT" -> includesUsers = false; case "COUNT" -> includesUsers = false;
@ -148,7 +157,9 @@ public class RedisBungeeListener {
if (includesUsers) { if (includesUsers) {
Multimap<String, String> human = HashMultimap.create(); Multimap<String, String> human = HashMultimap.create();
for (Map.Entry<String, UUID> entry : multimap.entries()) { for (Map.Entry<String, UUID> entry : multimap.entries()) {
human.put(entry.getKey(), plugin.getUuidTranslator().getNameFromUuid(entry.getValue(), false)); human.put(
entry.getKey(),
plugin.getUuidTranslator().getNameFromUuid(entry.getValue(), false));
} }
serializeMultimap(human, true, out); serializeMultimap(human, true, out);
} else { } else {
@ -163,19 +174,25 @@ public class RedisBungeeListener {
String username = in.readUTF(); String username = in.readUTF();
out.writeUTF("PlayerProxy"); out.writeUTF("PlayerProxy");
out.writeUTF(username); out.writeUTF(username);
out.writeUTF(plugin.getAbstractRedisBungeeApi().getProxy(Objects.requireNonNull(plugin.getUuidTranslator().getTranslatedUuid(username, true)))); out.writeUTF(
plugin
.getAbstractRedisBungeeApi()
.getProxy(
Objects.requireNonNull(
plugin.getUuidTranslator().getTranslatedUuid(username, true))));
} }
default -> { default -> {
return; return;
} }
} }
try { try {
// ServerConnection throws IllegalStateException when connection dies somehow so just ignore :/ // ServerConnection throws IllegalStateException when connection dies somehow so just
((ServerConnection) event.getSource()).sendPluginMessage(event.getIdentifier(), out.toByteArray()); // ignore :/
((ServerConnection) event.getSource())
.sendPluginMessage(event.getIdentifier(), out.toByteArray());
} catch (IllegalStateException ignored) { } catch (IllegalStateException ignored) {
} }
}); });
} }
@Subscribe @Subscribe
@ -184,16 +201,23 @@ public class RedisBungeeListener {
Player player = event.getPlayer(); Player player = event.getPlayer();
String lastServer = plugin.playerDataManager().getLastServerFor(player.getUniqueId()); String lastServer = plugin.playerDataManager().getLastServerFor(player.getUniqueId());
if (lastServer == null) return; if (lastServer == null) return;
player.sendMessage(plugin.langConfiguration().messages().serverConnecting(player.getPlayerSettings().getLocale(), lastServer)); player.sendMessage(
Optional<RegisteredServer> optionalRegisteredServer = ((RedisBungeeVelocityPlugin) plugin).getProxy().getServer(lastServer); plugin
.langConfiguration()
.messages()
.serverConnecting(player.getPlayerSettings().getLocale(), lastServer));
Optional<RegisteredServer> optionalRegisteredServer =
((RedisBungeeVelocityPlugin) plugin).getProxy().getServer(lastServer);
if (optionalRegisteredServer.isEmpty()) { if (optionalRegisteredServer.isEmpty()) {
player.sendMessage(plugin.langConfiguration().messages().serverNotFound(player.getPlayerSettings().getLocale(), lastServer)); player.sendMessage(
plugin
.langConfiguration()
.messages()
.serverNotFound(player.getPlayerSettings().getLocale(), lastServer));
return; return;
} }
RegisteredServer server = optionalRegisteredServer.get(); RegisteredServer server = optionalRegisteredServer.get();
event.setInitialServer(server); event.setInitialServer(server);
} }
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee; package com.imaginarycode.minecraft.redisbungee;
import co.aikar.commands.VelocityCommandManager; import co.aikar.commands.VelocityCommandManager;
@ -16,10 +15,8 @@ import com.imaginarycode.minecraft.redisbungee.api.PlayerDataManager;
import com.imaginarycode.minecraft.redisbungee.api.ProxyDataManager; import com.imaginarycode.minecraft.redisbungee.api.ProxyDataManager;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeeMode; import com.imaginarycode.minecraft.redisbungee.api.RedisBungeeMode;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin; import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.commands.CommandLoader;
import com.imaginarycode.minecraft.redisbungee.commands.utils.CommandPlatformHelper;
import com.imaginarycode.minecraft.redisbungee.api.config.loaders.ConfigLoader;
import com.imaginarycode.minecraft.redisbungee.api.config.RedisBungeeConfiguration; import com.imaginarycode.minecraft.redisbungee.api.config.RedisBungeeConfiguration;
import com.imaginarycode.minecraft.redisbungee.api.config.loaders.ConfigLoader;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerChangedServerNetworkEvent; import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerChangedServerNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerJoinedNetworkEvent; import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerJoinedNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerLeftNetworkEvent; import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerLeftNetworkEvent;
@ -27,6 +24,8 @@ import com.imaginarycode.minecraft.redisbungee.api.events.IPubSubMessageEvent;
import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner; import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner;
import com.imaginarycode.minecraft.redisbungee.api.util.InitialUtils; import com.imaginarycode.minecraft.redisbungee.api.util.InitialUtils;
import com.imaginarycode.minecraft.redisbungee.api.util.uuid.UUIDTranslator; import com.imaginarycode.minecraft.redisbungee.api.util.uuid.UUIDTranslator;
import com.imaginarycode.minecraft.redisbungee.commands.CommandLoader;
import com.imaginarycode.minecraft.redisbungee.commands.utils.CommandPlatformHelper;
import com.imaginarycode.minecraft.redisbungee.events.PlayerChangedServerNetworkEvent; import com.imaginarycode.minecraft.redisbungee.events.PlayerChangedServerNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.events.PlayerJoinedNetworkEvent; import com.imaginarycode.minecraft.redisbungee.events.PlayerJoinedNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.events.PlayerLeftNetworkEvent; import com.imaginarycode.minecraft.redisbungee.events.PlayerLeftNetworkEvent;
@ -43,12 +42,6 @@ import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier; import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier; import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
import com.velocitypowered.api.scheduler.ScheduledTask; import com.velocitypowered.api.scheduler.ScheduledTask;
import net.kyori.adventure.text.Component;
import net.limework.valiobungee.config.lang.LangConfiguration;
import net.limework.valiobungee.config.lang.LangConfigLoader;
import org.slf4j.Logger;
import redis.clients.jedis.exceptions.JedisConnectionException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.InetAddress; import java.net.InetAddress;
@ -59,9 +52,20 @@ import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import net.kyori.adventure.text.Component;
import net.limework.valiobungee.config.lang.LangConfigLoader;
import net.limework.valiobungee.config.lang.LangConfiguration;
import org.slf4j.Logger;
import redis.clients.jedis.exceptions.JedisConnectionException;
@Plugin(id = "redisbungee", name = "RedisBungee", version = Constants.VERSION, url = "https://github.com/ProxioDev/RedisBungee", authors = {"astei", "ProxioDev"}) @Plugin(
public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, ConfigLoader, LangConfigLoader, ApiPlatformSupport { id = "redisbungee",
name = "RedisBungee",
version = Constants.VERSION,
url = "https://github.com/ProxioDev/RedisBungee",
authors = {"astei", "ProxioDev"})
public class RedisBungeeVelocityPlugin
implements RedisBungeePlugin<Player>, ConfigLoader, LangConfigLoader, ApiPlatformSupport {
private final ProxyServer server; private final ProxyServer server;
private final Logger logger; private final Logger logger;
private final Path dataFolder; private final Path dataFolder;
@ -79,17 +83,18 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
private ScheduledTask cleanUpTask; private ScheduledTask cleanUpTask;
private ScheduledTask heartbeatTask; private ScheduledTask heartbeatTask;
public static final List<ChannelIdentifier> IDENTIFIERS = List.of( public static final List<ChannelIdentifier> IDENTIFIERS =
List.of(
MinecraftChannelIdentifier.create("legacy", "redisbungee"), MinecraftChannelIdentifier.create("legacy", "redisbungee"),
new LegacyChannelIdentifier("RedisBungee"), new LegacyChannelIdentifier("RedisBungee"),
// This is needed for clients before 1.13 // This is needed for clients before 1.13
new LegacyChannelIdentifier("legacy:redisbungee") new LegacyChannelIdentifier("legacy:redisbungee"));
);
private VelocityCommandManager commandManager; private VelocityCommandManager commandManager;
@Inject @Inject
public RedisBungeeVelocityPlugin(ProxyServer server, Logger logger, @DataDirectory Path dataDirectory) { public RedisBungeeVelocityPlugin(
ProxyServer server, Logger logger, @DataDirectory Path dataDirectory) {
this.server = server; this.server = server;
this.logger = logger; this.logger = logger;
this.dataFolder = dataDirectory; this.dataFolder = dataDirectory;
@ -104,7 +109,8 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
} }
this.api = new RedisBungeeAPI(this); this.api = new RedisBungeeAPI(this);
InitialUtils.checkRedisVersion(this); InitialUtils.checkRedisVersion(this);
this.proxyDataManager = new ProxyDataManager(this) { this.proxyDataManager =
new ProxyDataManager(this) {
@Override @Override
public Set<UUID> getLocalOnlineUUIDs() { public Set<UUID> getLocalOnlineUUIDs() {
HashSet<UUID> players = new HashSet<>(); HashSet<UUID> players = new HashSet<>();
@ -114,14 +120,15 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
@Override @Override
protected void handlePlatformCommandExecution(String command) { protected void handlePlatformCommandExecution(String command) {
server.getCommandManager().executeAsync(RedisBungeeCommandSource.getSingleton(), command); server
.getCommandManager()
.executeAsync(RedisBungeeCommandSource.getSingleton(), command);
} }
}; };
this.playerDataManager = new VelocityPlayerDataManager(this); this.playerDataManager = new VelocityPlayerDataManager(this);
uuidTranslator = new UUIDTranslator(this); uuidTranslator = new UUIDTranslator(this);
} }
@Override @Override
public Summoner<?> getSummoner() { public Summoner<?> getSummoner() {
return this.jedisSummoner; return this.jedisSummoner;
@ -147,7 +154,6 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
return this.uuidTranslator; return this.uuidTranslator;
} }
@Override @Override
public void executeAsync(Runnable runnable) { public void executeAsync(Runnable runnable) {
this.getProxy().getScheduler().buildTask(this, runnable).schedule(); this.getProxy().getScheduler().buildTask(this, runnable).schedule();
@ -229,7 +235,10 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
@Override @Override
public String getPlayerServerName(Player player) { public String getPlayerServerName(Player player) {
return player.getCurrentServer().map(serverConnection -> serverConnection.getServerInfo().getName()).orElse(null); return player
.getCurrentServer()
.map(serverConnection -> serverConnection.getServerInfo().getName())
.orElse(null);
} }
@Override @Override
@ -247,8 +256,18 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
logInfo("Initializing RedisBungee....."); logInfo("Initializing RedisBungee.....");
// start heartbeat task // start heartbeat task
// heartbeat and clean up // heartbeat and clean up
this.heartbeatTask = server.getScheduler().buildTask(this, this.proxyDataManager::publishHeartbeat).repeat(Duration.ofSeconds(1)).schedule(); this.heartbeatTask =
this.cleanUpTask = server.getScheduler().buildTask(this, this.proxyDataManager::correctionTask).repeat(Duration.ofSeconds(60)).schedule(); server
.getScheduler()
.buildTask(this, this.proxyDataManager::publishHeartbeat)
.repeat(Duration.ofSeconds(1))
.schedule();
this.cleanUpTask =
server
.getScheduler()
.buildTask(this, this.proxyDataManager::correctionTask)
.repeat(Duration.ofSeconds(60))
.schedule();
server.getEventManager().register(this, this.playerDataManager); server.getEventManager().register(this, this.playerDataManager);
server.getEventManager().register(this, new RedisBungeeListener(this)); server.getEventManager().register(this, new RedisBungeeListener(this));
@ -289,7 +308,8 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
} }
@Override @Override
public void onConfigLoad(RedisBungeeConfiguration configuration, Summoner<?> summoner, RedisBungeeMode mode) { public void onConfigLoad(
RedisBungeeConfiguration configuration, Summoner<?> summoner, RedisBungeeMode mode) {
this.jedisSummoner = summoner; this.jedisSummoner = summoner;
this.configuration = configuration; this.configuration = configuration;
this.redisBungeeMode = mode; this.redisBungeeMode = mode;
@ -305,7 +325,6 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
return this.redisBungeeMode; return this.redisBungeeMode;
} }
@Subscribe(order = PostOrder.FIRST) @Subscribe(order = PostOrder.FIRST)
public void onProxyInitializeEvent(ProxyInitializeEvent event) { public void onProxyInitializeEvent(ProxyInitializeEvent event) {
initialize(); initialize();
@ -316,9 +335,9 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
stop(); stop();
} }
@Override @Override
public IPlayerChangedServerNetworkEvent createPlayerChangedServerNetworkEvent(UUID uuid, String previousServer, String server) { public IPlayerChangedServerNetworkEvent createPlayerChangedServerNetworkEvent(
UUID uuid, String previousServer, String server) {
return new PlayerChangedServerNetworkEvent(uuid, previousServer, server); return new PlayerChangedServerNetworkEvent(uuid, previousServer, server);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee; package com.imaginarycode.minecraft.redisbungee;
import co.aikar.commands.CommandIssuer; import co.aikar.commands.CommandIssuer;
@ -22,5 +21,4 @@ public class VelocityCommandPlatformHelper extends CommandPlatformHelper {
VelocityCommandIssuer vIssuer = (VelocityCommandIssuer) issuer; VelocityCommandIssuer vIssuer = (VelocityCommandIssuer) issuer;
vIssuer.getIssuer().sendMessage(component); vIssuer.getIssuer().sendMessage(component);
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,7 +7,6 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee; package com.imaginarycode.minecraft.redisbungee;
import com.imaginarycode.minecraft.redisbungee.api.PlayerDataManager; import com.imaginarycode.minecraft.redisbungee.api.PlayerDataManager;
@ -23,12 +22,10 @@ import com.velocitypowered.api.event.connection.LoginEvent;
import com.velocitypowered.api.event.connection.PostLoginEvent; import com.velocitypowered.api.event.connection.PostLoginEvent;
import com.velocitypowered.api.event.player.ServerConnectedEvent; import com.velocitypowered.api.event.player.ServerConnectedEvent;
import com.velocitypowered.api.proxy.Player; import com.velocitypowered.api.proxy.Player;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
public class VelocityPlayerDataManager extends PlayerDataManager<Player> { public class VelocityPlayerDataManager extends PlayerDataManager<Player> {
@ -71,7 +68,8 @@ public class VelocityPlayerDataManager extends PlayerDataManager<Player> {
super.playerChangedServer(event.getPlayer().getUniqueId(), oldServer, currentServer); super.playerChangedServer(event.getPlayer().getUniqueId(), oldServer, currentServer);
} }
private final static MiniMessage MINI_MESSAGE_SERIALIZER = MiniMessage.miniMessage(); private static final MiniMessage MINI_MESSAGE_SERIALIZER = MiniMessage.miniMessage();
@Override @Override
public boolean handleSerializedKick(UUID uuid, String serializedMiniMessage) { public boolean handleSerializedKick(UUID uuid, String serializedMiniMessage) {
Player player = plugin.getPlayer(uuid); Player player = plugin.getPlayer(uuid);
@ -96,18 +94,26 @@ public class VelocityPlayerDataManager extends PlayerDataManager<Player> {
public void onLoginEvent(LoginEvent event, Continuation continuation) { public void onLoginEvent(LoginEvent event, Continuation continuation) {
// check if online // check if online
if (getLastOnline(event.getPlayer().getUniqueId()) == 0) { if (getLastOnline(event.getPlayer().getUniqueId()) == 0) {
// because something can go wrong and proxy somehow does not update player data correctly on shutdown // because something can go wrong and proxy somehow does not update player data correctly on
// shutdown
// we have to check proxy if it has the player // we have to check proxy if it has the player
String proxyId = getProxyFor(event.getPlayer().getUniqueId()); String proxyId = getProxyFor(event.getPlayer().getUniqueId());
if (proxyId == null || !plugin.proxyDataManager().isPlayerTrulyOnProxy(proxyId, event.getPlayer().getUniqueId())) { if (proxyId == null
|| !plugin
.proxyDataManager()
.isPlayerTrulyOnProxy(proxyId, event.getPlayer().getUniqueId())) {
continuation.resume(); continuation.resume();
} else { } else {
if (plugin.configuration().kickWhenOnline()) { if (plugin.configuration().kickWhenOnline()) {
kickPlayer(event.getPlayer().getUniqueId(), vplugin.langConfiguration().messages().loggedInFromOtherLocation()); kickPlayer(
event.getPlayer().getUniqueId(),
vplugin.langConfiguration().messages().loggedInFromOtherLocation());
// wait 3 seconds before releasing the event // wait 3 seconds before releasing the event
plugin.executeAsyncAfter(continuation::resume, TimeUnit.SECONDS, 3); plugin.executeAsyncAfter(continuation::resume, TimeUnit.SECONDS, 3);
} else { } else {
event.setResult(ResultedEvent.ComponentResult.denied(vplugin.langConfiguration().messages().alreadyLoggedIn())); event.setResult(
ResultedEvent.ComponentResult.denied(
vplugin.langConfiguration().messages().alreadyLoggedIn()));
continuation.resume(); continuation.resume();
} }
} }
@ -118,12 +124,16 @@ public class VelocityPlayerDataManager extends PlayerDataManager<Player> {
@Subscribe @Subscribe
public void onLoginEvent(PostLoginEvent event) { public void onLoginEvent(PostLoginEvent event) {
addPlayer(event.getPlayer().getUniqueId(), event.getPlayer().getUsername(), event.getPlayer().getRemoteAddress().getAddress()); addPlayer(
event.getPlayer().getUniqueId(),
event.getPlayer().getUsername(),
event.getPlayer().getRemoteAddress().getAddress());
} }
@Subscribe @Subscribe
public void onDisconnectEvent(DisconnectEvent event) { public void onDisconnectEvent(DisconnectEvent event) {
if (event.getLoginStatus() == DisconnectEvent.LoginStatus.SUCCESSFUL_LOGIN || event.getLoginStatus() == DisconnectEvent.LoginStatus.PRE_SERVER_JOIN) { if (event.getLoginStatus() == DisconnectEvent.LoginStatus.SUCCESSFUL_LOGIN
|| event.getLoginStatus() == DisconnectEvent.LoginStatus.PRE_SERVER_JOIN) {
removePlayer(event.getPlayer().getUniqueId()); removePlayer(event.getPlayer().getUniqueId());
} }
} }

View File

@ -3,6 +3,9 @@ plugins {
`maven-publish` `maven-publish`
} }
version = property("redisbungee-version").toString()
group = property("redisbungee-group").toString()
dependencies { dependencies {
api(project(":RedisBungee-API")) { api(project(":RedisBungee-API")) {
// Since velocity already includes guava / configurate / guava exlude them // Since velocity already includes guava / configurate / guava exlude them

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,13 +7,11 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee; package com.imaginarycode.minecraft.redisbungee;
import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.ProxyServer;
import net.kyori.adventure.text.Component;
import java.util.UUID; import java.util.UUID;
import net.kyori.adventure.text.Component;
// this class used to redirect calls to keep the implementation and api separate // this class used to redirect calls to keep the implementation and api separate
public interface ApiPlatformSupport { public interface ApiPlatformSupport {
@ -21,5 +19,4 @@ public interface ApiPlatformSupport {
ProxyServer getProxy(); ProxyServer getProxy();
void kickPlayer(UUID player, Component message); void kickPlayer(UUID player, Component message);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,21 +7,20 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee; package com.imaginarycode.minecraft.redisbungee;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin; import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.velocitypowered.api.proxy.server.RegisteredServer; import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo; import com.velocitypowered.api.proxy.server.ServerInfo;
import java.util.UUID;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.UUID;
/** /**
* This platform class exposes some internal RedisBungee functions. You obtain an instance of this object by invoking {@link RedisBungeeAPI#getRedisBungeeApi()} * This platform class exposes some internal RedisBungee functions. You obtain an instance of this
* or somehow you got the Plugin instance by you can call the api using {@link RedisBungeePlugin#getAbstractRedisBungeeApi()}. * object by invoking {@link RedisBungeeAPI#getRedisBungeeApi()} or somehow you got the Plugin
* instance by you can call the api using {@link RedisBungeePlugin#getAbstractRedisBungeeApi()}.
* *
* @author tuxed * @author tuxed
* @since 0.2.3 * @since 0.2.3
@ -35,12 +34,11 @@ public class RedisBungeeAPI extends AbstractRedisBungeeAPI {
if (redisBungeeApi == null) { if (redisBungeeApi == null) {
redisBungeeApi = this; redisBungeeApi = this;
} }
} }
/** /**
* Get the server where the specified player is playing. This function also deals with the case of local players * Get the server where the specified player is playing. This function also deals with the case of
* as well, and will return local information on them. * local players as well, and will return local information on them.
* *
* @param player a player uuid * @param player a player uuid
* @return {@link ServerInfo} Can be null if proxy can't find it. * @return {@link ServerInfo} Can be null if proxy can't find it.
@ -50,12 +48,15 @@ public class RedisBungeeAPI extends AbstractRedisBungeeAPI {
public final ServerInfo getServerFor(@NonNull UUID player) { public final ServerInfo getServerFor(@NonNull UUID player) {
String serverName = this.getServerNameFor(player); String serverName = this.getServerNameFor(player);
if (serverName == null) return null; if (serverName == null) return null;
return ((ApiPlatformSupport) this.plugin).getProxy().getServer(serverName).map((RegisteredServer::getServerInfo)).orElse(null); return ((ApiPlatformSupport) this.plugin)
.getProxy()
.getServer(serverName)
.map((RegisteredServer::getServerInfo))
.orElse(null);
} }
/** /**
* Kicks a player from the network * Kicks a player from the network calls {@link #getUuidFromName(String)} to get uuid
* calls {@link #getUuidFromName(String)} to get uuid
* *
* @param playerName player name * @param playerName player name
* @param message kick message that player will see on kick * @param message kick message that player will see on kick
@ -76,7 +77,6 @@ public class RedisBungeeAPI extends AbstractRedisBungeeAPI {
((ApiPlatformSupport) this.plugin).kickPlayer(playerUUID, message); ((ApiPlatformSupport) this.plugin).kickPlayer(playerUUID, message);
} }
/** /**
* Api instance * Api instance
* *

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,20 +7,17 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.events; package com.imaginarycode.minecraft.redisbungee.events;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerChangedServerNetworkEvent; import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerChangedServerNetworkEvent;
import java.util.UUID; import java.util.UUID;
/** /**
* This event is sent when a player connects to a new server. RedisBungee sends the event only when * This event is sent when a player connects to a new server. RedisBungee sends the event only when
* the proxy the player has been connected to is different than the local proxy. * the proxy the player has been connected to is different than the local proxy.
* <p> *
* This event corresponds to {@link com.velocitypowered.api.event.player.ServerConnectedEvent}, and is fired * <p>This event corresponds to {@link com.velocitypowered.api.event.player.ServerConnectedEvent},
* asynchronously. * and is fired asynchronously.
* *
* @since 0.3.4 * @since 0.3.4
*/ */

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,20 +7,17 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.events; package com.imaginarycode.minecraft.redisbungee.events;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerJoinedNetworkEvent; import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerJoinedNetworkEvent;
import java.util.UUID; import java.util.UUID;
/** /**
* This event is sent when a player joins the network. RedisBungee sends the event only when * This event is sent when a player joins the network. RedisBungee sends the event only when the
* the proxy the player has been connected to is different than the local proxy. * proxy the player has been connected to is different than the local proxy.
* <p> *
* This event corresponds to {@link com.velocitypowered.api.event.connection.PostLoginEvent}, and is fired * <p>This event corresponds to {@link com.velocitypowered.api.event.connection.PostLoginEvent}, and
* asynchronously. * is fired asynchronously.
* *
* @since 0.3.4 * @since 0.3.4
*/ */

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,20 +7,17 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.events; package com.imaginarycode.minecraft.redisbungee.events;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerLeftNetworkEvent; import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerLeftNetworkEvent;
import java.util.UUID; import java.util.UUID;
/** /**
* This event is sent when a player disconnects. RedisBungee sends the event only when * This event is sent when a player disconnects. RedisBungee sends the event only when the proxy the
* the proxy the player has been connected to is different than the local proxy. * player has been connected to is different than the local proxy.
* <p> *
* This event corresponds to {@link com.velocitypowered.api.event.connection.DisconnectEvent}, and is fired * <p>This event corresponds to {@link com.velocitypowered.api.event.connection.DisconnectEvent},
* asynchronously. * and is fired asynchronously.
* *
* @since 0.3.4 * @since 0.3.4
*/ */

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-present RedisBungee contributors * Copyright (c) 2026 RedisBungee contributors
* *
* All rights reserved. This program and the accompanying materials * All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0 * are made available under the terms of the Eclipse Public License v1.0
@ -7,20 +7,17 @@
* *
* http://www.eclipse.org/legal/epl-v10.html * http://www.eclipse.org/legal/epl-v10.html
*/ */
package com.imaginarycode.minecraft.redisbungee.events; package com.imaginarycode.minecraft.redisbungee.events;
import com.imaginarycode.minecraft.redisbungee.api.events.IPubSubMessageEvent; import com.imaginarycode.minecraft.redisbungee.api.events.IPubSubMessageEvent;
/** /**
* This event is posted when a PubSub message is received. * This event is posted when a PubSub message is received.
* <p> *
* <strong>Warning</strong>: This event is fired in a separate thread! * <p><strong>Warning</strong>: This event is fired in a separate thread!
* *
* @since 0.2.6 * @since 0.2.6
*/ */
public class PubSubMessageEvent implements IPubSubMessageEvent { public class PubSubMessageEvent implements IPubSubMessageEvent {
private final String channel; private final String channel;
private final String message; private final String message;

View File

@ -9,10 +9,12 @@ rootProject.name = "ValioBungee"
fun configureProject(name: String) { fun configureProject(name: String) {
val projectName = ":valiobungee-$name" val projectName = ":valiobungee-$name"
include(projectName) configureProject(projectName,name)
project(projectName).projectDir = file(name) }
fun configureProject(name: String, path: String) {
include(name)
project(name).projectDir = file(path)
} }
dependencyResolutionManagement { dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
@ -24,4 +26,15 @@ dependencyResolutionManagement {
} }
} }
// main project stuff
sequenceOf("core", "api").forEach{configureProject(it)} sequenceOf("core", "api").forEach{configureProject(it)}
// RedisBunggee Project
configureProject(":RedisBungee-API", "redisbungee/api")
configureProject(":RedisBungee-Lang", "redisbungee/lang")
configureProject(":RedisBungee-Commands", "redisbungee/commands")
configureProject(":RedisBungee-Bungee", "redisbungee/proxies/bungeecord/bungeecord-api")
configureProject(":RedisBungee-Proxy-Bungee", "redisbungee/proxies/bungeecord")
configureProject(":RedisBungee-Velocity", "redisbungee/proxies/velocity/velocity-api")
configureProject(":RedisBungee-Proxy-Velocity", "redisbungee/proxies/velocity")