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
name: RedisBungee-Bungee
# Destination path
path: proxies/bungeecord/build/libs/*
path: redisbungee/proxies/bungeecord/build/libs/*
- name: Upload Velocity
uses: actions/upload-artifact@v4.4.0
with:
name: RedisBungee-Velocity
path: proxies/velocity/build/libs/*
path: redisbungee/proxies/velocity/build/libs/*
- name: Upload API
uses: actions/upload-artifact@v4.4.0
with:
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> {
var redisBungeeProjects = sequenceOf("RedisBungee-API", "RedisBungee-Lang", "RedisBungee-Commands", "RedisBungee-Bungee", "RedisBungee-Proxy-Bungee", "RedisBungee-Velocity", "RedisBungee-Proxy-Velocity")
java {
removeUnusedImports()
googleJavaFormat()
if (project.name == "valiobungee-api") {
licenseHeaderFile(file("copyright_header.txt"))
} else if (redisBungeeProjects.contains(project.name)) {
licenseHeaderFile(rootProject.file("redisbungee/copyright_header.txt"))
} else {
licenseHeaderFile(rootProject.file("copyright_header.txt"))
}

View File

@ -1,3 +1,6 @@
group=net.limework
version=1.0.0-SNAPSHOT
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
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]
blossom = "net.kyori.blossom:2.2.0"
indragit = "net.kyori.indra.git:4.0.0"
shadow = "com.gradleup.shadow:9.3.1"
spotless = "com.diffplug.spotless:8.2.0"
protobuf = { id = "com.google.protobuf", version.ref = "protobuf-plugin" }
run-velocity = { id = "xyz.jpenilla.run-velocity", version = "2.3.1" }
[libraries]
redisson = "org.redisson:redisson:4.3.0"
# protobuf
protobuf = { group = "com.google.protobuf", name = "protobuf-java", 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
slf4j = { group = "org.slf4j", name = "slf4j-api", version.ref = "slf4j" }
# testing
testing-juipter = "org.junit.jupiter:junit-jupiter:6.0.3"
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 {
`java-library`
`maven-publish`
@ -8,6 +6,10 @@ plugins {
}
version = property("redisbungee-version").toString()
group = property("redisbungee-group").toString()
dependencies {
api(libs.guava)
api(libs.jedis)
@ -22,8 +24,8 @@ sourceSets {
main {
blossom {
javaSources {
property("version", "$version")
property("git-commit", indraGit.commit().toString())
property("version", version.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,13 +1,12 @@
/*
* 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
*/
* 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;
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.RedisBungeePlugin;
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.Nullable;
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.
* 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,
* this will return -1. Otherwise it will return a value in milliseconds.
* 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, this will return -1. Otherwise it will return a value in
* milliseconds.
*
* @param player a player name
* @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
* as well, and will return local information on them.
* Get the server where the specified player is playing. This function also deals with the case of
* local players as well, and will return local information on them.
*
* @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
public final String getServerNameFor(@NonNull UUID player) {
@ -76,8 +76,9 @@ public abstract class AbstractRedisBungeeAPI {
/**
* 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
*/
@ -188,8 +189,8 @@ public abstract class AbstractRedisBungeeAPI {
/**
* 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 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
* 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>
* If performance is a concern, use {@link #getNameFromUuid(java.util.UUID, boolean)} as this allows you to disable Mojang lookups.
* Fetch a name from the specified UUID. UUIDs are cached locally and in Redis. This function
* 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>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
* @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
* as a last resort if {@code expensiveLookups} is true, so calls <strong>may</strong> be blocking.
* <p>
* For the common use case of translating the list of online players into names, use {@link #getHumanPlayersOnline()}.
* <p>
* If performance is a concern, set {@code expensiveLookups} to false as this will disable lookups via Mojang.
* Fetch a name from the specified UUID. UUIDs are cached locally and in Redis. This function can
* fall back to Mojang as a last resort if {@code expensiveLookups} is true, so calls
* <strong>may</strong> be blocking.
*
* <p>For the common use case of translating the list of online players into names, use {@link
* #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 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
* 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:
* Fetch a UUID from the specified name. Names are cached locally and in Redis. This function
* 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:
*
* <ul>
* <li>Searching local entries case-insensitively</li>
* <li>Searching Mojang</li>
* <li>Searching local entries case-insensitively
* <li>Searching Mojang
* </ul>
*
* @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
* as a last resort if {@code expensiveLookups} is true, so calls <strong>may</strong> be blocking.
* <p>
* If performance is a concern, set {@code expensiveLookups} to false to disable searching Mojang and searching for usernames
* case-insensitively.
* Fetch a UUID from the specified name. Names are cached locally and in Redis. This function
* falls back to Mojang as a last resort if {@code expensiveLookups} is true, so calls
* <strong>may</strong> be blocking.
*
* <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 expensiveLookups whether or not to perform potentially expensive lookups
@ -288,9 +297,9 @@ public abstract class AbstractRedisBungeeAPI {
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}
* @since 0.8.0
@ -300,9 +309,9 @@ public abstract class AbstractRedisBungeeAPI {
}
/**
* Kicks a player from the network using miniMessage
* calls {@link #getUuidFromName(String)} to get uuid
* <a href="https://docs.advntr.dev/minimessage/format.html">...</a>
* Kicks a player from the network using miniMessage calls {@link #getUuidFromName(String)} to get
* uuid <a href="https://docs.advntr.dev/minimessage/format.html">...</a>
*
* @param playerName player name
* @param miniMessage kick message that player will see on kick using minimessage as format
* @since 0.13.0
@ -312,8 +321,9 @@ public abstract class AbstractRedisBungeeAPI {
}
/**
* Kicks a player from the network
* <a href="https://docs.advntr.dev/minimessage/format.html">...</a>
* Kicks a player from the network <a
* href="https://docs.advntr.dev/minimessage/format.html">...</a>
*
* @param player player uuid
* @param miniMessage kick message that player will see on kick using minimessage as format
* @since 0.13.0
@ -323,8 +333,8 @@ public abstract class AbstractRedisBungeeAPI {
}
/**
* shows what mode is RedisBungee is on
* Basically what every redis mode is used like cluster or single instance.
* shows what mode is RedisBungee is on Basically what every redis mode is used like cluster or
* single instance.
*
* @return {@link RedisBungeeMode}
* @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,13 +1,12 @@
/*
* 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
*/
* 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;
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.IPubSubMessageEvent;
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 redis.clients.jedis.ClusterPipeline;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.Response;
import redis.clients.jedis.UnifiedJedis;
import java.net.InetAddress;
import java.util.*;
import java.util.concurrent.TimeUnit;
public abstract class PlayerDataManager<P> {
protected final RedisBungeePlugin<P> plugin;
@ -37,12 +35,20 @@ public abstract class PlayerDataManager<P> {
private final UnifiedJedis unifiedJedis;
private final String proxyId;
private final String networkId;
private final LoadingCache<UUID, String> serverCache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build(this::getServerFromRedis);
private final LoadingCache<UUID, String> lastServerCache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build(this::getLastServerFromRedis);
private final LoadingCache<UUID, String> proxyCache = 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);
private final LoadingCache<UUID, String> serverCache =
Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build(this::getServerFromRedis);
private final LoadingCache<UUID, String> lastServerCache =
Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build(this::getLastServerFromRedis);
private final LoadingCache<UUID, String> proxyCache =
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) {
this.plugin = plugin;
@ -72,7 +78,8 @@ public abstract class PlayerDataManager<P> {
this.serverCache.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);
}
@ -82,7 +89,8 @@ public abstract class PlayerDataManager<P> {
this.ipCache.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);
}
@ -92,11 +100,11 @@ public abstract class PlayerDataManager<P> {
this.ipCache.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);
}
protected void handlePubSubMessageEvent(IPubSubMessageEvent event) {
switch (event.getChannel()) {
case "redisbungee-serverchange" -> {
@ -124,7 +132,6 @@ public abstract class PlayerDataManager<P> {
handleSerializedKick(uuid, message);
}
}
}
protected void playerChangedServer(UUID uuid, String from, String to) {
@ -178,8 +185,12 @@ public abstract class PlayerDataManager<P> {
}
protected void removePlayer(UUID uuid) {
unifiedJedis.hset("redisbungee::" + this.networkId + "::player::" + uuid + "::data", "last-online", String.valueOf(System.currentTimeMillis()));
unifiedJedis.hdel("redisbungee::" + this.networkId + "::player::" + uuid + "::data", "server", "proxy", "ip");
unifiedJedis.hset(
"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();
data.put("proxy", this.proxyId);
data.put("uuid", uuid);
@ -188,27 +199,32 @@ public abstract class PlayerDataManager<P> {
this.plugin.proxyDataManager().removePlayer(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) {
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) {
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) {
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;
return InetAddresses.forString(ip);
}
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;
return Long.parseLong(unixString);
}
@ -247,12 +263,17 @@ public abstract class PlayerDataManager<P> {
public Multimap<String, UUID> doPooledPipeline(Pipeline pipeline) {
HashMap<UUID, Response<String>> responses = new HashMap<>();
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);
});
}
pipeline.sync();
responses.forEach((uuid, response) -> {
responses.forEach(
(uuid, response) -> {
String key = response.get();
if (key == null) return;
@ -265,12 +286,17 @@ public abstract class PlayerDataManager<P> {
public Multimap<String, UUID> clusterPipeline(ClusterPipeline pipeline) {
HashMap<UUID, Response<String>> responses = new HashMap<>();
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);
});
}
pipeline.sync();
responses.forEach((uuid, response) -> {
responses.forEach(
(uuid, response) -> {
String key = response.get();
if (key == null) return;
builder.put(key, uuid);
@ -282,5 +308,4 @@ public abstract class PlayerDataManager<P> {
throw new RuntimeException(e);
}
}
}

View File

@ -1,15 +1,16 @@
/*
* 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
*/
* 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;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
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.tasks.RedisPipelineTask;
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.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import static com.google.common.base.Preconditions.checkArgument;
import redis.clients.jedis.*;
import redis.clients.jedis.params.XAddParams;
import redis.clients.jedis.params.XReadParams;
import redis.clients.jedis.resps.StreamEntry;
public abstract class ProxyDataManager implements Runnable {
@ -47,7 +45,8 @@ public abstract class ProxyDataManager implements Runnable {
// data:
// 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;
@ -60,7 +59,14 @@ public abstract class ProxyDataManager implements Runnable {
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) {
this.plugin = plugin;
@ -85,10 +91,11 @@ public abstract class ProxyDataManager implements Runnable {
// this skip checking if proxy is and its package private
// due proxy shutdown shenanigans
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() {
return Collections.list(this.heartbeats.keys());
}
@ -109,7 +116,9 @@ public abstract class ProxyDataManager implements Runnable {
// call every 1 second
public synchronized void publishHeartbeat() {
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);
publishPayload(new HeartbeatPayload(this.proxyId, heartbeatData));
}
@ -121,7 +130,9 @@ public abstract class ProxyDataManager implements Runnable {
public Set<UUID> doPooledPipeline(Pipeline pipeline) {
HashSet<Response<Set<String>>> responses = new HashSet<>();
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();
HashSet<UUID> uuids = new HashSet<>();
@ -137,7 +148,9 @@ public abstract class ProxyDataManager implements Runnable {
public Set<UUID> clusterPipeline(ClusterPipeline pipeline) {
HashSet<Response<Set<String>>> responses = new HashSet<>();
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();
HashSet<UUID> uuids = new HashSet<>();
@ -152,7 +165,6 @@ public abstract class ProxyDataManager implements Runnable {
} catch (Exception e) {
throw new RuntimeException("unable to get network players", e);
}
}
public int totalNetworkPlayers() {
@ -179,10 +191,10 @@ public abstract class ProxyDataManager implements Runnable {
data.put("payload", gson.toJson(payload));
data.put("data-manager-uuid", this.dataManagerUUID.toString());
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) {
String id = payload.senderProxy();
if (!heartbeats.containsKey(id)) {
@ -191,7 +203,6 @@ public abstract class ProxyDataManager implements Runnable {
heartbeats.put(id, payload.data());
}
// call every 1 minutes
public void correctionTask() {
// 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);
}
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 {
new RedisPipelineTask<Void>(plugin) {
@ -222,8 +234,12 @@ public abstract class ProxyDataManager implements Runnable {
for (UUID uuid : add) {
addString.add(uuid.toString());
}
pipeline.srem("redisbungee::" + networkId + "::proxies::" + proxyId + "::online-players", removeString.toArray(new String[]{}));
pipeline.sadd("redisbungee::" + networkId + "::proxies::" + proxyId + "::online-players", addString.toArray(new String[]{}));
pipeline.srem(
"redisbungee::" + networkId + "::proxies::" + proxyId + "::online-players",
removeString.toArray(new String[] {}));
pipeline.sadd(
"redisbungee::" + networkId + "::proxies::" + proxyId + "::online-players",
addString.toArray(new String[] {}));
pipeline.sync();
return null;
}
@ -238,8 +254,12 @@ public abstract class ProxyDataManager implements Runnable {
for (UUID uuid : add) {
addString.add(uuid.toString());
}
pipeline.srem("redisbungee::" + networkId + "::proxies::" + proxyId + "::online-players", removeString.toArray(new String[]{}));
pipeline.sadd("redisbungee::" + networkId + "::proxies::" + proxyId + "::online-players", addString.toArray(new String[]{}));
pipeline.srem(
"redisbungee::" + networkId + "::proxies::" + proxyId + "::online-players",
removeString.toArray(new String[] {}));
pipeline.sadd(
"redisbungee::" + networkId + "::proxies::" + proxyId + "::online-players",
addString.toArray(new String[] {}));
pipeline.sync();
return null;
}
@ -250,10 +270,11 @@ public abstract class ProxyDataManager implements Runnable {
plugin.logInfo("Player set has been corrected!");
}
// handle dead proxies "THAT" Didn't send death payload but considered dead due TIMEOUT ~30 seconds
// handle dead proxies "THAT" Didn't send death payload but considered dead due TIMEOUT ~30
// seconds
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();
long heartbeat = stringHeartbeatDataEntry.getValue().heartbeat();
if (Instant.now().getEpochSecond() - heartbeat > RedisUtil.PROXY_TIMEOUT) {
@ -266,7 +287,8 @@ public abstract class ProxyDataManager implements Runnable {
@Override
public Void doPooledPipeline(Pipeline pipeline) {
for (String deadProxy : deadProxies) {
pipeline.del("redisbungee::" + networkId + "::proxies::" + deadProxy + "::online-players");
pipeline.del(
"redisbungee::" + networkId + "::proxies::" + deadProxy + "::online-players");
}
pipeline.sync();
return null;
@ -275,7 +297,8 @@ public abstract class ProxyDataManager implements Runnable {
@Override
public Void clusterPipeline(ClusterPipeline pipeline) {
for (String deadProxy : deadProxies) {
pipeline.del("redisbungee::" + networkId + "::proxies::" + deadProxy + "::online-players");
pipeline.del(
"redisbungee::" + networkId + "::proxies::" + deadProxy + "::online-players");
}
pipeline.sync();
return null;
@ -294,7 +317,8 @@ public abstract class ProxyDataManager implements Runnable {
if (id.equals(this.proxyId())) {
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);
plugin.logInfo("Proxy {} has disconnected", id);
}
@ -315,21 +339,27 @@ public abstract class ProxyDataManager implements Runnable {
}
}
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) {
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() {
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) {
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<>();
for (String proxyMember : uuidsStrings) {
uuids.add(UUID.fromString(proxyMember));
@ -344,19 +374,28 @@ public abstract class ProxyDataManager implements Runnable {
public void run() {
while (!isClosed()) {
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 (StreamEntry streamEntry : datum.getValue()) {
this.lastStreamEntryID = streamEntry.getID();
String payloadData = streamEntry.getFields().get("payload");
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 (!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;
}

View File

@ -1,15 +1,15 @@
/*
* 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
*/
* 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;
public enum RedisBungeeMode {
SINGLE, CLUSTER
SINGLE,
CLUSTER
}

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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;
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.summoners.Summoner;
import com.imaginarycode.minecraft.redisbungee.api.util.uuid.UUIDTranslator;
import java.net.InetAddress;
import java.util.UUID;
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
* <p>
* Reason this is interface because some proxies implementations require the user to extend class for plugins for example bungeecord.
* 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
*
* <p>Reason this is interface because some proxies implementations require the user to extend class
* for plugins for example bungeecord.
*
* @author Ham1255
* @since 0.7.0
*/
public interface RedisBungeePlugin<P> extends EventsPlatform {
default void initialize() {
default void initialize() {}
}
default void stop() {
}
default void stop() {}
void logInfo(String msg);

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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.config;
public enum HandleMotdOrder {

View File

@ -1,18 +1,16 @@
/*
* 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
*/
* 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.config;
import com.google.common.collect.ImmutableList;
import com.google.common.net.InetAddresses;
import java.net.InetAddress;
import java.util.List;
@ -29,8 +27,15 @@ public class RedisBungeeConfiguration {
private final CommandsConfiguration commandsConfiguration;
private final String networkId;
public RedisBungeeConfiguration(String networkId, String proxyId, List<String> exemptAddresses, boolean kickWhenOnline, boolean handleReconnectToLastServer, boolean handleMotd, HandleMotdOrder handleMotdOrder, CommandsConfiguration commandsConfiguration) {
public RedisBungeeConfiguration(
String networkId,
String proxyId,
List<String> exemptAddresses,
boolean kickWhenOnline,
boolean handleReconnectToLastServer,
boolean handleMotd,
HandleMotdOrder handleMotdOrder,
CommandsConfiguration commandsConfiguration) {
this.proxyId = proxyId;
ImmutableList.Builder<InetAddress> addressBuilder = ImmutableList.builder();
for (String s : exemptAddresses) {
@ -69,19 +74,30 @@ public class RedisBungeeConfiguration {
return this.handleReconnectToLastServer;
}
public record CommandsConfiguration(boolean redisbungeeEnabled, boolean redisbungeeLegacyEnabled,
LegacySubCommandsConfiguration legacySubCommandsConfiguration) {
public record CommandsConfiguration(
boolean redisbungeeEnabled,
boolean redisbungeeLegacyEnabled,
LegacySubCommandsConfiguration legacySubCommandsConfiguration) {}
}
public record LegacySubCommandsConfiguration(boolean findEnabled, boolean glistEnabled, boolean ipEnabled,
boolean lastseenEnabled, boolean plistEnabled, boolean pproxyEnabled,
boolean sendtoallEnabled, boolean serveridEnabled,
boolean serveridsEnabled, boolean installFind, boolean installGlist, boolean installIp,
boolean installLastseen, boolean installPlist, boolean installPproxy,
boolean installSendtoall, boolean installServerid,
boolean installServerids) {
}
public record LegacySubCommandsConfiguration(
boolean findEnabled,
boolean glistEnabled,
boolean ipEnabled,
boolean lastseenEnabled,
boolean plistEnabled,
boolean pproxyEnabled,
boolean sendtoallEnabled,
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() {
return commandsConfiguration;

View File

@ -1,16 +1,14 @@
/*
* 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
*/
* 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.config.loaders;
import com.google.common.reflect.TypeToken;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeeMode;
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.JedisPooledSummoner;
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.objectmapping.ObjectMappingException;
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.PooledConnectionProvider;
import java.io.IOException;
import java.nio.file.Path;
import java.util.*;
public interface ConfigLoader extends GenericConfigLoader {
int CONFIG_VERSION = 2;
default void loadConfig(RedisBungeePlugin<?> plugin, Path dataFolder) throws IOException {
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();
if (node.getNode("config-version").getInt(0) != CONFIG_VERSION) {
handleOldConfig(dataFolder, "config.yml", "config.yml");
@ -69,13 +67,19 @@ public interface ConfigLoader extends GenericConfigLoader {
// env var
String proxyIdFromEnv = System.getenv("REDISBUNGEE_PROXY_ID");
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;
}
String networkIdFromEnv = System.getenv("REDISBUNGEE_NETWORK_ID");
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;
}
@ -112,47 +116,107 @@ public interface ConfigLoader extends GenericConfigLoader {
try {
handleMotdOrder = HandleMotdOrder.valueOf(handleMotdOrderName.toUpperCase(Locale.ROOT));
} 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);
// commands
boolean redisBungeeEnabled = node.getNode("commands", "redisbungee", "enabled").getBoolean(true);
boolean redisBungeeLegacyEnabled =node.getNode("commands", "redisbungee-legacy", "enabled").getBoolean(false);
boolean redisBungeeEnabled =
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 findEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "find", "enabled").getBoolean(false);
boolean lastseenEnabled = node.getNode("commands", "redisbungee-legacy", "subcommands", "lastseen", "enabled").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 glistEnabled =
node.getNode("commands", "redisbungee-legacy", "subcommands", "glist", "enabled")
.getBoolean(false);
boolean findEnabled =
node.getNode("commands", "redisbungee-legacy", "subcommands", "find", "enabled")
.getBoolean(false);
boolean lastseenEnabled =
node.getNode("commands", "redisbungee-legacy", "subcommands", "lastseen", "enabled")
.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 installFind = node.getNode("commands", "redisbungee-legacy", "subcommands", "find", "install").getBoolean(false);
boolean installLastseen = node.getNode("commands", "redisbungee-legacy", "subcommands", "lastseen", "install").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);
boolean installGlist =
node.getNode("commands", "redisbungee-legacy", "subcommands", "glist", "install")
.getBoolean(false);
boolean installFind =
node.getNode("commands", "redisbungee-legacy", "subcommands", "find", "install")
.getBoolean(false);
boolean installLastseen =
node.getNode("commands", "redisbungee-legacy", "subcommands", "lastseen", "install")
.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 = new RedisBungeeConfiguration(networkId, proxyId, exemptAddresses, kickWhenOnline, reconnectToLastServer, handleMotd, handleMotdOrder,
RedisBungeeConfiguration configuration =
new RedisBungeeConfiguration(
networkId,
proxyId,
exemptAddresses,
kickWhenOnline,
reconnectToLastServer,
handleMotd,
handleMotdOrder,
new RedisBungeeConfiguration.CommandsConfiguration(
redisBungeeEnabled, redisBungeeLegacyEnabled,
redisBungeeEnabled,
redisBungeeLegacyEnabled,
new RedisBungeeConfiguration.LegacySubCommandsConfiguration(
findEnabled, glistEnabled, ipEnabled,
lastseenEnabled, pListEnabled, pproxyEnabled,
sendToAllEnabled, serverIdEnabled, serverIdsEnabled,
installFind, installGlist, installIp,
installLastseen, installPlist, installPproxy,
installSendToAll, installServerid, installServerIds)
));
findEnabled,
glistEnabled,
ipEnabled,
lastseenEnabled,
pListEnabled,
pproxyEnabled,
sendToAllEnabled,
serverIdEnabled,
serverIdsEnabled,
installFind,
installGlist,
installIp,
installLastseen,
installPlist,
installPproxy,
installSendToAll,
installServerid,
installServerIds)));
Summoner<?> summoner;
RedisBungeeMode redisBungeeMode;
if (useSSL) {
@ -164,7 +228,10 @@ public interface ConfigLoader extends GenericConfigLoader {
GenericObjectPoolConfig<Connection> poolConfig = new GenericObjectPoolConfig<>();
poolConfig.setMaxTotal(maxConnections);
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();
String host = hostAndPort.get("host").getString();
int port = hostAndPort.get("port").getInt();
@ -174,7 +241,18 @@ public interface ConfigLoader extends GenericConfigLoader {
if (hostAndPortSet.isEmpty()) {
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;
} else {
plugin.logInfo("RedisBungee MODE: SINGLE");
@ -188,20 +266,33 @@ public interface ConfigLoader extends GenericConfigLoader {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(node.getNode("compatibility-max-connections").getInt(3));
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");
}
GenericObjectPoolConfig<Connection> poolConfig = new GenericObjectPoolConfig<>();
poolConfig.setMaxTotal(maxConnections);
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;
}
plugin.logInfo("Successfully connected to Redis.");
onConfigLoad(configuration, summoner, redisBungeeMode);
}
void onConfigLoad(RedisBungeeConfiguration configuration, Summoner<?> summoner, RedisBungeeMode mode);
void onConfigLoad(
RedisBungeeConfiguration configuration, Summoner<?> summoner, RedisBungeeMode mode);
}

View File

@ -1,31 +1,29 @@
/*
* 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
*/
* 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.config.loaders;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.time.Instant;
import org.jetbrains.annotations.Nullable;
public interface GenericConfigLoader {
// CHANGES on every reboot
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)) {
Files.createDirectory(dataFolder);
}
@ -40,7 +38,8 @@ public interface GenericConfigLoader {
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");
if (Files.notExists(oldConfigFolder)) {
Files.createDirectory(oldConfigFolder);
@ -54,5 +53,4 @@ public interface GenericConfigLoader {
Files.move(oldConfigPath, randomStoreConfigDirectory.resolve(configFile));
createConfigFile(dataFolder, configFile, defaultResourceID);
}
}

View File

@ -1,26 +1,27 @@
/*
* 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
*/
* 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.events;
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
* @since 0.7.0
*/
public interface EventsPlatform {
IPlayerChangedServerNetworkEvent createPlayerChangedServerNetworkEvent(UUID uuid, String previousServer, String server);
IPlayerChangedServerNetworkEvent createPlayerChangedServerNetworkEvent(
UUID uuid, String previousServer, String server);
IPlayerJoinedNetworkEvent createPlayerJoinedNetworkEvent(UUID uuid);
@ -29,5 +30,4 @@ public interface EventsPlatform {
IPubSubMessageEvent createPubSubEvent(String channel, String message);
void fireEvent(Object event);
}

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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.events;
import java.util.UUID;
@ -19,5 +18,4 @@ public interface IPlayerChangedServerNetworkEvent extends RedisBungeeEvent {
String getServer();
String getPreviousServer();
}

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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.events;
import java.util.UUID;
@ -15,5 +14,4 @@ import java.util.UUID;
public interface IPlayerJoinedNetworkEvent extends RedisBungeeEvent {
UUID getUuid();
}

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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.events;
import java.util.UUID;
@ -15,5 +14,4 @@ import java.util.UUID;
public interface IPlayerLeftNetworkEvent extends RedisBungeeEvent {
UUID getUuid();
}

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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.events;
public interface IPubSubMessageEvent extends RedisBungeeEvent {
@ -15,6 +14,4 @@ public interface IPubSubMessageEvent extends RedisBungeeEvent {
String getChannel();
String getMessage();
}

View File

@ -1,14 +1,12 @@
/*
* 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
*/
* 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.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;
public abstract class AbstractPayload {
@ -20,5 +28,4 @@ public abstract class AbstractPayload {
public String getClassName() {
return getClass().getName();
}
}

View File

@ -1,32 +1,33 @@
/*
* 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
*/
* 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.gson;
import com.google.gson.*;
import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload;
import java.lang.reflect.Type;
public class AbstractPayloadSerializer implements JsonSerializer<AbstractPayload>, JsonDeserializer<AbstractPayload> {
public class AbstractPayloadSerializer
implements JsonSerializer<AbstractPayload>, JsonDeserializer<AbstractPayload> {
@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();
return new AbstractPayload(jsonObject.get("proxy").getAsString(), jsonObject.get("class").getAsString()) {
};
return new AbstractPayload(
jsonObject.get("proxy").getAsString(), jsonObject.get("class").getAsString()) {};
}
@Override
public JsonElement serialize(AbstractPayload src, Type typeOfSrc, JsonSerializationContext context) {
public JsonElement serialize(
AbstractPayload src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject();
jsonObject.add("proxy", new JsonPrimitive(src.senderProxy()));
return jsonObject;

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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.proxy;
import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload;

View File

@ -1,22 +1,19 @@
/*
* 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
*/
* 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.proxy;
import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload;
public class HeartbeatPayload extends AbstractPayload {
public record HeartbeatData(long heartbeat, int players) {
}
public record HeartbeatData(long heartbeat, int players) {}
private final HeartbeatData data;

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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.proxy;
import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload;
@ -17,7 +16,6 @@ public class PubSubPayload extends AbstractPayload {
private final String channel;
private final String message;
public PubSubPayload(String proxyId, String channel, String message) {
super(proxyId);
this.channel = channel;

View File

@ -1,25 +1,22 @@
/*
* 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
*/
* 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.proxy;
import com.imaginarycode.minecraft.redisbungee.api.payloads.AbstractPayload;
public class RunCommandPayload extends AbstractPayload {
private final String proxyToRun;
private final String command;
public RunCommandPayload(String proxyId, String proxyToRun, String command) {
super(proxyId);
this.proxyToRun = proxyToRun;

View File

@ -1,27 +1,27 @@
/*
* 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
*/
* 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.proxy.gson;
import com.google.gson.*;
import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.DeathPayload;
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();
@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();
String senderProxy = jsonObject.get("proxy").getAsString();
return new DeathPayload(senderProxy);

View File

@ -1,34 +1,36 @@
/*
* 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
*/
* 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.proxy.gson;
import com.google.gson.*;
import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.HeartbeatPayload;
import java.lang.reflect.Type;
public class HeartbeatPayloadSerializer implements JsonSerializer<HeartbeatPayload>, JsonDeserializer<HeartbeatPayload> {
public class HeartbeatPayloadSerializer
implements JsonSerializer<HeartbeatPayload>, JsonDeserializer<HeartbeatPayload> {
@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();
String senderProxy = jsonObject.get("proxy").getAsString();
long heartbeat = jsonObject.get("heartbeat").getAsLong();
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
public JsonElement serialize(HeartbeatPayload src, Type typeOfSrc, JsonSerializationContext context) {
public JsonElement serialize(
HeartbeatPayload src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject();
jsonObject.add("proxy", new JsonPrimitive(src.senderProxy()));
jsonObject.add("heartbeat", new JsonPrimitive(src.data().heartbeat()));

View File

@ -1,27 +1,27 @@
/*
* 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
*/
* 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.proxy.gson;
import com.google.gson.*;
import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.PubSubPayload;
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();
@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();
String senderProxy = jsonObject.get("proxy").getAsString();
String channel = jsonObject.get("channel").getAsString();
@ -30,7 +30,8 @@ public class PubSubPayloadSerializer implements JsonSerializer<PubSubPayload>, J
}
@Override
public JsonElement serialize(PubSubPayload src, Type typeOfSrc, JsonSerializationContext context) {
public JsonElement serialize(
PubSubPayload src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject();
jsonObject.add("proxy", new JsonPrimitive(src.senderProxy()));
jsonObject.add("channel", new JsonPrimitive(src.channel()));

View File

@ -1,25 +1,25 @@
/*
* 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
*/
* 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.proxy.gson;
import com.google.gson.*;
import com.imaginarycode.minecraft.redisbungee.api.payloads.proxy.RunCommandPayload;
import java.lang.reflect.Type;
public class RunCommandPayloadSerializer implements JsonSerializer<RunCommandPayload>, JsonDeserializer<RunCommandPayload> {
public class RunCommandPayloadSerializer
implements JsonSerializer<RunCommandPayload>, JsonDeserializer<RunCommandPayload> {
@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();
String senderProxy = jsonObject.get("proxy").getAsString();
String proxyToRun = jsonObject.get("proxy-to-run").getAsString();
@ -28,7 +28,8 @@ public class RunCommandPayloadSerializer implements JsonSerializer<RunCommandPay
}
@Override
public JsonElement serialize(RunCommandPayload src, Type typeOfSrc, JsonSerializationContext context) {
public JsonElement serialize(
RunCommandPayload src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject jsonObject = new JsonObject();
jsonObject.add("proxy", new JsonPrimitive(src.senderProxy()));
jsonObject.add("proxy-to-run", new JsonPrimitive(src.proxyToRun()));

View File

@ -1,20 +1,18 @@
/*
* 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
*/
* 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.summoners;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.providers.ClusterConnectionProvider;
import java.io.IOException;
import java.time.Duration;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.providers.ClusterConnectionProvider;
public class JedisClusterSummoner implements Summoner<JedisCluster> {
private final ClusterConnectionProvider clusterConnectionProvider;
@ -27,7 +25,6 @@ public class JedisClusterSummoner implements Summoner<JedisCluster> {
jedisCluster.del("random_data");
}
@Override
public void close() throws IOException {
this.clusterConnectionProvider.close();
@ -37,6 +34,4 @@ public class JedisClusterSummoner implements Summoner<JedisCluster> {
public JedisCluster obtainResource() {
return new NotClosableJedisCluster(this.clusterConnectionProvider, 60, Duration.ofSeconds(10));
}
}

View File

@ -1,22 +1,20 @@
/*
* 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
*/
* 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.summoners;
import java.io.IOException;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPooled;
import redis.clients.jedis.providers.PooledConnectionProvider;
import java.io.IOException;
public class JedisPooledSummoner implements Summoner<JedisPooled> {
private final PooledConnectionProvider connectionProvider;
@ -31,12 +29,10 @@ public class JedisPooledSummoner implements Summoner<JedisPooled> {
// Test the connection to make sure configuration is right
jedis.ping();
}
}
final JedisPooled jedisPooled = this.obtainResource();
jedisPooled.set("random_data", "0");
jedisPooled.del("random_data");
}
@Override
@ -55,6 +51,5 @@ public class JedisPooledSummoner implements Summoner<JedisPooled> {
this.jedisPool.close();
}
this.connectionProvider.close();
}
}

View File

@ -1,29 +1,25 @@
/*
* 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
*/
* 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.summoners;
import java.time.Duration;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.providers.ClusterConnectionProvider;
import java.time.Duration;
public class NotClosableJedisCluster extends JedisCluster {
NotClosableJedisCluster(ClusterConnectionProvider provider, int maxAttempts, Duration maxTotalRetriesDuration) {
NotClosableJedisCluster(
ClusterConnectionProvider provider, int maxAttempts, Duration maxTotalRetriesDuration) {
super(provider, maxAttempts, maxTotalRetriesDuration);
}
@Override
public void close() {
}
public void close() {}
}

View File

@ -1,26 +1,22 @@
/*
* 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
*/
* 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.summoners;
import redis.clients.jedis.JedisPooled;
import redis.clients.jedis.providers.PooledConnectionProvider;
public class NotClosableJedisPooled extends JedisPooled {
NotClosableJedisPooled(PooledConnectionProvider provider) {
super(provider);
}
@Override
public void close() {
}
public void close() {}
}

View File

@ -1,19 +1,16 @@
/*
* 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
*/
* 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.summoners;
import redis.clients.jedis.UnifiedJedis;
import java.io.Closeable;
import redis.clients.jedis.UnifiedJedis;
/**
* 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 {
P obtainResource();
}

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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.tasks;
import com.imaginarycode.minecraft.redisbungee.AbstractRedisBungeeAPI;
@ -16,7 +15,6 @@ import redis.clients.jedis.*;
public abstract class RedisPipelineTask<T> extends RedisTask<T> {
public RedisPipelineTask(AbstractRedisBungeeAPI api) {
super(api);
}
@ -25,7 +23,6 @@ public abstract class RedisPipelineTask<T> extends RedisTask<T> {
super(plugin);
}
@Override
public T unifiedJedisTask(UnifiedJedis unifiedJedis) {
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 clusterPipeline(ClusterPipeline pipeline);
}

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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.tasks;
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.JedisPooledSummoner;
import com.imaginarycode.minecraft.redisbungee.api.summoners.Summoner;
import java.util.concurrent.Callable;
import redis.clients.jedis.UnifiedJedis;
import java.util.concurrent.Callable;
/**
* Since Jedis now have UnifiedJedis which basically extended by cluster / single connections classes
* can help us to have shared code.
* Since Jedis now have UnifiedJedis which basically extended by cluster / single connections
* classes can help us to have shared code.
*/
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() {
// 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) {
JedisPooledSummoner jedisSummoner = (JedisPooledSummoner) summoner;
return this.unifiedJedisTask(jedisSummoner.obtainResource());
@ -63,5 +62,4 @@ public abstract class RedisTask<V> implements Runnable, Callable<V> {
}
return null;
}
}

View File

@ -1,25 +1,22 @@
/*
* 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
*/
* 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.tasks;
import com.google.gson.Gson;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.api.util.uuid.CachedUUIDEntry;
import java.util.ArrayList;
import redis.clients.jedis.UnifiedJedis;
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 RedisBungeePlugin<?> plugin;
@ -36,7 +33,10 @@ public class UUIDCleanupTask extends RedisTask<Void>{
final long number = unifiedJedis.hlen("uuid-cache");
plugin.logInfo("Found {} entries", number);
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);
if (cachedUUIDEntry.expired()) {
fieldsToRemove.add(field);
@ -51,6 +51,4 @@ public class UUIDCleanupTask extends RedisTask<Void>{
}
return null;
}
}

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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;
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.UnifiedJedis;
public class InitialUtils {
public static void checkRedisVersion(RedisBungeePlugin<?> plugin) {
@ -29,12 +27,20 @@ public class InitialUtils {
String version = s.split(":")[1];
plugin.logInfo("Redis server version: " + 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");
}
long uuidCacheSize = unifiedJedis.hlen("uuid-cache");
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;
}
@ -43,6 +49,4 @@ public class InitialUtils {
}
}.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;
import com.google.common.annotations.VisibleForTesting;
@VisibleForTesting
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 MINOR_VERSION = 2;
@ -19,7 +28,6 @@ public class RedisUtil {
if (major > MAJOR_VERSION) return true;
return major == MAJOR_VERSION && minor >= MINOR_VERSION;
}
// 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;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multiset;
import com.google.common.io.ByteArrayDataOutput;
import java.util.Collection;
import java.util.Map;
@ -18,7 +26,8 @@ public class MultiMapSerialization {
}
@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());
for (Map.Entry<String, Collection<String>> entry : collection.asMap().entrySet()) {
output.writeUTF(entry.getKey());
@ -36,5 +45,4 @@ public class MultiMapSerialization {
output.writeUTF(o.toString());
}
}
}

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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.uuid;
import java.util.Calendar;

View File

@ -1,25 +1,23 @@
/*
* 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
*/
* 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.uuid;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.ResponseBody;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.ResponseBody;
public class NameFetcher {
private static final OkHttpClient httpClient = new OkHttpClient();
@ -33,7 +31,8 @@ public class NameFetcher {
public static String getName(UUID uuid) throws IOException {
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")
.url(url)
.get()

View File

@ -1,24 +1,22 @@
/*
* 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
*/
* 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.uuid;
import com.google.common.collect.ImmutableList;
import com.google.gson.Gson;
import okhttp3.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Callable;
import okhttp3.*;
/* Credits to evilmidget38 for this class. I modified it to use Gson. */
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) {
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 {
@ -48,7 +55,8 @@ public class UUIDFetcher implements Callable<Map<String, UUID>> {
int requests = (int) Math.ceil(names.size() / PROFILES_PER_REQUEST);
for (int i = 0; i < requests; i++) {
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();
String response = responseBody.string();
responseBody.close();

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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.uuid;
import com.google.common.base.Charsets;
@ -15,19 +14,20 @@ import com.google.common.collect.ImmutableMap;
import com.google.gson.Gson;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
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.Collections;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
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 {
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 final RedisBungeePlugin<?> plugin;
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) {
// If the player is online, give them their UUID.
// Remember, local data > remote data.
if (plugin.getPlayer(player) != null)
return plugin.getPlayerUUID(player);
if (plugin.getPlayer(player) != null) return plugin.getPlayerUUID(player);
// Check if it exists in the map
CachedUUIDEntry cachedUUIDEntry = nameToUuidMap.get(player.toLowerCase());
if (cachedUUIDEntry != null) {
if (!cachedUUIDEntry.expired())
return cachedUUIDEntry.uuid();
else
nameToUuidMap.remove(player);
if (!cachedUUIDEntry.expired()) return cachedUUIDEntry.uuid();
else nameToUuidMap.remove(player);
}
// Check if we can exit early
@ -81,7 +78,8 @@ public final class UUIDTranslator {
if (!plugin.isOnlineMode()) {
return UUID.nameUUIDFromBytes(("OfflinePlayer:" + player).getBytes(Charsets.UTF_8));
}
RedisTask<UUID> redisTask = new RedisTask<UUID>(plugin) {
RedisTask<UUID> redisTask =
new RedisTask<UUID>(plugin) {
@Override
public UUID unifiedJedisTask(UnifiedJedis unifiedJedis) {
String stored = unifiedJedis.hget("uuid-cache", player.toLowerCase());
@ -102,8 +100,7 @@ public final class UUIDTranslator {
}
// That didn't work. Let's ask Mojang.
if (!expensiveLookups || !plugin.isOnlineMode())
return null;
if (!expensiveLookups || !plugin.isOnlineMode()) return null;
Map<String, UUID> uuidMap1;
try {
@ -134,19 +131,17 @@ public final class UUIDTranslator {
public String getNameFromUuid(@NonNull UUID player, boolean expensiveLookups) {
// If the player is online, give them their UUID.
// Remember, local data > remote data.
if (plugin.getPlayer(player) != null)
return plugin.getPlayerName(player);
if (plugin.getPlayer(player) != null) return plugin.getPlayerName(player);
// Check if it exists in the map
CachedUUIDEntry cachedUUIDEntry = uuidToNameMap.get(player);
if (cachedUUIDEntry != null) {
if (!cachedUUIDEntry.expired())
return cachedUUIDEntry.name();
else
uuidToNameMap.remove(player);
if (!cachedUUIDEntry.expired()) return cachedUUIDEntry.name();
else uuidToNameMap.remove(player);
}
RedisTask<String> redisTask = new RedisTask<String>(plugin) {
RedisTask<String> redisTask =
new RedisTask<String>(plugin) {
@Override
public String unifiedJedisTask(UnifiedJedis unifiedJedis) {
String stored = unifiedJedis.hget("uuid-cache", player.toString());
@ -158,7 +153,8 @@ public final class UUIDTranslator {
if (entry.expired()) {
unifiedJedis.hdel("uuid-cache", player.toString());
// 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());
} else {
nameToUuidMap.put(entry.name().toLowerCase(), entry);
@ -167,8 +163,7 @@ public final class UUIDTranslator {
}
}
if (!expensiveLookups || !plugin.isOnlineMode())
return null;
if (!expensiveLookups || !plugin.isOnlineMode()) return null;
// That didn't work. Let's ask PlayerDB.
String name;
@ -188,7 +183,6 @@ public final class UUIDTranslator {
}
};
// Okay, it wasn't locally cached. Let's try Redis.
try {
return redisTask.execute();
@ -201,8 +195,7 @@ public final class UUIDTranslator {
public void persistInfo(String name, UUID uuid, UnifiedJedis unifiedJedis) {
addToMaps(name, 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`
}
version = property("redisbungee-version").toString()
group = property("redisbungee-group").toString()
dependencies {
compileOnly(project(":RedisBungee-API"))
implementation(libs.acf.core)

View File

@ -1,43 +1,42 @@
/*
* 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
*/
* 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;
import co.aikar.commands.CommandContexts;
import co.aikar.commands.CommandManager;
import co.aikar.commands.InvalidCommandArgument;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.commands.legacy.LegacyRedisBungeeCommands;
import java.util.UUID;
public class CommandLoader {
public static void initCommands(CommandManager<?, ?, ?, ?, ?, ?> commandManager, RedisBungeePlugin<?> plugin) {
public static void initCommands(
CommandManager<?, ?, ?, ?, ?, ?> commandManager, RedisBungeePlugin<?> plugin) {
registerContexts(commandManager);
var commandsConfiguration = plugin.configuration().commandsConfiguration();
if (commandsConfiguration.redisbungeeEnabled()) {
commandManager.registerCommand(new CommandRedisBungee(plugin));
}
if (commandsConfiguration.redisbungeeLegacyEnabled()) {
commandManager.registerCommand(new LegacyRedisBungeeCommands(commandManager,plugin));
commandManager.registerCommand(new LegacyRedisBungeeCommands(commandManager, plugin));
}
commandManager.registerCommand(new CommandRedisBungeeDebug(plugin));
}
private static void registerContexts(CommandManager<?, ?, ?, ?, ?, ?> commandManager) {
CommandContexts<?> commandContexts = commandManager.getCommandContexts();
commandContexts.registerContext(UUID.class, c -> {
commandContexts.registerContext(
UUID.class,
c -> {
String uuidString = c.popFirstArg();
try {
return UUID.fromString(uuidString);
@ -46,5 +45,4 @@ public class CommandLoader {
}
});
}
}

View File

@ -1,23 +1,24 @@
/*
* 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
*/
* 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;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.RegisteredCommand;
import co.aikar.commands.annotation.*;
import com.google.common.primitives.Ints;
import com.imaginarycode.minecraft.redisbungee.Constants;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
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.TextComponent;
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.tag.resolver.Placeholder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@CommandAlias("rb|redisbungee")
@CommandPermission("redisbungee.command.use")
@Description("Main command")
@ -45,7 +42,8 @@ public class CommandRedisBungee extends AdventureBaseCommand {
@Subcommand("info|version|git")
@Description("information about current redisbungee build")
public void info(CommandIssuer issuer) {
final String message = """
final String message =
"""
<color:aqua>This proxy is running RedisBungee Limework's fork
<color:gold>========================================
<color:aqua>RedisBungee version: <color:green><version>
@ -61,10 +59,15 @@ public class CommandRedisBungee extends AdventureBaseCommand {
Placeholder.component(
"commit",
Component.text(Constants.GIT_COMMIT.substring(0, 8))
.clickEvent(ClickEvent.clickEvent(ClickEvent.Action.OPEN_URL, Constants.getGithubCommitLink()))
.hoverEvent(HoverEvent.showText(Component.text("Click me to open: " + Constants.getGithubCommitLink())))
)));
.clickEvent(
ClickEvent.clickEvent(
ClickEvent.Action.OPEN_URL, Constants.getGithubCommitLink()))
.hoverEvent(
HoverEvent.showText(
Component.text(
"Click me to open: " + Constants.getGithubCommitLink()))))));
}
// <color:aqua>......: <color:green>......
@HelpCommand
@Description("shows the help page")
@ -75,12 +78,22 @@ public class CommandRedisBungee extends AdventureBaseCommand {
TextComponent.Builder message = Component.text();
message.append(MiniMessage.miniMessage().deserialize(barFormat));
getSubCommands().forEach((subCommand, registeredCommand) -> {
getSubCommands()
.forEach(
(subCommand, registeredCommand) -> {
String[] split = registeredCommand.getCommand().split(" ");
if (split.length > 1 && subCommand.equalsIgnoreCase(split[1])) {
message.appendNewline().append(MiniMessage.miniMessage().deserialize(commandFormat, Placeholder.component("sub-command", Component.text(subCommand)),
Placeholder.component("description", MiniMessage.miniMessage().deserialize(registeredCommand.getHelpText()))
));
message
.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());
}
@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
public void cleanUp(CommandIssuer issuer) {
if (StopperUUIDCleanupTask.isRunning) {
sendMessage(issuer,
Component.text("cleanup is currently running!").color(NamedTextColor.RED));
sendMessage(
issuer, Component.text("cleanup is currently running!").color(NamedTextColor.RED));
return;
}
sendMessage(issuer,
Component.text("cleanup is Starting, you should see the output status in the proxy console").color(NamedTextColor.GOLD));
sendMessage(
issuer,
Component.text("cleanup is Starting, you should see the output status in the proxy console")
.color(NamedTextColor.GOLD));
plugin.executeAsync(new StopperUUIDCleanupTask(plugin));
}
private List<Map.Entry<String, Integer>> subListProxies(List<Map.Entry<String, Integer>> data, final int currentPage, final int pageSize) {
return data.subList(((currentPage * pageSize) - pageSize), Ints.constrainToRange(currentPage * pageSize, 0, data.size()));
private List<Map.Entry<String, Integer>> subListProxies(
List<Map.Entry<String, Integer>> data, final int currentPage, final int pageSize) {
return data.subList(
((currentPage * pageSize) - pageSize),
Ints.constrainToRange(currentPage * pageSize, 0, data.size()));
}
@Subcommand("show")
@Description("Shows proxies in this network")
public void showProxies(CommandIssuer issuer, String[] args) {
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 proxyHere = " (#) ";
final String nextPage = ">>>>>";
@ -146,44 +165,60 @@ public class CommandRedisBungee extends AdventureBaseCommand {
var subList = subListProxies(data, currentPage, pageSize);
TextComponent.Builder builder = Component.text();
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("max", Component.text(maxPages)),
Placeholder.component("network", Component.text(plugin.proxyDataManager().networkId())),
Placeholder.component("proxies", Component.text(data.size()))
)).appendNewline();
Placeholder.component(
"network", Component.text(plugin.proxyDataManager().networkId())),
Placeholder.component("proxies", Component.text(data.size()))))
.appendNewline();
int left = pageSize;
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("here", Component.text(plugin.proxyDataManager().proxyId().equals(entrySet.getKey()) ? proxyHere : "")),
Placeholder.component("players", Component.text(entrySet.getValue()))
)).appendNewline();
Placeholder.component(
"here",
Component.text(
plugin.proxyDataManager().proxyId().equals(entrySet.getKey())
? proxyHere
: "")),
Placeholder.component("players", Component.text(entrySet.getValue()))))
.appendNewline();
left--;
}
while(left > 0) {
while (left > 0) {
builder.appendNewline();
left--;
}
if (currentPage > 1) {
builder.append(MiniMessage.miniMessage().deserialize(previousPage)
.color(NamedTextColor.WHITE).clickEvent(ClickEvent.runCommand("/rb show " + (currentPage - 1))));
builder.append(
MiniMessage.miniMessage()
.deserialize(previousPage)
.color(NamedTextColor.WHITE)
.clickEvent(ClickEvent.runCommand("/rb show " + (currentPage - 1))));
} 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()) {
builder.append(MiniMessage.miniMessage().deserialize(nextPage)
.color(NamedTextColor.WHITE).clickEvent(ClickEvent.runCommand("/rb show " + (currentPage + 1))));
builder.append(
MiniMessage.miniMessage()
.deserialize(nextPage)
.color(NamedTextColor.WHITE)
.clickEvent(ClickEvent.runCommand("/rb show " + (currentPage + 1))));
} else {
builder.append(MiniMessage.miniMessage().deserialize(nextPage).color(NamedTextColor.GRAY));
}
builder.appendNewline();
builder.append(MiniMessage.miniMessage().deserialize(closer));
sendMessage(issuer, builder.build());
}
}

View File

@ -1,20 +1,18 @@
/*
* 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
*/
* 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;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.annotation.*;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseCommand;
import java.util.UUID;
@CommandAlias("rbd|redisbungeedebug")
@ -32,15 +30,19 @@ public class CommandRedisBungeeDebug extends AdventureBaseCommand {
@Description("kicks a player from the network by name")
@Private
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")
@Description("kicks a player from the network by UUID")
@Private
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,13 +1,12 @@
/*
* 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
*/
* 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.legacy;
import co.aikar.commands.CommandIssuer;
@ -30,5 +29,4 @@ public class CommandFind extends AdventureBaseCommand {
public void find(CommandIssuer issuer, String[] args) {
rootCommand.find(issuer, args);
}
}

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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.legacy;
import co.aikar.commands.CommandIssuer;
@ -30,5 +29,4 @@ public class CommandGList extends AdventureBaseCommand {
public void gList(CommandIssuer issuer, String[] args) {
rootCommand.gList(issuer, args);
}
}

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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.legacy;
import co.aikar.commands.CommandIssuer;
@ -26,7 +25,6 @@ public class CommandIp extends AdventureBaseCommand {
this.rootCommand = rootCommand;
}
@Default
public void ip(CommandIssuer issuer, String[] args) {
this.rootCommand.ip(issuer, args);

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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.legacy;
import co.aikar.commands.CommandIssuer;
@ -20,7 +19,6 @@ import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseComma
@CommandPermission("redisbungee.command.lastseen")
public class CommandLastSeen extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand;
public CommandLastSeen(LegacyRedisBungeeCommands rootCommand) {
@ -29,6 +27,6 @@ public class CommandLastSeen extends AdventureBaseCommand {
@Default
public void lastSeen(CommandIssuer issuer, String[] args) {
this.rootCommand.lastSeen(issuer,args);
this.rootCommand.lastSeen(issuer, args);
}
}

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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.legacy;
import co.aikar.commands.CommandIssuer;
@ -27,7 +26,6 @@ public class CommandPProxy extends AdventureBaseCommand {
@Default
public void playerProxy(CommandIssuer issuer, String[] args) {
this.rootCommand.playerProxy(issuer,args);
this.rootCommand.playerProxy(issuer, args);
}
}

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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.legacy;
import co.aikar.commands.CommandIssuer;
@ -20,7 +19,6 @@ import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseComma
@CommandPermission("redisbungee.command.plist")
public class CommandPlist extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand;
public CommandPlist(LegacyRedisBungeeCommands rootCommand) {
@ -31,5 +29,4 @@ public class CommandPlist extends AdventureBaseCommand {
public void playerList(CommandIssuer issuer, String[] args) {
this.rootCommand.playerList(issuer, args);
}
}

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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.legacy;
import co.aikar.commands.CommandIssuer;
@ -20,12 +19,12 @@ import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseComma
@CommandPermission("redisbungee.command.sendtoall")
public class CommandSendToAll extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand;
public CommandSendToAll(LegacyRedisBungeeCommands rootCommand) {
this.rootCommand = rootCommand;
}
@Default
public void sendToAll(CommandIssuer issuer, String[] args) {
this.rootCommand.sendToAll(issuer, args);

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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.legacy;
import co.aikar.commands.CommandIssuer;
@ -20,12 +19,12 @@ import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseComma
@CommandPermission("redisbungee.command.serverid")
public class CommandServerId extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand;
public CommandServerId(LegacyRedisBungeeCommands rootCommand) {
this.rootCommand = rootCommand;
}
@Default
public void serverId(CommandIssuer issuer) {
this.rootCommand.serverId(issuer);

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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.legacy;
import co.aikar.commands.CommandIssuer;
@ -20,7 +19,6 @@ import com.imaginarycode.minecraft.redisbungee.commands.utils.AdventureBaseComma
@CommandPermission("redisbungee.command.serverids")
public class CommandServerIds extends AdventureBaseCommand {
private final LegacyRedisBungeeCommands rootCommand;
public CommandServerIds(LegacyRedisBungeeCommands rootCommand) {
@ -31,6 +29,4 @@ public class CommandServerIds extends AdventureBaseCommand {
public void serverIds(CommandIssuer issuer) {
this.rootCommand.serverIds(issuer);
}
}

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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.legacy;
import co.aikar.commands.CommandIssuer;
@ -20,15 +19,14 @@ import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
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.text.SimpleDateFormat;
import java.util.Set;
import java.util.TreeSet;
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")
@CommandPermission("redisbungee.legacy.use")
@ -36,10 +34,12 @@ public class LegacyRedisBungeeCommands extends AdventureBaseCommand {
private final RedisBungeePlugin<?> plugin;
public LegacyRedisBungeeCommands(CommandManager<?, ?, ?, ?, ?, ?> commandManager, RedisBungeePlugin<?> plugin) {
public LegacyRedisBungeeCommands(
CommandManager<?, ?, ?, ?, ?, ?> commandManager, RedisBungeePlugin<?> plugin) {
this.plugin = plugin;
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.installGlist()) commandManager.registerCommand(new CommandGList(this));
@ -67,36 +67,47 @@ public class LegacyRedisBungeeCommands extends AdventureBaseCommand {
@Subcommand("glist")
@CommandPermission("redisbungee.command.glist")
public void gList(CommandIssuer issuer, String[] args) {
plugin.executeAsync(() -> {
plugin.executeAsync(
() -> {
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")) {
Multimap<String, UUID> serverToPlayers = plugin.getAbstractRedisBungeeApi().getServerToPlayers();
Multimap<String, UUID> serverToPlayers =
plugin.getAbstractRedisBungeeApi().getServerToPlayers();
Multimap<String, String> human = HashMultimap.create();
serverToPlayers.forEach((key, value) -> {
// if for any reason UUID translation fails just return the uuid as name, to make command finish executing.
serverToPlayers.forEach(
(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);
human.put(key, playerName != null ? playerName : value.toString());
});
for (String server : new TreeSet<>(serverToPlayers.keySet())) {
Component serverName = Component.text("[" + server + "] ", NamedTextColor.GREEN);
Component serverCount = Component.text("(" + serverToPlayers.get(server).size() + "): ", NamedTextColor.YELLOW);
Component serverPlayers = Component.text(Joiner.on(", ").join(human.get(server)), NamedTextColor.WHITE);
Component serverCount =
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, playersOnline);
} else {
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")
@CommandPermission("redisbungee.command.find")
public void find(CommandIssuer issuer, String[] args) {
plugin.executeAsync(() -> {
plugin.executeAsync(
() -> {
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
@ -106,7 +117,10 @@ public class LegacyRedisBungeeCommands extends AdventureBaseCommand {
String proxyId = plugin.playerDataManager().getProxyFor(uuid);
if (proxyId != null) {
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);
} else {
sendMessage(issuer, PLAYER_NOT_FOUND);
@ -115,13 +129,13 @@ public class LegacyRedisBungeeCommands extends AdventureBaseCommand {
sendMessage(issuer, NO_PLAYER_SPECIFIED);
}
});
}
@Subcommand("lastseen")
@CommandPermission("redisbungee.command.lastseen")
public void lastSeen(CommandIssuer issuer, String[] args) {
plugin.executeAsync(() -> {
plugin.executeAsync(
() -> {
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
@ -135,7 +149,8 @@ public class LegacyRedisBungeeCommands extends AdventureBaseCommand {
message.content(args[0] + " is currently online.");
} else if (secs != -1) {
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 {
message.color(NamedTextColor.RED);
message.content(args[0] + " has never been online.");
@ -144,15 +159,14 @@ public class LegacyRedisBungeeCommands extends AdventureBaseCommand {
} else {
sendMessage(issuer, NO_PLAYER_SPECIFIED);
}
});
}
@Subcommand("ip")
@CommandPermission("redisbungee.command.ip")
public void ip(CommandIssuer issuer, String[] args) {
plugin.executeAsync(() -> {
plugin.executeAsync(
() -> {
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
@ -161,7 +175,9 @@ public class LegacyRedisBungeeCommands extends AdventureBaseCommand {
}
InetAddress ia = plugin.getAbstractRedisBungeeApi().getPlayerIp(uuid);
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);
} else {
sendMessage(issuer, PLAYER_NOT_FOUND);
@ -175,7 +191,8 @@ public class LegacyRedisBungeeCommands extends AdventureBaseCommand {
@Subcommand("pproxy")
@CommandPermission("redisbungee.command.pproxy")
public void playerProxy(CommandIssuer issuer, String[] args) {
plugin.executeAsync(() -> {
plugin.executeAsync(
() -> {
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
@ -184,7 +201,8 @@ public class LegacyRedisBungeeCommands extends AdventureBaseCommand {
}
String proxy = plugin.getAbstractRedisBungeeApi().getProxy(uuid);
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);
} else {
sendMessage(issuer, PLAYER_NOT_FOUND);
@ -193,7 +211,6 @@ public class LegacyRedisBungeeCommands extends AdventureBaseCommand {
sendMessage(issuer, NO_PLAYER_SPECIFIED);
}
});
}
@Subcommand("sendtoall")
@ -202,59 +219,81 @@ public class LegacyRedisBungeeCommands extends AdventureBaseCommand {
if (args.length > 0) {
String command = Joiner.on(" ").skipNulls().join(args);
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);
} else {
sendMessage(issuer, NO_COMMAND_SPECIFIED);
}
}
@Subcommand("serverid")
@CommandPermission("redisbungee.command.serverid")
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")
@CommandPermission("redisbungee.command.serverids")
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")
@CommandPermission("redisbungee.command.plist")
public void playerList(CommandIssuer issuer, String[] args) {
plugin.executeAsync(() -> {
plugin.executeAsync(
() -> {
String proxy = args.length >= 1 ? args[0] : plugin.configuration().getProxyId();
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;
}
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")) {
Multimap<String, UUID> serverToPlayers = plugin.getAbstractRedisBungeeApi().getServerToPlayers();
Multimap<String, UUID> serverToPlayers =
plugin.getAbstractRedisBungeeApi().getServerToPlayers();
Multimap<String, String> human = HashMultimap.create();
serverToPlayers.forEach((key, value) -> {
serverToPlayers.forEach(
(key, value) -> {
if (players.contains(value)) {
human.put(key, plugin.getUuidTranslator().getNameFromUuid(value, false));
}
});
for (String server : new TreeSet<>(human.keySet())) {
TextComponent serverName = Component.text("[" + server + "] ", NamedTextColor.RED);
TextComponent serverCount = Component.text("(" + human.get(server).size() + "): ", NamedTextColor.YELLOW);
TextComponent serverPlayers = Component.text(Joiner.on(", ").join(human.get(server)), NamedTextColor.WHITE);
TextComponent serverCount =
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, playersOnline);
} else {
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,26 +1,22 @@
/*
* 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
*/
* 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;
import co.aikar.commands.BaseCommand;
import co.aikar.commands.CommandIssuer;
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 {
protected void sendMessage(CommandIssuer issuer, Component component) {
CommandPlatformHelper.getPlatformHelper().sendMessage(issuer, component);
}
}

View File

@ -1,20 +1,17 @@
/*
* 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
*/
* 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;
import co.aikar.commands.CommandIssuer;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import net.kyori.adventure.text.Component;
public abstract class CommandPlatformHelper {
private static CommandPlatformHelper SINGLETON;
@ -28,9 +25,7 @@ public abstract class CommandPlatformHelper {
SINGLETON = platformHelper;
}
public static CommandPlatformHelper getPlatformHelper() {
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;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
@ -12,13 +21,13 @@ public class StopperUUIDCleanupTask extends UUIDCleanupTask {
super(plugin);
}
@Override
public Void unifiedJedisTask(UnifiedJedis unifiedJedis) {
isRunning = true;
try {
super.unifiedJedisTask(unifiedJedis);
} catch (Exception ignored) {}
} catch (Exception ignored) {
}
isRunning = false;
return null;
}

View File

@ -1,7 +1,9 @@
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
/*
* 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
*/

View File

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

View File

@ -1,33 +1,32 @@
/*
* 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
*/
* 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 net.limework.valiobungee.config.lang;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
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.minimessage.MiniMessage;
import ninja.leaping.configurate.ConfigurationNode;
import ninja.leaping.configurate.yaml.YAMLConfigurationLoader;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Locale;
public interface LangConfigLoader extends GenericConfigLoader {
int CONFIG_VERSION = 1;
default void loadLangConfig(RedisBungeePlugin<?> plugin, Path dataFolder) throws IOException {
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();
if (node.getNode("config-version").getInt(0) != CONFIG_VERSION) {
handleOldConfig(dataFolder, "lang.yml", "lang.yml");
@ -36,20 +35,29 @@ public interface LangConfigLoader extends GenericConfigLoader {
// MINI message serializer
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"));
boolean useClientLocale = node.getNode("use-client-locale").getBoolean(true);
LangConfiguration.Messages messages = new LangConfiguration.Messages(defaultLocale);
node.getNode("messages").getChildrenMap().forEach((key, childNode) -> childNode.getChildrenMap().forEach((childKey, childChildNode) -> {
messages.register(key.toString(), Locale.forLanguageTag(childKey.toString()), childChildNode.getString());
node.getNode("messages")
.getChildrenMap()
.forEach(
(key, childNode) ->
childNode
.getChildrenMap()
.forEach(
(childKey, childChildNode) -> {
messages.register(
key.toString(),
Locale.forLanguageTag(childKey.toString()),
childChildNode.getString());
}));
messages.test(defaultLocale);
onLangConfigLoad(new LangConfiguration(prefix, defaultLocale, useClientLocale, messages));
}
void onLangConfigLoad(LangConfiguration langConfiguration);
}

View File

@ -1,27 +1,24 @@
/*
* 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
*/
* 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 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.Locale;
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
* until I come up with better system but for now we will use Maps instead :/
* Todo: possible usage of adventure api
* This language support implementation is temporarily until I come up with better system but for
* now we will use Maps instead :/ Todo: possible usage of adventure api
*/
public class LangConfiguration {
@ -32,12 +29,12 @@ public class LangConfiguration {
void test(Locale locale);
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 {
private final Map<Locale, Component> LOGGED_IN_FROM_OTHER_LOCATION;
private final Map<Locale, Component> ALREADY_LOGGED_IN;
@ -58,8 +55,11 @@ public class LangConfiguration {
switch (id) {
case "server-not-found" -> SERVER_NOT_FOUND.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 "already-logged-in" -> ALREADY_LOGGED_IN.put(locale, MiniMessage.miniMessage().deserialize(miniMessage));
case "logged-in-other-location" ->
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);
}
// 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() {
return this.alreadyLoggedIn(this.defaultLocale);
}
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);
}
// 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() {
return this.loggedInFromOtherLocation(this.defaultLocale);
}
@ -90,7 +93,8 @@ public class LangConfiguration {
} else {
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) {
@ -104,21 +108,23 @@ public class LangConfiguration {
} else {
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) {
return this.serverNotFound(this.defaultLocale, server);
}
// tests locale if set CORRECTLY or just throw if not
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");
}
}
}
private final Component redisBungeePrefix;
@ -129,7 +135,11 @@ public class LangConfiguration {
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.defaultLanguage = defaultLanguage;
this.useClientLanguage = useClientLanguage;
@ -151,5 +161,4 @@ public class LangConfiguration {
public Messages messages() {
return messages;
}
}

View File

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

View File

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

View File

@ -1,22 +1,19 @@
/*
* 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
*/
* 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;
import net.kyori.adventure.text.Component;
import java.util.UUID;
import net.kyori.adventure.text.Component;
// this class used to redirect calls to keep the implementation and api separate
public interface ApiPlatformSupport {
void kickPlayer(UUID player, Component message);
}

View File

@ -1,16 +1,17 @@
/*
* 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
*/
* 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;
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.serializer.bungeecord.BungeeComponentSerializer;
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.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()}
* or somehow you got the Plugin instance by you can call the api using {@link RedisBungeePlugin#getAbstractRedisBungeeApi()}.
* This platform class exposes some internal RedisBungee functions. You obtain an instance of this
* 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
* @since 0.2.3 | updated 0.8.0
@ -33,7 +32,8 @@ public class RedisBungeeAPI extends AbstractRedisBungeeAPI {
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) {
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
* as well, and will return local information on them.
* Get the server where the specified player is playing. This function also deals with the case of
* local players as well, and will return local information on them.
*
* @param player a player uuid
* @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
* calls {@link #getUuidFromName(String)} to get uuid
* Kicks a player from the network calls {@link #getUuidFromName(String)} to get uuid
*
* @param playerName player name
* @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
* calls {@link #getUuidFromName(String)} to get uuid
* Kicks a player from the network calls {@link #getUuidFromName(String)} to get uuid
*
* @param playerName player name
* @param message kick message that player will see on kick
@ -137,8 +135,7 @@ public class RedisBungeeAPI extends AbstractRedisBungeeAPI {
* @deprecated No longer required
*/
@Deprecated(forRemoval = true)
public final void registerPubSubChannels(String... channels) {
}
public final void registerPubSubChannels(String... channels) {}
/**
* Unregister (a) PubSub channel(s).
@ -148,8 +145,7 @@ public class RedisBungeeAPI extends AbstractRedisBungeeAPI {
* @deprecated No longer required
*/
@Deprecated(forRemoval = true)
public final void unregisterPubSubChannels(String... channels) {
}
public final void unregisterPubSubChannels(String... channels) {}
/**
* Api instance

View File

@ -1,30 +1,29 @@
/*
* 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
*/
* 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.events;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerChangedServerNetworkEvent;
import net.md_5.bungee.api.plugin.Event;
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
* 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.
*
* @since 0.3.4
*/
public class PlayerChangedServerNetworkEvent extends Event implements IPlayerChangedServerNetworkEvent {
public class PlayerChangedServerNetworkEvent extends Event
implements IPlayerChangedServerNetworkEvent {
private final UUID uuid;
private final String previousServer;
private final String server;

View File

@ -1,25 +1,23 @@
/*
* 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
*/
* 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.events;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerJoinedNetworkEvent;
import java.util.UUID;
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
* 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.PostLoginEvent}, and is fired
* This event is sent when a player joins the network. RedisBungee sends the event only when 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.PostLoginEvent}, and is fired
* asynchronously.
*
* @since 0.3.4

View File

@ -1,26 +1,24 @@
/*
* 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
*/
* 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.events;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerLeftNetworkEvent;
import java.util.UUID;
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
* 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.PlayerDisconnectEvent}, and is fired
* asynchronously.
* This event is sent when a player disconnects. RedisBungee sends the event only when 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.PlayerDisconnectEvent}, and is
* fired asynchronously.
*
* @since 0.3.4
*/

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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.events;
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.
* <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
*/
public class PubSubMessageEvent extends Event implements IPubSubMessageEvent {
private final String channel;
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;
import co.aikar.commands.BungeeCommandIssuer;
@ -13,5 +22,4 @@ public class BungeeCommandPlatformHelper extends CommandPlatformHelper {
BungeeCommandIssuer bIssuer = (BungeeCommandIssuer) issuer;
bIssuer.getIssuer().sendMessage(BungeeComponentSerializer.get().serialize(component));
}
}

View File

@ -1,25 +1,24 @@
/*
* 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
*/
* 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;
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.PlayerJoinedNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.events.PlayerLeftNetworkEvent;
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.minimessage.MiniMessage;
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.event.LoginEvent;
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.event.EventHandler;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class BungeePlayerDataManager extends PlayerDataManager<ProxiedPlayer> implements Listener {
private final RedisBungee bPlugin;
public BungeePlayerDataManager(RedisBungee plugin) {
super(plugin);
bPlugin = plugin;
@ -64,13 +60,18 @@ public class BungeePlayerDataManager extends PlayerDataManager<ProxiedPlayer> im
@EventHandler
public void onServerConnectedEvent(ServerConnectedEvent event) {
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);
}
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
public boolean handleSerializedKick(UUID uuid, String serializedMiniMessage) {
ProxiedPlayer player = plugin.getPlayer(uuid);
@ -96,39 +97,46 @@ public class BungeePlayerDataManager extends PlayerDataManager<ProxiedPlayer> im
event.registerIntent((Plugin) plugin);
// check if online
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
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);
} else {
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
plugin.executeAsyncAfter(() -> event.completeIntent((Plugin) plugin), TimeUnit.SECONDS, 3);
plugin.executeAsyncAfter(
() -> event.completeIntent((Plugin) plugin), TimeUnit.SECONDS, 3);
} else {
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);
}
}
} else {
event.completeIntent((Plugin) plugin);
}
}
@EventHandler
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
public void onDisconnectEvent(PlayerDisconnectEvent event) {
super.removePlayer(event.getPlayer().getUniqueId());
}
}

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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;
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.RedisBungeeMode;
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.loaders.ConfigLoader;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerChangedServerNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerJoinedNetworkEvent;
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.PlayerLeftNetworkEvent;
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.limework.valiobungee.config.lang.LangConfigLoader;
import net.limework.valiobungee.config.lang.LangConfiguration;
@ -43,17 +50,11 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.JedisPool;
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;
public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlayer>, ConfigLoader, LangConfigLoader, ApiPlatformSupport {
public class RedisBungee extends Plugin
implements RedisBungeePlugin<ProxiedPlayer>,
ConfigLoader,
LangConfigLoader,
ApiPlatformSupport {
private static RedisBungeeAPI apiStatic;
private AbstractRedisBungeeAPI api;
@ -70,7 +71,6 @@ public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlay
private final Logger logger = LoggerFactory.getLogger("RedisBungee");
@Override
public RedisBungeeConfiguration configuration() {
return this.configuration;
@ -175,7 +175,6 @@ public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlay
return player.getAddress().getAddress();
}
@Override
public void initialize() {
logInfo("Initializing RedisBungee.....");
@ -199,18 +198,23 @@ public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlay
throw new RuntimeException("Unable to load/save config", e);
}
// init the proxy data manager
this.proxyDataManager = new ProxyDataManager(this) {
this.proxyDataManager =
new ProxyDataManager(this) {
@Override
public Set<UUID> getLocalOnlineUUIDs() {
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;
}
@Override
protected void handlePlatformCommandExecution(String command) {
logInfo("Dispatching {}", command);
ProxyServer.getInstance().getPluginManager().dispatchCommand(RedisBungeeCommandSender.getSingleton(), command);
ProxyServer.getInstance()
.getPluginManager()
.dispatchCommand(RedisBungeeCommandSender.getSingleton(), command);
}
};
this.playerDataManager = new BungeePlayerDataManager(this);
@ -220,9 +224,15 @@ public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlay
// start listening
getProxy().getScheduler().runAsync(this, proxyDataManager);
// 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
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
InitialUtils.checkRedisVersion(this);
uuidTranslator = new UUIDTranslator(this);
@ -301,7 +311,8 @@ public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlay
}
@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);
}
@ -321,7 +332,8 @@ public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlay
}
@Override
public void onConfigLoad(RedisBungeeConfiguration configuration, Summoner<?> summoner, RedisBungeeMode mode) {
public void onConfigLoad(
RedisBungeeConfiguration configuration, Summoner<?> summoner, RedisBungeeMode mode) {
this.configuration = configuration;
this.redisBungeeMode = mode;
this.summoner = summoner;
@ -346,7 +358,8 @@ public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlay
* This returns an instance of {@link RedisBungeeAPI}
*
* @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
public static RedisBungeeAPI getApi() {
@ -358,12 +371,12 @@ public class RedisBungee extends Plugin implements RedisBungeePlugin<ProxiedPlay
if (api.getMode() == RedisBungeeMode.SINGLE) {
JedisPool jedisPool = ((JedisPooledSummoner) getSummoner()).getCompatibilityJedisPool();
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;
} else {
throw new IllegalStateException("Mode is not " + RedisBungeeMode.SINGLE);
}
}
}

View File

@ -1,20 +1,18 @@
/*
* 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
*/
* 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;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.BaseComponent;
import java.util.Collection;
import java.util.Collections;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.BaseComponent;
public class RedisBungeeCommandSender implements CommandSender {
private static final RedisBungeeCommandSender singleton;
@ -33,24 +31,16 @@ public class RedisBungeeCommandSender implements CommandSender {
}
@Override
public void sendMessage(String s) {
}
public void sendMessage(String s) {}
@Override
public void sendMessages(String... strings) {
}
public void sendMessages(String... strings) {}
@Override
public void sendMessage(BaseComponent... baseComponents) {
}
public void sendMessage(BaseComponent... baseComponents) {}
@Override
public void sendMessage(BaseComponent baseComponent) {
}
public void sendMessage(BaseComponent baseComponent) {}
@Override
public Collection<String> getGroups() {
@ -58,14 +48,10 @@ public class RedisBungeeCommandSender implements CommandSender {
}
@Override
public void addGroups(String... strings) {
}
public void addGroups(String... strings) {}
@Override
public void removeGroups(String... strings) {
}
public void removeGroups(String... strings) {}
@Override
public boolean hasPermission(String s) {
@ -73,9 +59,7 @@ public class RedisBungeeCommandSender implements CommandSender {
}
@Override
public void setPermission(String s, boolean b) {
}
public void setPermission(String s, boolean b) {}
@Override
public Collection<String> getPermissions() {

View File

@ -1,23 +1,24 @@
/*
* 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
*/
* 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;
import static com.imaginarycode.minecraft.redisbungee.api.util.serialize.MultiMapSerialization.*;
import com.google.common.base.Joiner;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.api.config.HandleMotdOrder;
import java.util.*;
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
import net.md_5.bungee.api.AbstractReconnectHandler;
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.EventPriority;
import java.util.*;
import static com.imaginarycode.minecraft.redisbungee.api.util.serialize.MultiMapSerialization.*;
public class RedisBungeeListener implements Listener {
private final RedisBungee plugin;
@ -69,7 +66,10 @@ public class RedisBungeeListener implements Listener {
private void onPing0(ProxyPingEvent event) {
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());
if (forced != null && event.getConnection().getListener().isPingPassthrough()) return;
@ -79,10 +79,12 @@ public class RedisBungeeListener implements Listener {
@SuppressWarnings("UnstableApiUsage")
@EventHandler
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 byte[] data = Arrays.copyOf(event.getData(), event.getData().length);
plugin.executeAsync(() -> {
plugin.executeAsync(
() -> {
ByteArrayDataInput in = ByteStreams.newDataInput(data);
String subchannel = in.readUTF();
@ -118,7 +120,8 @@ public class RedisBungeeListener implements Listener {
} else {
out.writeUTF(type);
try {
out.writeInt(plugin.getAbstractRedisBungeeApi().getPlayersOnServer(type).size());
out.writeInt(
plugin.getAbstractRedisBungeeApi().getPlayersOnServer(type).size());
} catch (IllegalArgumentException e) {
out.writeInt(0);
}
@ -128,12 +131,18 @@ public class RedisBungeeListener implements Listener {
String user = in.readUTF();
out.writeUTF("LastOnline");
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" -> {
String type1 = in.readUTF();
out.writeUTF("ServerPlayers");
Multimap<String, UUID> multimap = plugin.getAbstractRedisBungeeApi().getServerToPlayers();
Multimap<String, UUID> multimap =
plugin.getAbstractRedisBungeeApi().getServerToPlayers();
boolean includesUsers;
switch (type1) {
case "COUNT" -> includesUsers = false;
@ -147,7 +156,9 @@ public class RedisBungeeListener implements Listener {
if (includesUsers) {
Multimap<String, String> human = HashMultimap.create();
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);
} else {
@ -162,7 +173,12 @@ public class RedisBungeeListener implements Listener {
String username = in.readUTF();
out.writeUTF("PlayerProxy");
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 -> {
return;
@ -176,14 +192,28 @@ public class RedisBungeeListener implements Listener {
@EventHandler
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();
String lastServer = plugin.playerDataManager().getLastServerFor(event.getPlayer().getUniqueId());
String lastServer =
plugin.playerDataManager().getLastServerFor(event.getPlayer().getUniqueId());
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);
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;
}
event.setTarget(serverInfo);

View File

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

View File

@ -1,23 +1,22 @@
/*
* 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
*/
* 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;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.permission.Tristate;
import net.kyori.adventure.permission.PermissionChecker;
public class RedisBungeeCommandSource implements CommandSource {
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 {
singleton = new RedisBungeeCommandSource();
@ -27,7 +26,6 @@ public class RedisBungeeCommandSource implements CommandSource {
return singleton;
}
@Override
public boolean hasPermission(String permission) {
return this.permissionChecker.test(permission);

View File

@ -1,22 +1,23 @@
/*
* 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
*/
* 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;
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.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.api.config.HandleMotdOrder;
import com.velocitypowered.api.event.PostOrder;
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.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerPing;
import java.util.*;
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 {
private final RedisBungeeVelocityPlugin plugin;
@ -68,7 +65,10 @@ public class RedisBungeeListener {
private void onPing0(ProxyPingEvent event) {
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();
ping.onlinePlayers(plugin.proxyDataManager().totalNetworkPlayers());
@ -77,13 +77,15 @@ public class RedisBungeeListener {
@Subscribe
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;
}
event.setResult(PluginMessageEvent.ForwardResult.handled());
plugin.executeAsync(() -> {
plugin.executeAsync(
() -> {
ByteArrayDataInput in = event.dataAsDataStream();
String subchannel = in.readUTF();
@ -105,7 +107,8 @@ public class RedisBungeeListener {
} catch (IllegalArgumentException ignored) {
}
}
Set<String> players = original.stream()
Set<String> players =
original.stream()
.map(uuid -> plugin.getUuidTranslator().getNameFromUuid(uuid, false))
.collect(Collectors.toSet());
out.writeUTF(Joiner.on(',').join(players));
@ -129,12 +132,18 @@ public class RedisBungeeListener {
String user = in.readUTF();
out.writeUTF("LastOnline");
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" -> {
String type1 = in.readUTF();
out.writeUTF("ServerPlayers");
Multimap<String, UUID> multimap = plugin.getAbstractRedisBungeeApi().getServerToPlayers();
Multimap<String, UUID> multimap =
plugin.getAbstractRedisBungeeApi().getServerToPlayers();
boolean includesUsers;
switch (type1) {
case "COUNT" -> includesUsers = false;
@ -148,7 +157,9 @@ public class RedisBungeeListener {
if (includesUsers) {
Multimap<String, String> human = HashMultimap.create();
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);
} else {
@ -163,19 +174,25 @@ public class RedisBungeeListener {
String username = in.readUTF();
out.writeUTF("PlayerProxy");
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 -> {
return;
}
}
try {
// ServerConnection throws IllegalStateException when connection dies somehow so just ignore :/
((ServerConnection) event.getSource()).sendPluginMessage(event.getIdentifier(), out.toByteArray());
// ServerConnection throws IllegalStateException when connection dies somehow so just
// ignore :/
((ServerConnection) event.getSource())
.sendPluginMessage(event.getIdentifier(), out.toByteArray());
} catch (IllegalStateException ignored) {
}
});
}
@Subscribe
@ -184,16 +201,23 @@ public class RedisBungeeListener {
Player player = event.getPlayer();
String lastServer = plugin.playerDataManager().getLastServerFor(player.getUniqueId());
if (lastServer == null) return;
player.sendMessage(plugin.langConfiguration().messages().serverConnecting(player.getPlayerSettings().getLocale(), lastServer));
Optional<RegisteredServer> optionalRegisteredServer = ((RedisBungeeVelocityPlugin) plugin).getProxy().getServer(lastServer);
player.sendMessage(
plugin
.langConfiguration()
.messages()
.serverConnecting(player.getPlayerSettings().getLocale(), lastServer));
Optional<RegisteredServer> optionalRegisteredServer =
((RedisBungeeVelocityPlugin) plugin).getProxy().getServer(lastServer);
if (optionalRegisteredServer.isEmpty()) {
player.sendMessage(plugin.langConfiguration().messages().serverNotFound(player.getPlayerSettings().getLocale(), lastServer));
player.sendMessage(
plugin
.langConfiguration()
.messages()
.serverNotFound(player.getPlayerSettings().getLocale(), lastServer));
return;
}
RegisteredServer server = optionalRegisteredServer.get();
event.setInitialServer(server);
}
}
}

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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;
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.RedisBungeeMode;
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.loaders.ConfigLoader;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerChangedServerNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerJoinedNetworkEvent;
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.util.InitialUtils;
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.PlayerJoinedNetworkEvent;
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.MinecraftChannelIdentifier;
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.InputStream;
import java.net.InetAddress;
@ -59,9 +52,20 @@ import java.util.List;
import java.util.Set;
import java.util.UUID;
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"})
public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, ConfigLoader, LangConfigLoader, ApiPlatformSupport {
@Plugin(
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 Logger logger;
private final Path dataFolder;
@ -79,17 +83,18 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
private ScheduledTask cleanUpTask;
private ScheduledTask heartbeatTask;
public static final List<ChannelIdentifier> IDENTIFIERS = List.of(
public static final List<ChannelIdentifier> IDENTIFIERS =
List.of(
MinecraftChannelIdentifier.create("legacy", "redisbungee"),
new LegacyChannelIdentifier("RedisBungee"),
// This is needed for clients before 1.13
new LegacyChannelIdentifier("legacy:redisbungee")
);
new LegacyChannelIdentifier("legacy:redisbungee"));
private VelocityCommandManager commandManager;
@Inject
public RedisBungeeVelocityPlugin(ProxyServer server, Logger logger, @DataDirectory Path dataDirectory) {
public RedisBungeeVelocityPlugin(
ProxyServer server, Logger logger, @DataDirectory Path dataDirectory) {
this.server = server;
this.logger = logger;
this.dataFolder = dataDirectory;
@ -104,7 +109,8 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
}
this.api = new RedisBungeeAPI(this);
InitialUtils.checkRedisVersion(this);
this.proxyDataManager = new ProxyDataManager(this) {
this.proxyDataManager =
new ProxyDataManager(this) {
@Override
public Set<UUID> getLocalOnlineUUIDs() {
HashSet<UUID> players = new HashSet<>();
@ -114,14 +120,15 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
@Override
protected void handlePlatformCommandExecution(String command) {
server.getCommandManager().executeAsync(RedisBungeeCommandSource.getSingleton(), command);
server
.getCommandManager()
.executeAsync(RedisBungeeCommandSource.getSingleton(), command);
}
};
this.playerDataManager = new VelocityPlayerDataManager(this);
uuidTranslator = new UUIDTranslator(this);
}
@Override
public Summoner<?> getSummoner() {
return this.jedisSummoner;
@ -147,7 +154,6 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
return this.uuidTranslator;
}
@Override
public void executeAsync(Runnable runnable) {
this.getProxy().getScheduler().buildTask(this, runnable).schedule();
@ -229,7 +235,10 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
@Override
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
@ -247,8 +256,18 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
logInfo("Initializing RedisBungee.....");
// start heartbeat task
// heartbeat and clean up
this.heartbeatTask = 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();
this.heartbeatTask =
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, new RedisBungeeListener(this));
@ -289,7 +308,8 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
}
@Override
public void onConfigLoad(RedisBungeeConfiguration configuration, Summoner<?> summoner, RedisBungeeMode mode) {
public void onConfigLoad(
RedisBungeeConfiguration configuration, Summoner<?> summoner, RedisBungeeMode mode) {
this.jedisSummoner = summoner;
this.configuration = configuration;
this.redisBungeeMode = mode;
@ -305,7 +325,6 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
return this.redisBungeeMode;
}
@Subscribe(order = PostOrder.FIRST)
public void onProxyInitializeEvent(ProxyInitializeEvent event) {
initialize();
@ -316,9 +335,9 @@ public class RedisBungeeVelocityPlugin implements RedisBungeePlugin<Player>, Con
stop();
}
@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);
}

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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;
import co.aikar.commands.CommandIssuer;
@ -22,5 +21,4 @@ public class VelocityCommandPlatformHelper extends CommandPlatformHelper {
VelocityCommandIssuer vIssuer = (VelocityCommandIssuer) issuer;
vIssuer.getIssuer().sendMessage(component);
}
}

View File

@ -1,13 +1,12 @@
/*
* 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
*/
* 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;
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.player.ServerConnectedEvent;
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.concurrent.TimeUnit;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
public class VelocityPlayerDataManager extends PlayerDataManager<Player> {
@ -71,7 +68,8 @@ public class VelocityPlayerDataManager extends PlayerDataManager<Player> {
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
public boolean handleSerializedKick(UUID uuid, String serializedMiniMessage) {
Player player = plugin.getPlayer(uuid);
@ -96,18 +94,26 @@ public class VelocityPlayerDataManager extends PlayerDataManager<Player> {
public void onLoginEvent(LoginEvent event, Continuation continuation) {
// check if online
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
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();
} else {
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
plugin.executeAsyncAfter(continuation::resume, TimeUnit.SECONDS, 3);
} else {
event.setResult(ResultedEvent.ComponentResult.denied(vplugin.langConfiguration().messages().alreadyLoggedIn()));
event.setResult(
ResultedEvent.ComponentResult.denied(
vplugin.langConfiguration().messages().alreadyLoggedIn()));
continuation.resume();
}
}
@ -118,12 +124,16 @@ public class VelocityPlayerDataManager extends PlayerDataManager<Player> {
@Subscribe
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
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());
}
}

View File

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

View File

@ -1,19 +1,17 @@
/*
* 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
*/
* 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;
import com.velocitypowered.api.proxy.ProxyServer;
import net.kyori.adventure.text.Component;
import java.util.UUID;
import net.kyori.adventure.text.Component;
// this class used to redirect calls to keep the implementation and api separate
public interface ApiPlatformSupport {
@ -21,5 +19,4 @@ public interface ApiPlatformSupport {
ProxyServer getProxy();
void kickPlayer(UUID player, Component message);
}

View File

@ -1,27 +1,26 @@
/*
* 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
*/
* 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;
import com.imaginarycode.minecraft.redisbungee.api.RedisBungeePlugin;
import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.velocitypowered.api.proxy.server.ServerInfo;
import java.util.UUID;
import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.NonNull;
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()}
* or somehow you got the Plugin instance by you can call the api using {@link RedisBungeePlugin#getAbstractRedisBungeeApi()}.
* This platform class exposes some internal RedisBungee functions. You obtain an instance of this
* 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
* @since 0.2.3
@ -35,12 +34,11 @@ public class RedisBungeeAPI extends AbstractRedisBungeeAPI {
if (redisBungeeApi == null) {
redisBungeeApi = this;
}
}
/**
* Get the server where the specified player is playing. This function also deals with the case of local players
* as well, and will return local information on them.
* Get the server where the specified player is playing. This function also deals with the case of
* local players as well, and will return local information on them.
*
* @param player a player uuid
* @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) {
String serverName = this.getServerNameFor(player);
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
* calls {@link #getUuidFromName(String)} to get uuid
* Kicks a player from the network calls {@link #getUuidFromName(String)} to get uuid
*
* @param playerName player name
* @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);
}
/**
* Api instance
*

View File

@ -1,26 +1,23 @@
/*
* 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
*/
* 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.events;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerChangedServerNetworkEvent;
import java.util.UUID;
/**
* 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.
* <p>
* This event corresponds to {@link com.velocitypowered.api.event.player.ServerConnectedEvent}, and is fired
* asynchronously.
*
* <p>This event corresponds to {@link com.velocitypowered.api.event.player.ServerConnectedEvent},
* and is fired asynchronously.
*
* @since 0.3.4
*/

View File

@ -1,26 +1,23 @@
/*
* 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
*/
* 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.events;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerJoinedNetworkEvent;
import java.util.UUID;
/**
* This event is sent when a player joins the network. RedisBungee sends the event only when
* the 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
* asynchronously.
* This event is sent when a player joins the network. RedisBungee sends the event only when the
* 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 asynchronously.
*
* @since 0.3.4
*/

View File

@ -1,26 +1,23 @@
/*
* 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
*/
* 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.events;
import com.imaginarycode.minecraft.redisbungee.api.events.IPlayerLeftNetworkEvent;
import java.util.UUID;
/**
* This event is sent when a player disconnects. RedisBungee sends the event only when
* the proxy the 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
* asynchronously.
* This event is sent when a player disconnects. RedisBungee sends the event only when the proxy the
* 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 asynchronously.
*
* @since 0.3.4
*/

View File

@ -1,26 +1,23 @@
/*
* 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
*/
* 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.events;
import com.imaginarycode.minecraft.redisbungee.api.events.IPubSubMessageEvent;
/**
* 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
*/
public class PubSubMessageEvent implements IPubSubMessageEvent {
private final String channel;
private final String message;

View File

@ -9,10 +9,12 @@ rootProject.name = "ValioBungee"
fun configureProject(name: String) {
val projectName = ":valiobungee-$name"
include(projectName)
project(projectName).projectDir = file(name)
configureProject(projectName,name)
}
fun configureProject(name: String, path: String) {
include(name)
project(name).projectDir = file(path)
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
@ -24,4 +26,15 @@ dependencyResolutionManagement {
}
}
// main project stuff
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")