2
0
mirror of https://github.com/proxiodev/RedisBungee.git synced 2026-05-03 11:40:29 +00:00

30 Commits
0.6.5 ... 0.7.0

Author SHA1 Message Date
86eeacbb1c disable relocation 2022-04-14 21:41:45 +04:00
43fe50a87f javadocs last changes hopefully 2022-04-14 16:26:33 +04:00
dba8dc8ab7 .gitignore 2022-04-14 16:07:52 +04:00
8bfefc1ab0 seperate events 2022-04-14 15:59:02 +04:00
2bc30ce5f3 update git ignore 2022-04-14 15:54:13 +04:00
070e416fac more java docs stuff + added jedis pool method and deprecate it 2022-04-14 14:34:56 +04:00
102f8591ab java docs 2022-04-14 14:16:34 +04:00
4d0222d2ae added some javadocs info + added jedis summoner class for future release 2022-04-14 14:12:47 +04:00
e53f8a02ba add glist command *reset will be added later 2022-04-14 01:56:17 +04:00
08c7d89749 revert payloads due mass errors 2022-04-14 01:40:16 +04:00
d3889d8bd9 forgot to implement some methods 2022-04-14 01:16:31 +04:00
baab263355 add the updated config 2022-04-13 22:26:36 +04:00
df92d15743 comments were removed accdenitly 2022-04-13 22:23:15 +04:00
3461bbcd1b java docs 2022-04-13 22:19:42 +04:00
7de457b6fa finished but untest 2022-04-13 22:17:38 +04:00
9f09ed21f1 more progress 2022-04-13 20:22:07 +04:00
17fdeda147 more progress 2022-04-13 20:08:46 +04:00
61dec7f03c seperate the internals from bungeecord api for easily supporting platform | progress 60% 2022-04-13 17:15:08 +04:00
165ba84791 update depends 2022-04-13 12:50:58 +04:00
b683d5e226 remove mvn wrapper 2022-04-13 12:29:12 +04:00
b27ddb6cf2 update readme 2022-04-13 12:28:10 +04:00
7c8748e262 update readme 2022-04-13 12:22:35 +04:00
mohammed Alteniji
2df0b9fc60 Update README.md 2022-03-22 16:57:07 +04:00
mohammed Alteniji
bd7d0f4d66 Update README.md 2022-03-22 15:28:41 +04:00
45b4ec65df #24 2021-12-20 05:21:53 +04:00
mohammed Alteniji
bdd02043ea Update README.md 2021-11-16 15:03:16 +04:00
mohammed jasem alaajel
e8439950e3 Update README.md 2021-09-19 19:46:54 +04:00
mohammed jasem alaajel
fbed68e1ab Update README.md 2021-09-19 19:43:25 +04:00
mohammed jasem alaajel
a006c343a7 change redis version check method. 2021-08-09 14:16:07 +04:00
mohammed jasem alaajel
c9798e2371 supresss unused warnings 2021-08-09 14:05:02 +04:00
48 changed files with 1496 additions and 1913 deletions

34
.gitignore vendored
View File

@@ -1,36 +1,36 @@
# Eclipse stuff # Eclipse stuff
/.classpath .classpath
/.project .project
/.settings .settings
# netbeans # netbeans
/nbproject nbproject
/nbactions.xml nbactions.xml
/nb-configuration.xml nb-configuration.xml
# we use maven! # we use maven!
/build.xml build.xml
# maven # maven
/target target
/dependency-reduced-pom.xml dependency-reduced-pom.xml
*/target
*/dependency-reduced-pom.xml
# vim # vim
.*.sw[a-p] .*.sw[a-p]
# various other potential build files # various other potential build files
/build build
/bin bin
/dist dist
/manifest.mf manifest.mf
# Mac filesystem dust # Mac filesystem dust
/.DS_Store .DS_Store
# intellij # intellij
*.iml *.iml
*.ipr *.ipr
*.iws *.iws
.idea/ .idea/
# java docs
javadoc

View File

@@ -1,117 +0,0 @@
/*
* Copyright 2007-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.net.*;
import java.io.*;
import java.nio.channels.*;
import java.util.Properties;
public class MavenWrapperDownloader {
private static final String WRAPPER_VERSION = "0.5.6";
/**
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
*/
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
/**
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
* use instead of the default one.
*/
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
".mvn/wrapper/maven-wrapper.properties";
/**
* Path where the maven-wrapper.jar will be saved to.
*/
private static final String MAVEN_WRAPPER_JAR_PATH =
".mvn/wrapper/maven-wrapper.jar";
/**
* Name of the property which should be used to override the default download url for the wrapper.
*/
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
public static void main(String args[]) {
System.out.println("- Downloader started");
File baseDirectory = new File(args[0]);
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
// If the maven-wrapper.properties exists, read it and check if it contains a custom
// wrapperUrl parameter.
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
String url = DEFAULT_DOWNLOAD_URL;
if(mavenWrapperPropertyFile.exists()) {
FileInputStream mavenWrapperPropertyFileInputStream = null;
try {
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
Properties mavenWrapperProperties = new Properties();
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
} catch (IOException e) {
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
} finally {
try {
if(mavenWrapperPropertyFileInputStream != null) {
mavenWrapperPropertyFileInputStream.close();
}
} catch (IOException e) {
// Ignore ...
}
}
}
System.out.println("- Downloading from: " + url);
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
if(!outputFile.getParentFile().exists()) {
if(!outputFile.getParentFile().mkdirs()) {
System.out.println(
"- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
}
}
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
try {
downloadFileFromURL(url, outputFile);
System.out.println("Done");
System.exit(0);
} catch (Throwable e) {
System.out.println("- Error downloading");
e.printStackTrace();
System.exit(1);
}
}
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
String username = System.getenv("MVNW_USERNAME");
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
}
URL website = new URL(urlString);
ReadableByteChannel rbc;
rbc = Channels.newChannel(website.openStream());
FileOutputStream fos = new FileOutputStream(destination);
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
fos.close();
rbc.close();
}
}

Binary file not shown.

View File

@@ -1,2 +0,0 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar

View File

@@ -1,75 +1,66 @@
# Limework fork of RedisBungee # RedisBungee By Limework
[![RedisBungee Build](https://github.com/Limework/RedisBungee/actions/workflows/maven.yml/badge.svg)](https://github.com/Limework/RedisBungee/actions/workflows/maven.yml) [![](https://jitpack.io/v/limework/redisbungee.svg)](https://jitpack.io/#limework/redisbungee)
[![RedisBungee Build](https://github.com/proxiodev/RedisBungee/actions/workflows/maven.yml/badge.svg)](https://github.com/Limework/RedisBungee/actions/workflows/maven.yml) [![](https://jitpack.io/v/limework/redisbungee.svg)](https://jitpack.io/#limework/redisbungee)
Spigot link: [click](https://www.spigotmc.org/resources/redisbungee.87700/) Spigot link: [click](https://www.spigotmc.org/resources/redisbungee.87700/)
The maintainer of RedisBungee has became inactive, so we have taken the development of the plugin. The main project of RedisBungee is no longer maintained, so we have forked the plugin.
RedisBungee uses [Redis](https://redis.io) to Synchronize data between [BungeeCord](https://github.com/SpigotMC/BungeeCord) proxies RedisBungee uses [Redis](https://redis.io) to Synchronize data between [BungeeCord](https://github.com/SpigotMC/BungeeCord) proxies
## Notice: about older versions of redis than redis 6.0 ## Notice 1: about older versions of redis than redis 6.0
As of now Version 0.6.4 is only supporting redis 6.0 and above! any versions of redis-bungee 0.6.4 or above only supports 6.0
Do not Open issues regarding it.
## Notice 2: users on git.limework.net
please create the issues on GitHub as its main project source.
## Compiling ## Compiling
Now you can use Maven without installing it using [Maven wrappe](https://github.com/takari/maven-wrapper) :)
RedisBungee is distributed as a [maven](https://maven.apache.org) project. RedisBungee is distributed as a [maven](https://maven.apache.org) project.
To compile and installing to in your local Maven repository: To compile the plugin:
git clone https://github.com/Limework/RedisBungee.git . git clone https://github.com/Limework/RedisBungee.git .
mvnw clean install mvn clean package
mvnw package mvn clean install # to install it in your maven local repo
If you have deb maven installed, you can use the `mvn` command instead. then you should find it in target folder.
And use it in your pom file. if you want to use it on your project considering you have installed it in your local repo
<dependency> <dependency>
<groupId>com.imaginarycode.minecraft</groupId> <groupId>com.imaginarycode.minecraft</groupId>
<artifactId>RedisBungee</artifactId> <artifactId>RedisBungee</artifactId>
<version>0.6.5-SNAPSHOT</version> <version>VERSION</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
Or if you want to use the jitpack maven server
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
And use it in your pom file.
<dependency>
<groupId>com.github.limework</groupId>
<artifactId>redisbungee</artifactId>
<version>0.6.4</version>
</dependency>
## Javadocs ## Javadocs
Hosted on limework website. Version 0.6.0 ~~(note: any version 0.6.* will not have API changes.)~~ Check out our Java docs on github pages
https://limework.net/JavaDocs/RedisBungee/ https://proxiodev.github.io/RedisBungee-JavaDocs/0.7.0-SNAPSHOT
Note: we might add more api into RedisBungeeApi Class but we wont remove the old ones to
keep old plugins working....
## Configuration ## Configuration
**REDISBUNGEE REQUIRES A REDIS SERVER**, preferably with reasonably low latency. The default [config](https://github.com/limework/RedisBungee/blob/master/src/main/resources/example_config.yml) is saved when the plugin first starts. **REDISBUNGEE REQUIRES A REDIS SERVER**, preferably with reasonably low latency. The default [config](https://github.com/proxiodev/RedisBungee/blob/master/src/main/resources/example_config.yml) is saved when the plugin first starts.
## License! ## License!
This project is distributed under Eclipse Public License 1.0 This project is distributed under Eclipse Public License 1.0
You can find it [here](https://github.com/Limework/RedisBungee/blob/master/LICENSE) You can find it [here](https://github.com/proxiodev/RedisBungee/blob/master/LICENSE)
You can find the original RedisBungee by minecrafter [here](https://github.com/minecrafter/RedisBungee) or spigot page [here](https://www.spigotmc.org/resources/redisbungee.13494/) You can find the original RedisBungee by minecrafter [here](https://github.com/minecrafter/RedisBungee) or spigot page [here](https://www.spigotmc.org/resources/redisbungee.13494/)
## Support
You can join our matrix room [here](https://matrix.to/#/!zhedzmRNSZXfuOPZUB:govindas.net?via=govindas.net&via=matrix.org)
![icon](https://matrix.org/images/matrix-logo-white.svg)
## YourKit ## YourKit
YourKit supports open source projects with innovative and intelligent tools for monitoring and profiling Java and .NET applications. YourKit is the creator of [YourKit Java Profiler](https://www.yourkit.com/java/profiler/), [YourKit .NET Profiler](https://www.yourkit.com/.net/profiler/) and [YourKit YouMonitor](https://www.yourkit.com/youmonitor/). YourKit supports open source projects with innovative and intelligent tools for monitoring and profiling Java and .NET applications. YourKit is the creator of [YourKit Java Profiler](https://www.yourkit.com/java/profiler/), [YourKit .NET Profiler](https://www.yourkit.com/.net/profiler/) and [YourKit YouMonitor](https://www.yourkit.com/youmonitor/).

35
RedisBungee-API/pom.xml Normal file
View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>RedisBungee</artifactId>
<groupId>com.imaginarycode.minecraft</groupId>
<version>0.7.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>RedisBungee-API</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.3.2</version>
<configuration>
<source>8</source>
<reportOutputDirectory>../javadoc</reportOutputDirectory>
<destDir>${project.name}</destDir>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>

View File

@@ -0,0 +1,32 @@
package com.imaginarycode.minecraft.redisbungee;
/**
* This used to be old plugin instance of redis-bungee, but now it's used to get the api for old plugins
*
* @deprecated its deprecated but won't be removed, so please use {@link RedisBungeeAPI#getRedisBungeeApi()}
*
*/
@Deprecated
public class RedisBungee {
private static RedisBungeeAPI api;
public RedisBungee(RedisBungeeAPI api) {
RedisBungee.api = api;
}
/**
* This returns an instance of {@link RedisBungeeAPI}
*
* @deprecated Please use {@link RedisBungeeAPI#getRedisBungeeApi()} this class intended to for old plugins that no longer updated.
*
* @return the {@link RedisBungeeAPI} object instance.
*/
@Deprecated
public static RedisBungeeAPI getApi() {
return api;
}
}

View File

@@ -4,30 +4,34 @@ import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import lombok.NonNull; import com.imaginarycode.minecraft.redisbungee.internal.RedisBungeePlugin;
import net.md_5.bungee.api.config.ServerInfo; import org.checkerframework.checker.nullness.qual.NonNull;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPool;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.*; import java.util.*;
/** /**
* This class exposes some internal RedisBungee functions. You obtain an instance of this object by invoking {@link RedisBungee#getApi()}. * This 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#getApi()}.
* *
* @author tuxed * @author tuxed
* @since 0.2.3 * @since 0.2.3 | updated 0.7.0
*
*/ */
@SuppressWarnings("unused")
public class RedisBungeeAPI { public class RedisBungeeAPI {
private final RedisBungee plugin; private final RedisBungeePlugin<?> plugin;
private final List<String> reservedChannels; private final List<String> reservedChannels;
private static RedisBungeeAPI redisBungeeApi; private static RedisBungeeAPI redisBungeeApi;
RedisBungeeAPI(RedisBungee plugin) { RedisBungeeAPI(RedisBungeePlugin<?> plugin) {
this.plugin = plugin; this.plugin = plugin;
redisBungeeApi = this; redisBungeeApi = this;
this.reservedChannels = ImmutableList.of( this.reservedChannels = ImmutableList.of(
"redisbungee-allservers", "redisbungee-allservers",
"redisbungee-" + RedisBungee.getConfiguration().getServerId(), "redisbungee-" + plugin.getConfiguration().getServerId(),
"redisbungee-data" "redisbungee-data"
); );
} }
@@ -57,11 +61,10 @@ public class RedisBungeeAPI {
* as well, and will return local information on them. * as well, and will return local information on them.
* *
* @param player a player name * @param player a player name
* @return a {@link net.md_5.bungee.api.config.ServerInfo} for the server the player is on. * @return a String name for the server the player is on.
*/ */
public final ServerInfo getServerFor(@NonNull UUID player) { public final String getServerFor(@NonNull UUID player) {
String server = plugin.getDataManager().getServer(player); return plugin.getDataManager().getServer(player);
return plugin.getProxy().getServerInfo(server);
} }
/** /**
@@ -177,7 +180,8 @@ public class RedisBungeeAPI {
} }
/** /**
* Sends a message to a PubSub channel. The channel has to be subscribed to on this, or another redisbungee instance for {@link com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent} to fire. * Sends a message to a PubSub channel. The channel has to be subscribed to on this, or another redisbungee instance for
* PubSubMessageEvent to fire.
* *
* @param channel The PubSub channel * @param channel The PubSub channel
* @param message the message body to send * @param message the message body to send
@@ -195,7 +199,7 @@ public class RedisBungeeAPI {
* @since 0.2.5 * @since 0.2.5
*/ */
public final String getServerId() { public final String getServerId() {
return RedisBungee.getConfiguration().getServerId(); return plugin.getConfiguration().getServerId();
} }
/** /**
@@ -210,13 +214,13 @@ public class RedisBungeeAPI {
} }
/** /**
* Register (a) PubSub channel(s), so that you may handle {@link com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent} for it. * Register (a) PubSub channel(s), so that you may handle PubSubMessageEvent for it.
* *
* @param channels the channels to register * @param channels the channels to register
* @since 0.3 * @since 0.3
*/ */
public final void registerPubSubChannels(String... channels) { public final void registerPubSubChannels(String... channels) {
RedisBungee.getPubSubListener().addChannel(channels); plugin.getPubSubListener().addChannel(channels);
} }
/** /**
@@ -230,7 +234,7 @@ public class RedisBungeeAPI {
Preconditions.checkArgument(!reservedChannels.contains(channel), "attempting to unregister internal channel"); Preconditions.checkArgument(!reservedChannels.contains(channel), "attempting to unregister internal channel");
} }
RedisBungee.getPubSubListener().removeChannel(channels); plugin.getPubSubListener().removeChannel(channels);
} }
/** /**
@@ -293,26 +297,37 @@ public class RedisBungeeAPI {
* *
* @param name the UUID to fetch the name for * @param name the UUID to fetch the name for
* @param expensiveLookups whether or not to perform potentially expensive lookups * @param expensiveLookups whether or not to perform potentially expensive lookups
* @return the UUID for the name * @return the {@link UUID} for the name
* @since 0.3.2 * @since 0.3.2
*/ */
public final UUID getUuidFromName(@NonNull String name, boolean expensiveLookups) { public final UUID getUuidFromName(@NonNull String name, boolean expensiveLookups) {
return plugin.getUuidTranslator().getTranslatedUuid(name, expensiveLookups); return plugin.getUuidTranslator().getTranslatedUuid(name, expensiveLookups);
} }
/** /**
* This gets Redis Bungee Jedis pool * This gets Redis Bungee {@link JedisPool}
*
* @return {@link JedisPool} * @return {@link JedisPool}
* @deprecated this secluded to be removed when support for redis sentinel or redis cluster is finished, use {@link RedisBungeeAPI#requestJedis()}
* @since 0.6.5 * @since 0.6.5
*/ */
@Deprecated
public JedisPool getJedisPool() { public JedisPool getJedisPool() {
return this.plugin.getPool(); return this.plugin.getJedisPool();
}
/**
* This gives you instance of Jedis
* @return {@link Jedis}
* @since 0.7.0
*/
public Jedis requestJedis() {
return this.plugin.requestJedis();
} }
/** /**
* This alternative to {@link RedisBungee#getApi()}
* which now deprecated. but to maintain old plugins compatibility it won't be removed.
* *
* @return the API instance. * @return the API instance.
* @since 0.6.5 * @since 0.6.5

View File

@@ -0,0 +1,70 @@
package com.imaginarycode.minecraft.redisbungee.internal;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multiset;
import com.google.common.io.ByteArrayDataOutput;
import com.google.gson.Gson;
import java.net.InetAddress;
import java.util.Collection;
import java.util.List;
import java.util.Map;
public abstract class AbstractRedisBungeeListener<LE, PLE, PD, SC, PP, PM, PS> {
protected static final String ALREADY_LOGGED_IN = "§cYou are already logged on to this server. \n\nIt may help to try logging in again in a few minutes.\nIf this does not resolve your issue, please contact staff.";
protected static final String ONLINE_MODE_RECONNECT = "§cWhoops! You need to reconnect\n\nWe found someone online using your username. They were kicked and you may reconnect.\nIf this does not work, please contact staff.";
protected final RedisBungeePlugin<?> plugin;
protected final List<InetAddress> exemptAddresses;
protected final Gson gson = new Gson();
public AbstractRedisBungeeListener(RedisBungeePlugin<?> plugin, List<InetAddress> exemptAddresses) {
this.plugin = plugin;
this.exemptAddresses = exemptAddresses;
}
public abstract void onLogin(LE event);
public abstract void onPostLogin(PLE event);
public abstract void onPlayerDisconnect(PD event);
public abstract void onServerChange(SC event);
public abstract void onPing(PP event);
public abstract void onPluginMessage(PM event);
protected void serializeMultiset(Multiset<String> collection, ByteArrayDataOutput output) {
output.writeInt(collection.elementSet().size());
for (Multiset.Entry<String> entry : collection.entrySet()) {
output.writeUTF(entry.getElement());
output.writeInt(entry.getCount());
}
}
@SuppressWarnings("SameParameterValue")
protected 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());
if (includeNames) {
serializeCollection(entry.getValue(), output);
} else {
output.writeInt(entry.getValue().size());
}
}
}
private void serializeCollection(Collection<?> collection, ByteArrayDataOutput output) {
output.writeInt(collection.size());
for (Object o : collection) {
output.writeUTF(o.toString());
}
}
public abstract void onPubSubMessage(PS event);
}

View File

@@ -1,46 +1,37 @@
package com.imaginarycode.minecraft.redisbungee; package com.imaginarycode.minecraft.redisbungee.internal;
import com.google.common.cache.Cache; import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import com.google.common.net.InetAddresses; import com.google.common.net.InetAddresses;
import com.google.common.reflect.TypeToken;
import com.google.common.util.concurrent.UncheckedExecutionException; import com.google.common.util.concurrent.UncheckedExecutionException;
import com.google.gson.Gson;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
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 lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
import redis.clients.jedis.Jedis; import redis.clients.jedis.Jedis;
import java.lang.reflect.InvocationTargetException;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.Objects; import java.util.Objects;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
/** /**
* This class manages all the data that RedisBungee fetches from Redis, along with updates to that data. * This class manages all the data that RedisBungee fetches from Redis, along with updates to that data.
* *
* @since 0.3.3 * @since 0.3.3
*/ */
public class DataManager implements Listener { public abstract class DataManager<P, PL, PD, PS> {
private final RedisBungee plugin; private final RedisBungeePlugin<P> plugin;
private final Cache<UUID, String> serverCache = createCache(); private final Cache<UUID, String> serverCache = createCache();
private final Cache<UUID, String> proxyCache = createCache(); private final Cache<UUID, String> proxyCache = createCache();
private final Cache<UUID, InetAddress> ipCache = createCache(); private final Cache<UUID, InetAddress> ipCache = createCache();
private final Cache<UUID, Long> lastOnlineCache = createCache(); private final Cache<UUID, Long> lastOnlineCache = createCache();
private final Gson gson = new Gson();
public DataManager(RedisBungee plugin) { public DataManager(RedisBungeePlugin<P> plugin) {
this.plugin = plugin; this.plugin = plugin;
} }
@@ -55,16 +46,16 @@ public class DataManager implements Listener {
private final JsonParser parser = new JsonParser(); private final JsonParser parser = new JsonParser();
public String getServer(final UUID uuid) { public String getServer(final UUID uuid) {
ProxiedPlayer player = plugin.getProxy().getPlayer(uuid); P player = plugin.getPlayer(uuid);
if (player != null) if (player != null)
return player.getServer() != null ? player.getServer().getInfo().getName() : null; return plugin.isPlayerOnAServer(player) ? plugin.getPlayerServerName(player) : null;
try { try {
return serverCache.get(uuid, new Callable<String>() { return serverCache.get(uuid, new Callable<String>() {
@Override @Override
public String call() throws Exception { public String call() throws Exception {
try (Jedis tmpRsc = plugin.getPool().getResource()) { try (Jedis tmpRsc = plugin.requestJedis()) {
return Objects.requireNonNull(tmpRsc.hget("player:" + uuid, "server"), "user not found"); return Objects.requireNonNull(tmpRsc.hget("player:" + uuid, "server"), "user not found");
} }
} }
@@ -72,22 +63,23 @@ public class DataManager implements Listener {
} catch (ExecutionException | UncheckedExecutionException e) { } catch (ExecutionException | UncheckedExecutionException e) {
if (e.getCause() instanceof NullPointerException && e.getCause().getMessage().equals("user not found")) if (e.getCause() instanceof NullPointerException && e.getCause().getMessage().equals("user not found"))
return null; // HACK return null; // HACK
plugin.getLogger().log(Level.SEVERE, "Unable to get server", e); plugin.logFatal("Unable to get server");
throw new RuntimeException("Unable to get server for " + uuid, e); throw new RuntimeException("Unable to get server for " + uuid, e);
} }
} }
public String getProxy(final UUID uuid) { public String getProxy(final UUID uuid) {
ProxiedPlayer player = plugin.getProxy().getPlayer(uuid); P player = plugin.getPlayer(uuid);
if (player != null) if (player != null)
return RedisBungee.getConfiguration().getServerId(); return plugin.getConfiguration().getServerId();
try { try {
return proxyCache.get(uuid, new Callable<String>() { return proxyCache.get(uuid, new Callable<String>() {
@Override @Override
public String call() throws Exception { public String call() throws Exception {
try (Jedis tmpRsc = plugin.getPool().getResource()) { try (Jedis tmpRsc = plugin.requestJedis()) {
return Objects.requireNonNull(tmpRsc.hget("player:" + uuid, "proxy"), "user not found"); return Objects.requireNonNull(tmpRsc.hget("player:" + uuid, "proxy"), "user not found");
} }
} }
@@ -95,22 +87,22 @@ public class DataManager implements Listener {
} catch (ExecutionException | UncheckedExecutionException e) { } catch (ExecutionException | UncheckedExecutionException e) {
if (e.getCause() instanceof NullPointerException && e.getCause().getMessage().equals("user not found")) if (e.getCause() instanceof NullPointerException && e.getCause().getMessage().equals("user not found"))
return null; // HACK return null; // HACK
plugin.getLogger().log(Level.SEVERE, "Unable to get proxy", e); plugin.logFatal("Unable to get proxy");
throw new RuntimeException("Unable to get proxy for " + uuid, e); throw new RuntimeException("Unable to get proxy for " + uuid, e);
} }
} }
public InetAddress getIp(final UUID uuid) { public InetAddress getIp(final UUID uuid) {
ProxiedPlayer player = plugin.getProxy().getPlayer(uuid); P player = plugin.getPlayer(uuid);
if (player != null) if (player != null)
return player.getAddress().getAddress(); return plugin.getPlayerIp(player);
try { try {
return ipCache.get(uuid, new Callable<InetAddress>() { return ipCache.get(uuid, new Callable<InetAddress>() {
@Override @Override
public InetAddress call() throws Exception { public InetAddress call() throws Exception {
try (Jedis tmpRsc = plugin.getPool().getResource()) { try (Jedis tmpRsc = plugin.requestJedis()) {
String result = tmpRsc.hget("player:" + uuid, "ip"); String result = tmpRsc.hget("player:" + uuid, "ip");
if (result == null) if (result == null)
throw new NullPointerException("user not found"); throw new NullPointerException("user not found");
@@ -121,13 +113,13 @@ public class DataManager implements Listener {
} catch (ExecutionException | UncheckedExecutionException e) { } catch (ExecutionException | UncheckedExecutionException e) {
if (e.getCause() instanceof NullPointerException && e.getCause().getMessage().equals("user not found")) if (e.getCause() instanceof NullPointerException && e.getCause().getMessage().equals("user not found"))
return null; // HACK return null; // HACK
plugin.getLogger().log(Level.SEVERE, "Unable to get IP", e); plugin.logFatal("Unable to get IP");
throw new RuntimeException("Unable to get IP for " + uuid, e); throw new RuntimeException("Unable to get IP for " + uuid, e);
} }
} }
public long getLastOnline(final UUID uuid) { public long getLastOnline(final UUID uuid) {
ProxiedPlayer player = plugin.getProxy().getPlayer(uuid); P player = plugin.getPlayer(uuid);
if (player != null) if (player != null)
return 0; return 0;
@@ -136,123 +128,181 @@ public class DataManager implements Listener {
return lastOnlineCache.get(uuid, new Callable<Long>() { return lastOnlineCache.get(uuid, new Callable<Long>() {
@Override @Override
public Long call() throws Exception { public Long call() throws Exception {
try (Jedis tmpRsc = plugin.getPool().getResource()) { try (Jedis tmpRsc = plugin.requestJedis()) {
String result = tmpRsc.hget("player:" + uuid, "online"); String result = tmpRsc.hget("player:" + uuid, "online");
return result == null ? -1 : Long.valueOf(result); return result == null ? -1 : Long.valueOf(result);
} }
} }
}); });
} catch (ExecutionException e) { } catch (ExecutionException e) {
plugin.getLogger().log(Level.SEVERE, "Unable to get last time online", e); plugin.logFatal("Unable to get last time online");
throw new RuntimeException("Unable to get last time online for " + uuid, e); throw new RuntimeException("Unable to get last time online for " + uuid, e);
} }
} }
private void invalidate(UUID uuid) { protected void invalidate(UUID uuid) {
ipCache.invalidate(uuid); ipCache.invalidate(uuid);
lastOnlineCache.invalidate(uuid); lastOnlineCache.invalidate(uuid);
serverCache.invalidate(uuid); serverCache.invalidate(uuid);
proxyCache.invalidate(uuid); proxyCache.invalidate(uuid);
} }
@EventHandler // Invalidate all entries related to this player, since they now lie. (call invalidate(uuid))
public void onPostLogin(PostLoginEvent event) { public abstract void onPostLogin(PL event);
// Invalidate all entries related to this player, since they now lie. // Invalidate all entries related to this player, since they now lie. (call invalidate(uuid))
invalidate(event.getPlayer().getUniqueId()); public abstract void onPlayerDisconnect(PD event);
}
@EventHandler public abstract void onPubSubMessage(PS event);
public void onPlayerDisconnect(PlayerDisconnectEvent event) {
// Invalidate all entries related to this player, since they now lie.
invalidate(event.getPlayer().getUniqueId());
}
@EventHandler protected void handlePubSubMessage(String channel, String message) {
public void onPubSubMessage(PubSubMessageEvent event) { if (!channel.equals("redisbungee-data"))
if (!event.getChannel().equals("redisbungee-data"))
return; return;
// Partially deserialize the message so we can look at the action // Partially deserialize the message so we can look at the action
JsonObject jsonObject = parser.parse(event.getMessage()).getAsJsonObject(); JsonObject jsonObject = parser.parse(message).getAsJsonObject();
String source = jsonObject.get("source").getAsString(); String source = jsonObject.get("source").getAsString();
if (source.equals(RedisBungee.getConfiguration().getServerId())) if (source.equals(plugin.getConfiguration().getServerId()))
return; return;
DataManagerMessage.Action action = DataManagerMessage.Action.valueOf(jsonObject.get("action").getAsString()); DataManagerMessage.Action action = DataManagerMessage.Action.valueOf(jsonObject.get("action").getAsString());
switch (action) { switch (action) {
case JOIN: case JOIN:
final DataManagerMessage<LoginPayload> message1 = RedisBungee.getGson().fromJson(jsonObject, new TypeToken<DataManagerMessage<LoginPayload>>() { final DataManagerMessage<LoginPayload> message1 = gson.fromJson(jsonObject, new TypeToken<DataManagerMessage<LoginPayload>>() {
}.getType()); }.getType());
proxyCache.put(message1.getTarget(), message1.getSource()); proxyCache.put(message1.getTarget(), message1.getSource());
lastOnlineCache.put(message1.getTarget(), (long) 0); lastOnlineCache.put(message1.getTarget(), (long) 0);
ipCache.put(message1.getTarget(), message1.getPayload().getAddress()); ipCache.put(message1.getTarget(), message1.getPayload().getAddress());
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() { plugin.executeAsync(new Runnable() {
@Override @Override
public void run() { public void run() {
plugin.getProxy().getPluginManager().callEvent(new PlayerJoinedNetworkEvent(message1.getTarget())); Object event;
try {
event = plugin.getNetworkJoinEventClass().getDeclaredConstructor(UUID.class).newInstance(message1.getTarget());
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException("unable to dispatch an network join event", e);
}
plugin.callEvent(event);
} }
}); });
break; break;
case LEAVE: case LEAVE:
final DataManagerMessage<LogoutPayload> message2 = RedisBungee.getGson().fromJson(jsonObject, new TypeToken<DataManagerMessage<LogoutPayload>>() { final DataManagerMessage<LogoutPayload> message2 = gson.fromJson(jsonObject, new TypeToken<DataManagerMessage<LogoutPayload>>() {
}.getType()); }.getType());
invalidate(message2.getTarget()); invalidate(message2.getTarget());
lastOnlineCache.put(message2.getTarget(), message2.getPayload().getTimestamp()); lastOnlineCache.put(message2.getTarget(), message2.getPayload().getTimestamp());
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() { plugin.executeAsync(new Runnable() {
@Override @Override
public void run() { public void run() {
plugin.getProxy().getPluginManager().callEvent(new PlayerLeftNetworkEvent(message2.getTarget())); Object event;
try {
event = plugin.getNetworkQuitEventClass().getDeclaredConstructor(UUID.class).newInstance(message2.getTarget());
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException("unable to dispatch an network quit event", e);
}
plugin.callEvent(event);
} }
}); });
break; break;
case SERVER_CHANGE: case SERVER_CHANGE:
final DataManagerMessage<ServerChangePayload> message3 = RedisBungee.getGson().fromJson(jsonObject, new TypeToken<DataManagerMessage<ServerChangePayload>>() { final DataManagerMessage<ServerChangePayload> message3 = gson.fromJson(jsonObject, new TypeToken<DataManagerMessage<ServerChangePayload>>() {
}.getType()); }.getType());
serverCache.put(message3.getTarget(), message3.getPayload().getServer()); serverCache.put(message3.getTarget(), message3.getPayload().getServer());
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() { plugin.executeAsync(new Runnable() {
@Override @Override
public void run() { public void run() {
plugin.getProxy().getPluginManager().callEvent(new PlayerChangedServerNetworkEvent(message3.getTarget(), message3.getPayload().getOldServer(), message3.getPayload().getServer())); Object event;
try {
event = plugin.getServerChangeEventClass().getDeclaredConstructor(UUID.class, String.class, String.class).newInstance(message3.getTarget(), ((ServerChangePayload) message3.getPayload()).getOldServer(), ((ServerChangePayload) message3.getPayload()).getServer());
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException("unable to dispatch an server change event", e);
}
plugin.callEvent(event);
} }
}); });
break; break;
} }
} }
@Getter public static class DataManagerMessage<T> {
@RequiredArgsConstructor
static class DataManagerMessage<T> {
private final UUID target; private final UUID target;
private final String source = RedisBungee.getApi().getServerId(); private final String source;
private final Action action; // for future use! private final Action action; // for future use!
private final T payload; private final T payload;
enum Action { public DataManagerMessage(UUID target, String source, Action action, T payload) {
this.target = target;
this.source = source;
this.action = action;
this.payload = payload;
}
public UUID getTarget() {
return target;
}
public String getSource() {
return source;
}
public Action getAction() {
return action;
}
public T getPayload() {
return payload;
}
public enum Action {
JOIN, JOIN,
LEAVE, LEAVE,
SERVER_CHANGE SERVER_CHANGE
} }
} }
@Getter public static class LoginPayload {
@RequiredArgsConstructor
static class LoginPayload {
private final InetAddress address; private final InetAddress address;
public LoginPayload(InetAddress address) {
this.address = address;
}
public InetAddress getAddress() {
return address;
}
} }
@Getter public static class ServerChangePayload{
@RequiredArgsConstructor
static class ServerChangePayload {
private final String server; private final String server;
private final String oldServer; private final String oldServer;
public ServerChangePayload(String server, String oldServer) {
this.server = server;
this.oldServer = oldServer;
}
public String getServer() {
return server;
}
public String getOldServer() {
return oldServer;
}
} }
@Getter
@RequiredArgsConstructor public static class LogoutPayload {
static class LogoutPayload {
private final long timestamp; private final long timestamp;
public LogoutPayload(long timestamp) {
this.timestamp = timestamp;
}
public long getTimestamp() {
return timestamp;
}
} }
} }

View File

@@ -0,0 +1,20 @@
package com.imaginarycode.minecraft.redisbungee.internal;
/**
* 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 {
Class<?> getPubSubEventClass();
Class<?> getNetworkJoinEventClass();
Class<?> getServerChangeEventClass();
Class<?> getNetworkQuitEventClass();
}

View File

@@ -0,0 +1,34 @@
package com.imaginarycode.minecraft.redisbungee.internal;
import redis.clients.jedis.JedisPubSub;
import java.lang.reflect.InvocationTargetException;
public class JedisPubSubHandler extends JedisPubSub {
private final RedisBungeePlugin<?> plugin;
private final Class<?> eventClass;
public JedisPubSubHandler(RedisBungeePlugin<?> plugin) {
this.plugin = plugin;
this.eventClass = plugin.getPubSubEventClass();
}
@Override
public void onMessage(final String s, final String s2) {
if (s2.trim().length() == 0) return;
plugin.executeAsync(new Runnable() {
@Override
public void run() {
Object event;
try {
event = eventClass.getDeclaredConstructor(String.class, String.class).newInstance(s, s2);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException("unable to dispatch an pubsub event", e);
}
plugin.callEvent(event);
}
});
}
}

View File

@@ -0,0 +1,23 @@
package com.imaginarycode.minecraft.redisbungee.internal;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
/**
* This class intended for future release to support redis sentinel or redis clusters
*
* @author Ham1255
* @since 0.7.0
*
*/
public interface JedisSummoner {
Jedis requestJedis();
boolean isJedisAvailable();
@Deprecated
JedisPool getJedisPool();
}

View File

@@ -0,0 +1,72 @@
package com.imaginarycode.minecraft.redisbungee.internal;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisConnectionException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
public class PubSubListener implements Runnable {
private JedisPubSubHandler jpsh;
private final Set<String> addedChannels = new HashSet<String>();
private final RedisBungeePlugin<?> plugin;
public PubSubListener(RedisBungeePlugin<?> plugin) {
this.plugin = plugin;
}
@Override
public void run() {
boolean broken = false;
try (Jedis rsc = plugin.requestJedis()) {
try {
jpsh = new JedisPubSubHandler(plugin);
addedChannels.add("redisbungee-" + plugin.getConfiguration().getServerId());
addedChannels.add("redisbungee-allservers");
addedChannels.add("redisbungee-data");
rsc.subscribe(jpsh, addedChannels.toArray(new String[0]));
} catch (Exception e) {
// FIXME: Extremely ugly hack
// Attempt to unsubscribe this instance and try again.
plugin.logWarn("PubSub error, attempting to recover.");
try {
jpsh.unsubscribe();
} catch (Exception e1) {
/* This may fail with
- java.net.SocketException: Broken pipe
- redis.clients.jedis.exceptions.JedisConnectionException: JedisPubSub was not subscribed to a Jedis instance
*/
}
broken = true;
}
} catch (JedisConnectionException e) {
plugin.logWarn("PubSub error, attempting to recover in 5 secs.");
plugin.executeAsyncAfter(this, TimeUnit.SECONDS, 5);
}
if (broken) {
run();
}
}
public void addChannel(String... channel) {
addedChannels.addAll(Arrays.asList(channel));
jpsh.subscribe(channel);
}
public void removeChannel(String... channel) {
Arrays.asList(channel).forEach(addedChannels::remove);
jpsh.unsubscribe(channel);
}
public void poison() {
addedChannels.clear();
jpsh.unsubscribe();
}
}

View File

@@ -0,0 +1,36 @@
package com.imaginarycode.minecraft.redisbungee.internal;
import com.google.common.collect.ImmutableList;
import com.google.common.net.InetAddresses;
import java.net.InetAddress;
import java.util.List;
public class RedisBungeeConfiguration {
private final String serverId;
private final List<InetAddress> exemptAddresses;
private static RedisBungeeConfiguration config;
public RedisBungeeConfiguration(String serverId, List<String> exemptAddresses) {
this.serverId = serverId;
ImmutableList.Builder<InetAddress> addressBuilder = ImmutableList.builder();
for (String s : exemptAddresses) {
addressBuilder.add(InetAddresses.forString(s));
}
this.exemptAddresses = addressBuilder.build();
config = this;
}
public String getServerId() {
return serverId;
}
public List<InetAddress> getExemptAddresses() {
return exemptAddresses;
}
public static RedisBungeeConfiguration getConfig() {
return config;
}
}

View File

@@ -0,0 +1,96 @@
package com.imaginarycode.minecraft.redisbungee.internal;
import com.google.common.collect.Multimap;
import com.imaginarycode.minecraft.redisbungee.RedisBungeeAPI;
import com.imaginarycode.minecraft.redisbungee.internal.util.uuid.UUIDTranslator;
import java.net.InetAddress;
import java.util.List;
import java.util.Set;
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
*
* @author Ham1255
* @since 0.7.0
*
*/
public interface RedisBungeePlugin<P> extends JedisSummoner, EventsPlatform{
default void enable() {
}
default void disable() {
}
RedisBungeeConfiguration getConfiguration();
int getCount();
int getCurrentCount();
Set<String> getLocalPlayersAsUuidStrings();
DataManager<P, ?, ?, ?> getDataManager();
Set<UUID> getPlayers();
RedisBungeeAPI getApi();
UUIDTranslator getUuidTranslator();
Multimap<String, UUID> serversToPlayers();
Set<UUID> getPlayersOnProxy(String proxyId);
void sendProxyCommand(String serverId, String command);
List<String> getServerIds();
List<String > getCurrentServerIds(boolean nag, boolean lagged);
PubSubListener getPubSubListener();
void sendChannelMessage(String channel, String message);
void executeAsync(Runnable runnable);
void executeAsyncAfter(Runnable runnable, TimeUnit timeUnit, int time);
void callEvent(Object event);
boolean isOnlineMode();
void logInfo(String msg);
void logWarn(String msg);
void logFatal(String msg);
P getPlayer(UUID uuid);
P getPlayer(String name);
UUID getPlayerUUID(String player);
String getPlayerName(UUID player);
String getPlayerServerName(P player);
boolean isPlayerOnAServer(P player);
InetAddress getPlayerIp(P player);
void sendProxyCommand(String cmd);
long getRedisTime(List<String> timeRes);
void loadConfig() throws Exception;
}

View File

@@ -0,0 +1,51 @@
package com.imaginarycode.minecraft.redisbungee.internal;
import com.google.common.annotations.VisibleForTesting;
import com.google.gson.Gson;
import com.imaginarycode.minecraft.redisbungee.RedisBungeeAPI;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
import java.util.UUID;
@VisibleForTesting
public class RedisUtil {
private static final Gson gson = new Gson();
public static void cleanUpPlayer(String player, Jedis rsc) {
rsc.srem("proxy:" + RedisBungeeAPI.getRedisBungeeApi().getServerId() + ":usersOnline", player);
rsc.hdel("player:" + player, "server", "ip", "proxy");
long timestamp = System.currentTimeMillis();
rsc.hset("player:" + player, "online", String.valueOf(timestamp));
rsc.publish("redisbungee-data", gson.toJson(new DataManager.DataManagerMessage<>(
UUID.fromString(player), RedisBungeeAPI.getRedisBungeeApi().getServerId(), DataManager.DataManagerMessage.Action.LEAVE,
new DataManager.LogoutPayload(timestamp))));
}
public static void cleanUpPlayer(String player, Pipeline rsc) {
rsc.srem("proxy:" + RedisBungeeAPI.getRedisBungeeApi().getServerId() + ":usersOnline", player);
rsc.hdel("player:" + player, "server", "ip", "proxy");
long timestamp = System.currentTimeMillis();
rsc.hset("player:" + player, "online", String.valueOf(timestamp));
rsc.publish("redisbungee-data", gson.toJson(new DataManager.DataManagerMessage<>(
UUID.fromString(player), RedisBungeeAPI.getRedisBungeeApi().getServerId(), DataManager.DataManagerMessage.Action.LEAVE,
new DataManager.LogoutPayload(timestamp))));
}
public static boolean isRedisVersionRight(String redisVersion) {
// Need to use >=6.2 to use Lua optimizations.
String[] args = redisVersion.split("\\.");
if (args.length < 2) {
return false;
}
int major = Integer.parseInt(args[0]);
int minor = Integer.parseInt(args[1]);
return major >= 6 && minor >= 0;
}
// Ham1255: i am keeping this if some plugin uses this *IF*
@Deprecated
public static boolean canUseLua(String redisVersion) {
return isRedisVersionRight(redisVersion);
}
}

View File

@@ -1,14 +1,12 @@
package com.imaginarycode.minecraft.redisbungee.util; package com.imaginarycode.minecraft.redisbungee.internal.util;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class IOUtil { public class IOUtil {
public static String readInputStreamAsString(InputStream is) { public static String readInputStreamAsString(InputStream is) {
String string; String string;

View File

@@ -1,32 +1,46 @@
package com.imaginarycode.minecraft.redisbungee.util; package com.imaginarycode.minecraft.redisbungee.internal.util;
import com.imaginarycode.minecraft.redisbungee.RedisBungee; import com.imaginarycode.minecraft.redisbungee.internal.RedisBungeePlugin;
import lombok.RequiredArgsConstructor;
import redis.clients.jedis.Jedis; import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisDataException; import redis.clients.jedis.exceptions.JedisDataException;
import java.util.List; import java.util.List;
@RequiredArgsConstructor
public class LuaManager { public class LuaManager {
private final RedisBungee plugin; private final RedisBungeePlugin<?> plugin;
public LuaManager(RedisBungeePlugin<?> plugin) {
this.plugin = plugin;
}
public Script createScript(String script) { public Script createScript(String script) {
try (Jedis jedis = plugin.getPool().getResource()) { try (Jedis jedis = plugin.requestJedis()) {
String hash = jedis.scriptLoad(script); String hash = jedis.scriptLoad(script);
return new Script(script, hash); return new Script(script, hash);
} }
} }
@RequiredArgsConstructor
public class Script { public class Script {
private final String script; private final String script;
private final String hashed; private final String hashed;
public Script(String script, String hashed) {
this.script = script;
this.hashed = hashed;
}
public String getScript() {
return script;
}
public String getHashed() {
return hashed;
}
public Object eval(List<String> keys, List<String> args) { public Object eval(List<String> keys, List<String> args) {
Object data; Object data;
try (Jedis jedis = plugin.getPool().getResource()) { try (Jedis jedis = plugin.requestJedis()) {
try { try {
data = jedis.evalsha(hashed, keys, args); data = jedis.evalsha(hashed, keys, args);
} catch (JedisDataException e) { } catch (JedisDataException e) {

View File

@@ -1,16 +1,19 @@
package com.imaginarycode.minecraft.redisbungee.util; package com.imaginarycode.minecraft.redisbungee.internal.util;
import com.imaginarycode.minecraft.redisbungee.RedisBungee; import com.imaginarycode.minecraft.redisbungee.internal.RedisBungeePlugin;
import lombok.AllArgsConstructor;
import redis.clients.jedis.Jedis; import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisConnectionException;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.logging.Level;
@AllArgsConstructor
public abstract class RedisCallable<T> implements Callable<T>, Runnable { public abstract class RedisCallable<T> implements Callable<T>, Runnable {
private final RedisBungee plugin; private final RedisBungeePlugin<?> plugin;
public RedisCallable(RedisBungeePlugin<?> plugin) {
this.plugin = plugin;
}
@Override @Override
public T call() { public T call() {
@@ -22,10 +25,10 @@ public abstract class RedisCallable<T> implements Callable<T>, Runnable {
} }
private T run(boolean retry) { private T run(boolean retry) {
try (Jedis jedis = plugin.getPool().getResource()) { try (Jedis jedis = plugin.requestJedis()) {
return call(jedis); return call(jedis);
} catch (JedisConnectionException e) { } catch (JedisConnectionException e) {
plugin.getLogger().log(Level.SEVERE, "Unable to get connection", e); plugin.logFatal("Unable to get connection");
if (!retry) { if (!retry) {
// Wait one second before retrying the task // Wait one second before retrying the task

View File

@@ -1,24 +1,23 @@
package com.imaginarycode.minecraft.redisbungee.util.uuid; package com.imaginarycode.minecraft.redisbungee.internal.util.uuid;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import com.imaginarycode.minecraft.redisbungee.RedisBungee;
import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request; import com.squareup.okhttp.Request;
import com.squareup.okhttp.ResponseBody; import com.squareup.okhttp.ResponseBody;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class NameFetcher { public class NameFetcher {
@Setter
private static OkHttpClient httpClient; private static OkHttpClient httpClient;
private static final Gson gson = new Gson();
public static void setHttpClient(OkHttpClient httpClient) {
NameFetcher.httpClient = httpClient;
}
public static List<String> nameHistoryFromUuid(UUID uuid) throws IOException { public static List<String> nameHistoryFromUuid(UUID uuid) throws IOException {
String url = "https://api.mojang.com/user/profiles/" + uuid.toString().replace("-", "") + "/names"; String url = "https://api.mojang.com/user/profiles/" + uuid.toString().replace("-", "") + "/names";
@@ -29,7 +28,7 @@ public class NameFetcher {
Type listType = new TypeToken<List<Name>>() { Type listType = new TypeToken<List<Name>>() {
}.getType(); }.getType();
List<Name> names = RedisBungee.getGson().fromJson(response, listType); List<Name> names = gson.fromJson(response, listType);
List<String> humanNames = new ArrayList<>(); List<String> humanNames = new ArrayList<>();
for (Name name : names) { for (Name name : names) {

View File

@@ -1,9 +1,8 @@
package com.imaginarycode.minecraft.redisbungee.util.uuid; package com.imaginarycode.minecraft.redisbungee.internal.util.uuid;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.imaginarycode.minecraft.redisbungee.RedisBungee; import com.google.gson.Gson;
import com.squareup.okhttp.*; import com.squareup.okhttp.*;
import lombok.Setter;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@@ -18,8 +17,13 @@ public class UUIDFetcher implements Callable<Map<String, UUID>> {
private static final MediaType JSON = MediaType.parse("application/json"); private static final MediaType JSON = MediaType.parse("application/json");
private final List<String> names; private final List<String> names;
private final boolean rateLimiting; private final boolean rateLimiting;
private static final Gson gson = new Gson();
public static void setHttpClient(OkHttpClient httpClient) {
UUIDFetcher.httpClient = httpClient;
}
@Setter
private static OkHttpClient httpClient; private static OkHttpClient httpClient;
private UUIDFetcher(List<String> names, boolean rateLimiting) { private UUIDFetcher(List<String> names, boolean rateLimiting) {
@@ -39,12 +43,12 @@ public class UUIDFetcher implements Callable<Map<String, UUID>> {
Map<String, UUID> uuidMap = new HashMap<>(); Map<String, UUID> uuidMap = new HashMap<>();
int requests = (int) Math.ceil(names.size() / PROFILES_PER_REQUEST); int requests = (int) Math.ceil(names.size() / PROFILES_PER_REQUEST);
for (int i = 0; i < requests; i++) { for (int i = 0; i < requests; i++) {
String body = RedisBungee.getGson().toJson(names.subList(i * 100, Math.min((i + 1) * 100, names.size()))); String body = gson.toJson(names.subList(i * 100, Math.min((i + 1) * 100, names.size())));
Request request = new Request.Builder().url(PROFILE_URL).post(RequestBody.create(JSON, body)).build(); Request request = new Request.Builder().url(PROFILE_URL).post(RequestBody.create(JSON, body)).build();
ResponseBody responseBody = httpClient.newCall(request).execute().body(); ResponseBody responseBody = httpClient.newCall(request).execute().body();
String response = responseBody.string(); String response = responseBody.string();
responseBody.close(); responseBody.close();
Profile[] array = RedisBungee.getGson().fromJson(response, Profile[].class); Profile[] array = gson.fromJson(response, Profile[].class);
for (Profile profile : array) { for (Profile profile : array) {
UUID uuid = UUIDFetcher.getUUID(profile.id); UUID uuid = UUIDFetcher.getUUID(profile.id);
uuidMap.put(profile.name, uuid); uuidMap.put(profile.name, uuid);

View File

@@ -1,13 +1,12 @@
package com.imaginarycode.minecraft.redisbungee.util.uuid; package com.imaginarycode.minecraft.redisbungee.internal.util.uuid;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.imaginarycode.minecraft.redisbungee.RedisBungee; import com.google.gson.Gson;
import lombok.Getter; import com.imaginarycode.minecraft.redisbungee.internal.RedisBungeePlugin;
import lombok.NonNull;
import lombok.RequiredArgsConstructor; import org.checkerframework.checker.nullness.qual.NonNull;
import net.md_5.bungee.api.ProxyServer;
import redis.clients.jedis.Jedis; import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline; import redis.clients.jedis.Pipeline;
import redis.clients.jedis.exceptions.JedisException; import redis.clients.jedis.exceptions.JedisException;
@@ -17,13 +16,17 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@RequiredArgsConstructor
public final class UUIDTranslator { public final class UUIDTranslator {
private static final Pattern UUID_PATTERN = Pattern.compile("[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"); private static final Pattern UUID_PATTERN = Pattern.compile("[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}");
private static final Pattern MOJANGIAN_UUID_PATTERN = Pattern.compile("[a-fA-F0-9]{32}"); private static final Pattern MOJANGIAN_UUID_PATTERN = Pattern.compile("[a-fA-F0-9]{32}");
private final RedisBungee plugin; private final RedisBungeePlugin<?> plugin;
private final Map<String, CachedUUIDEntry> nameToUuidMap = new ConcurrentHashMap<>(128, 0.5f, 4); private final Map<String, CachedUUIDEntry> nameToUuidMap = new ConcurrentHashMap<>(128, 0.5f, 4);
private final Map<UUID, CachedUUIDEntry> uuidToNameMap = new ConcurrentHashMap<>(128, 0.5f, 4); private final Map<UUID, CachedUUIDEntry> uuidToNameMap = new ConcurrentHashMap<>(128, 0.5f, 4);
private static final Gson gson = new Gson();
public UUIDTranslator(RedisBungeePlugin<?> plugin) {
this.plugin = plugin;
}
private void addToMaps(String name, UUID uuid) { private void addToMaps(String name, UUID uuid) {
// This is why I like LocalDate... // This is why I like LocalDate...
@@ -41,8 +44,8 @@ public final class UUIDTranslator {
public final UUID getTranslatedUuid(@NonNull String player, boolean expensiveLookups) { public final UUID getTranslatedUuid(@NonNull String player, boolean expensiveLookups) {
// If the player is online, give them their UUID. // If the player is online, give them their UUID.
// Remember, local data > remote data. // Remember, local data > remote data.
if (ProxyServer.getInstance().getPlayer(player) != null) if (plugin.getPlayer(player) != null)
return ProxyServer.getInstance().getPlayer(player).getUniqueId(); return plugin.getPlayerUUID(player);
// Check if it exists in the map // Check if it exists in the map
CachedUUIDEntry cachedUUIDEntry = nameToUuidMap.get(player.toLowerCase()); CachedUUIDEntry cachedUUIDEntry = nameToUuidMap.get(player.toLowerCase());
@@ -65,16 +68,16 @@ public final class UUIDTranslator {
// If we are in offline mode, UUID generation is simple. // If we are in offline mode, UUID generation is simple.
// We don't even have to cache the UUID, since this is easy to recalculate. // We don't even have to cache the UUID, since this is easy to recalculate.
if (!plugin.getProxy().getConfig().isOnlineMode()) { if (!plugin.isOnlineMode()) {
return UUID.nameUUIDFromBytes(("OfflinePlayer:" + player).getBytes(Charsets.UTF_8)); return UUID.nameUUIDFromBytes(("OfflinePlayer:" + player).getBytes(Charsets.UTF_8));
} }
// Let's try Redis. // Let's try Redis.
try (Jedis jedis = plugin.getPool().getResource()) { try (Jedis jedis = plugin.requestJedis()) {
String stored = jedis.hget("uuid-cache", player.toLowerCase()); String stored = jedis.hget("uuid-cache", player.toLowerCase());
if (stored != null) { if (stored != null) {
// Found an entry value. Deserialize it. // Found an entry value. Deserialize it.
CachedUUIDEntry entry = RedisBungee.getGson().fromJson(stored, CachedUUIDEntry.class); CachedUUIDEntry entry = gson.fromJson(stored, CachedUUIDEntry.class);
// Check for expiry: // Check for expiry:
if (entry.expired()) { if (entry.expired()) {
@@ -89,14 +92,14 @@ public final class UUIDTranslator {
} }
// That didn't work. Let's ask Mojang. // That didn't work. Let's ask Mojang.
if (!expensiveLookups || !ProxyServer.getInstance().getConfig().isOnlineMode()) if (!expensiveLookups || !plugin.isOnlineMode())
return null; return null;
Map<String, UUID> uuidMap1; Map<String, UUID> uuidMap1;
try { try {
uuidMap1 = new UUIDFetcher(Collections.singletonList(player)).call(); uuidMap1 = new UUIDFetcher(Collections.singletonList(player)).call();
} catch (Exception e) { } catch (Exception e) {
plugin.getLogger().log(Level.SEVERE, "Unable to fetch UUID from Mojang for " + player, e); plugin.logFatal("Unable to fetch UUID from Mojang for " + player);
return null; return null;
} }
for (Map.Entry<String, UUID> entry : uuidMap1.entrySet()) { for (Map.Entry<String, UUID> entry : uuidMap1.entrySet()) {
@@ -106,7 +109,7 @@ public final class UUIDTranslator {
} }
} }
} catch (JedisException e) { } catch (JedisException e) {
plugin.getLogger().log(Level.SEVERE, "Unable to fetch UUID for " + player, e); plugin.logFatal("Unable to fetch UUID for " + player);
} }
return null; // Nope, game over! return null; // Nope, game over!
@@ -115,8 +118,8 @@ public final class UUIDTranslator {
public final String getNameFromUuid(@NonNull UUID player, boolean expensiveLookups) { public final String getNameFromUuid(@NonNull UUID player, boolean expensiveLookups) {
// If the player is online, give them their UUID. // If the player is online, give them their UUID.
// Remember, local data > remote data. // Remember, local data > remote data.
if (ProxyServer.getInstance().getPlayer(player) != null) if (plugin.getPlayer(player) != null)
return ProxyServer.getInstance().getPlayer(player).getName(); return plugin.getPlayerName(player);
// Check if it exists in the map // Check if it exists in the map
CachedUUIDEntry cachedUUIDEntry = uuidToNameMap.get(player); CachedUUIDEntry cachedUUIDEntry = uuidToNameMap.get(player);
@@ -128,11 +131,11 @@ public final class UUIDTranslator {
} }
// Okay, it wasn't locally cached. Let's try Redis. // Okay, it wasn't locally cached. Let's try Redis.
try (Jedis jedis = plugin.getPool().getResource()) { try (Jedis jedis = plugin.requestJedis()) {
String stored = jedis.hget("uuid-cache", player.toString()); String stored = jedis.hget("uuid-cache", player.toString());
if (stored != null) { if (stored != null) {
// Found an entry value. Deserialize it. // Found an entry value. Deserialize it.
CachedUUIDEntry entry = RedisBungee.getGson().fromJson(stored, CachedUUIDEntry.class); CachedUUIDEntry entry = gson.fromJson(stored, CachedUUIDEntry.class);
// Check for expiry: // Check for expiry:
if (entry.expired()) { if (entry.expired()) {
@@ -147,7 +150,7 @@ public final class UUIDTranslator {
} }
} }
if (!expensiveLookups || !ProxyServer.getInstance().getConfig().isOnlineMode()) if (!expensiveLookups || !plugin.isOnlineMode())
return null; return null;
// That didn't work. Let's ask Mojang. This call may fail, because Mojang is insane. // That didn't work. Let's ask Mojang. This call may fail, because Mojang is insane.
@@ -156,7 +159,7 @@ public final class UUIDTranslator {
List<String> nameHist = NameFetcher.nameHistoryFromUuid(player); List<String> nameHist = NameFetcher.nameHistoryFromUuid(player);
name = Iterables.getLast(nameHist, null); name = Iterables.getLast(nameHist, null);
} catch (Exception e) { } catch (Exception e) {
plugin.getLogger().log(Level.SEVERE, "Unable to fetch name from Mojang for " + player, e); plugin.logFatal("Unable to fetch name from Mojang for " + player);
return null; return null;
} }
@@ -167,30 +170,46 @@ public final class UUIDTranslator {
return null; return null;
} catch (JedisException e) { } catch (JedisException e) {
plugin.getLogger().log(Level.SEVERE, "Unable to fetch name for " + player, e); plugin.logFatal("Unable to fetch name for " + player);
return null; return null;
} }
} }
public final void persistInfo(String name, UUID uuid, Jedis jedis) { public final void persistInfo(String name, UUID uuid, Jedis jedis) {
addToMaps(name, uuid); addToMaps(name, uuid);
String json = RedisBungee.getGson().toJson(uuidToNameMap.get(uuid)); String json = gson.toJson(uuidToNameMap.get(uuid));
jedis.hmset("uuid-cache", ImmutableMap.of(name.toLowerCase(), json, uuid.toString(), json)); jedis.hmset("uuid-cache", ImmutableMap.of(name.toLowerCase(), json, uuid.toString(), json));
} }
public final void persistInfo(String name, UUID uuid, Pipeline jedis) { public final void persistInfo(String name, UUID uuid, Pipeline jedis) {
addToMaps(name, uuid); addToMaps(name, uuid);
String json = RedisBungee.getGson().toJson(uuidToNameMap.get(uuid)); String json = gson.toJson(uuidToNameMap.get(uuid));
jedis.hmset("uuid-cache", ImmutableMap.of(name.toLowerCase(), json, uuid.toString(), json)); jedis.hmset("uuid-cache", ImmutableMap.of(name.toLowerCase(), json, uuid.toString(), json));
} }
@RequiredArgsConstructor private static class CachedUUIDEntry {
@Getter
private class CachedUUIDEntry {
private final String name; private final String name;
private final UUID uuid; private final UUID uuid;
private final Calendar expiry; private final Calendar expiry;
public CachedUUIDEntry(String name, UUID uuid, Calendar expiry) {
this.name = name;
this.uuid = uuid;
this.expiry = expiry;
}
public String getName() {
return name;
}
public UUID getUuid() {
return uuid;
}
public Calendar getExpiry() {
return expiry;
}
public boolean expired() { public boolean expired() {
return Calendar.getInstance().after(expiry); return Calendar.getInstance().after(expiry);
} }

View File

@@ -8,21 +8,16 @@ redis-port: 6379
# OPTIONAL: If your Redis server uses AUTH, set the password required. # OPTIONAL: If your Redis server uses AUTH, set the password required.
redis-password: "" redis-password: ""
# Maximum connections that will be maintained to the Redis server. # Maximum connections that will be maintained to the Redis server.
# The default is 8. This setting should be left as-is unless you have some wildly # The default is 10. This setting should be left as-is unless you have some wildly
# inefficient plugins or a lot of players. # inefficient plugins or a lot of players.
max-redis-connections: 8 max-redis-connections: 10
# since redis can support ssl by version 6 you can use ssl in redis bungee too! # since redis can support ssl by version 6 you can use ssl in redis bungee too!
# you must disable this if redis version is under 6 you must disable this or connection wont work!!! # but there is more configuration needed to work see https://github.com/ProxioDev/RedisBungee/issues/18
useSSL: false useSSL: false
# An identifier for this BungeeCord instance. Will randomly generate if leaving it blank. # An identifier for this BungeeCord instance. Will randomly generate if leaving it blank.
server-id: "test1" server-id: "test1"
# Should use random string? if this is enabled the proxy id will be like this if server-id is test1: "test1-66cd2aeb-91f3-43a7-a106-e0307b098652"
# or if id is limework-bungee it will be "limework-bungee-66cd2aeb-91f3-43a7-a106-e0307b098652"
# this great for servers who run replicas in Kubernetes or any auto deploying replica service
# and used for if proxy died in a kubernetes network and deleted then new proxy setup itself.
use-random-id-string: false
# Whether or not RedisBungee should install its version of regular BungeeCord commands. # Whether or not RedisBungee should install its version of regular BungeeCord commands.
# Often, the RedisBungee commands are desired, but in some cases someone may wish to # Often, the RedisBungee commands are desired, but in some cases someone may wish to

109
RedisBungee-Bungee/pom.xml Normal file
View File

@@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>RedisBungee</artifactId>
<groupId>com.imaginarycode.minecraft</groupId>
<version>0.7.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>RedisBungee-Bungee</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<repositories>
<repository>
<id>bungeecord-repo</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<!--
<relocations>
<relocation>
<pattern>redis.clients.jedis</pattern>
<shadedPattern>com.imaginarycode.minecraft.redisbungee.internal.jedis
</shadedPattern>
</relocation>
<relocation>
<pattern>redis.clients.util</pattern>
<shadedPattern>com.imaginarycode.minecraft.redisbungee.internal.jedisutil
</shadedPattern>
</relocation>
<relocation>
<pattern>org.apache.commons.pool</pattern>
<shadedPattern>com.imaginarycode.minecraft.redisbungee.internal.commonspool
</shadedPattern>
</relocation>
<relocation>
<pattern>com.squareup.okhttp</pattern>
<shadedPattern>com.imaginarycode.minecraft.redisbungee.internal.okhttp
</shadedPattern>
</relocation>
<relocation>
<pattern>okio</pattern>
<shadedPattern>com.imaginarycode.minecraft.redisbungee.internal.okio
</shadedPattern>
</relocation>
</relocations>
-->
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.imaginarycode.minecraft</groupId>
<artifactId>RedisBungee-API</artifactId>
<version>0.7.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.imaginarycode.minecraft</groupId>
<artifactId>RedisBungee-BungeeEvents</artifactId>
<version>0.7.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>1.17-R0.1-SNAPSHOT</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,35 @@
package com.imaginarycode.minecraft.redisbungee;
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
import com.imaginarycode.minecraft.redisbungee.internal.DataManager;
import com.imaginarycode.minecraft.redisbungee.internal.RedisBungeePlugin;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
public class BungeeDataManager extends DataManager<ProxiedPlayer, PostLoginEvent, PlayerDisconnectEvent, PubSubMessageEvent> implements Listener {
public BungeeDataManager(RedisBungeePlugin<ProxiedPlayer> plugin) {
super(plugin);
}
@Override
@EventHandler
public void onPostLogin(PostLoginEvent event) {
invalidate(event.getPlayer().getUniqueId());
}
@Override
@EventHandler
public void onPlayerDisconnect(PlayerDisconnectEvent event) {
invalidate(event.getPlayer().getUniqueId());
}
@Override
@EventHandler
public void onPubSubMessage(PubSubMessageEvent event) {
handlePubSubMessage(event.getChannel(), event.getMessage());
}
}

View File

@@ -0,0 +1,39 @@
package com.imaginarycode.minecraft.redisbungee;
import com.google.gson.Gson;
import com.imaginarycode.minecraft.redisbungee.internal.DataManager;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import redis.clients.jedis.Pipeline;
import java.util.HashMap;
import java.util.Map;
public class RBUtils {
private static final Gson gson = new Gson();
protected static void createPlayer(ProxiedPlayer player, Pipeline pipeline, boolean fireEvent) {
createPlayer(player.getPendingConnection(), pipeline, fireEvent);
if (player.getServer() != null)
pipeline.hset("player:" + player.getUniqueId().toString(), "server", player.getServer().getInfo().getName());
}
protected static void createPlayer(PendingConnection connection, Pipeline pipeline, boolean fireEvent) {
Map<String, String> playerData = new HashMap<>(4);
playerData.put("online", "0");
playerData.put("ip", connection.getAddress().getAddress().getHostAddress());
playerData.put("proxy", RedisBungeeAPI.getRedisBungeeApi().getServerId());
pipeline.sadd("proxy:" + RedisBungeeAPI.getRedisBungeeApi().getServerId() + ":usersOnline", connection.getUniqueId().toString());
pipeline.hmset("player:" + connection.getUniqueId().toString(), playerData);
if (fireEvent) {
pipeline.publish("redisbungee-data", gson.toJson(new DataManager.DataManagerMessage<>(
connection.getUniqueId(), RedisBungeeAPI.getRedisBungeeApi().getServerId(), DataManager.DataManagerMessage.Action.JOIN,
new DataManager.LoginPayload(connection.getAddress().getAddress()))));
}
}
}

View File

@@ -2,31 +2,40 @@ package com.imaginarycode.minecraft.redisbungee;
import com.google.common.cache.Cache; import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import com.google.common.collect.*; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.imaginarycode.minecraft.redisbungee.commands.RedisBungeeCommands;
import com.imaginarycode.minecraft.redisbungee.events.PlayerChangedServerNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.events.PlayerJoinedNetworkEvent;
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent; import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
import com.imaginarycode.minecraft.redisbungee.util.*; import com.imaginarycode.minecraft.redisbungee.internal.*;
import com.imaginarycode.minecraft.redisbungee.util.uuid.NameFetcher; import com.imaginarycode.minecraft.redisbungee.internal.util.IOUtil;
import com.imaginarycode.minecraft.redisbungee.util.uuid.UUIDFetcher; import com.imaginarycode.minecraft.redisbungee.internal.util.LuaManager;
import com.imaginarycode.minecraft.redisbungee.util.uuid.UUIDTranslator; import com.imaginarycode.minecraft.redisbungee.internal.util.uuid.NameFetcher;
import com.imaginarycode.minecraft.redisbungee.internal.util.uuid.UUIDFetcher;
import com.imaginarycode.minecraft.redisbungee.internal.util.uuid.UUIDTranslator;
import com.squareup.okhttp.Dispatcher; import com.squareup.okhttp.Dispatcher;
import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.OkHttpClient;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Event;
import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.config.Configuration; import net.md_5.bungee.config.Configuration;
import net.md_5.bungee.config.ConfigurationProvider; import net.md_5.bungee.config.ConfigurationProvider;
import net.md_5.bungee.config.YamlConfiguration; import net.md_5.bungee.config.YamlConfiguration;
import redis.clients.jedis.*; import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisConnectionException;
import java.io.*; import java.io.*;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.net.InetAddress;
import java.util.*; import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@@ -34,39 +43,21 @@ import java.util.logging.Level;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
/** public class RedisBungeeBungeePlugin extends Plugin implements RedisBungeePlugin<ProxiedPlayer> {
* The RedisBungee plugin.
* <p> private static final Gson gson = new Gson();
* The only function of interest is {@link #getApi()}, which deprecated now, private RedisBungeeAPI api;
* Please check {@link RedisBungeeAPI#getRedisBungeeApi()}, private PubSubListener psl = null;
* private JedisPool jedisPool;
* which exposes some functions in this class.
* but if you want old version support,
* then you can use old method {@link #getApi()}
*
*/
public final class RedisBungee extends Plugin {
@Getter
private static Gson gson = new Gson();
private static RedisBungeeAPI api;
@Getter(AccessLevel.PACKAGE)
private static PubSubListener psl = null;
@Getter
private JedisPool pool;
@Getter
private UUIDTranslator uuidTranslator; private UUIDTranslator uuidTranslator;
@Getter(AccessLevel.PACKAGE) private RedisBungeeConfiguration configuration;
private static RedisBungeeConfiguration configuration; private BungeeDataManager dataManager;
@Getter private OkHttpClient httpClient;
private DataManager dataManager;
@Getter
private static OkHttpClient httpClient;
private volatile List<String> serverIds; private volatile List<String> serverIds;
private final AtomicInteger nagAboutServers = new AtomicInteger(); private final AtomicInteger nagAboutServers = new AtomicInteger();
private final AtomicInteger globalPlayerCount = new AtomicInteger(); private final AtomicInteger globalPlayerCount = new AtomicInteger();
private Future<?> integrityCheck; private Future<?> integrityCheck;
private Future<?> heartbeatTask; private Future<?> heartbeatTask;
private boolean usingLua;
private LuaManager.Script serverToPlayersScript; private LuaManager.Script serverToPlayersScript;
private LuaManager.Script getPlayerCountScript; private LuaManager.Script getPlayerCountScript;
@@ -75,28 +66,145 @@ public final class RedisBungee extends Plugin {
.expireAfterWrite(5, TimeUnit.SECONDS) .expireAfterWrite(5, TimeUnit.SECONDS)
.build(); .build();
/**
* Fetch the {@link RedisBungeeAPI} object created on plugin start. @Override
* public RedisBungeeConfiguration getConfiguration() {
* @deprecated Please use {@link RedisBungeeAPI#getRedisBungeeApi()} return this.configuration;
*
* @return the {@link RedisBungeeAPI} object instance.
*/
@Deprecated
public static RedisBungeeAPI getApi() {
return api;
} }
static PubSubListener getPubSubListener() { @Override
return psl; public int getCount() {
return this.globalPlayerCount.get();
} }
final List<String> getServerIds() { @Override
public int getCurrentCount() {
Long count = (Long) getPlayerCountScript.eval(ImmutableList.<String>of(), ImmutableList.<String>of());
return count.intValue();
}
@Override
public Set<String> getLocalPlayersAsUuidStrings() {
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
for (ProxiedPlayer player : getProxy().getPlayers()) {
builder.add(player.getUniqueId().toString());
}
return builder.build();
}
@Override
public DataManager<ProxiedPlayer, ?, ?, ?> getDataManager() {
return this.dataManager;
}
@Override
public Set<UUID> getPlayers() {
ImmutableSet.Builder<UUID> setBuilder = ImmutableSet.builder();
if (isJedisAvailable()) {
try (Jedis rsc = requestJedis()) {
List<String> keys = new ArrayList<>();
for (String i : getServerIds()) {
keys.add("proxy:" + i + ":usersOnline");
}
if (!keys.isEmpty()) {
Set<String> users = rsc.sunion(keys.toArray(new String[keys.size()]));
if (users != null && !users.isEmpty()) {
for (String user : users) {
try {
setBuilder = setBuilder.add(UUID.fromString(user));
} catch (IllegalArgumentException ignored) {
}
}
}
}
} catch (JedisConnectionException e) {
// Redis server has disappeared!
getLogger().log(Level.SEVERE, "Unable to get connection from pool - did your Redis server go away?", e);
throw new RuntimeException("Unable to get all players online", e);
}
}
return setBuilder.build();
}
@Override
public Jedis requestJedis() {
return this.jedisPool.getResource();
}
@Override
public boolean isJedisAvailable() {
return !jedisPool.isClosed();
}
@Override
public JedisPool getJedisPool() {
return this.jedisPool;
}
@Override
public RedisBungeeAPI getApi() {
return this.api;
}
@Override
public UUIDTranslator getUuidTranslator() {
return this.uuidTranslator;
}
@Override
public Multimap<String, UUID> serversToPlayers() {
try {
return serverToPlayersCache.get(SERVER_TO_PLAYERS_KEY, new Callable<Multimap<String, UUID>>() {
@Override
public Multimap<String, UUID> call() throws Exception {
Collection<String> data = (Collection<String>) serverToPlayersScript.eval(ImmutableList.<String>of(), getServerIds());
ImmutableMultimap.Builder<String, UUID> builder = ImmutableMultimap.builder();
String key = null;
for (String s : data) {
if (key == null) {
key = s;
continue;
}
builder.put(key, UUID.fromString(s));
key = null;
}
return builder.build();
}
});
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
@Override
public Set<UUID> getPlayersOnProxy(String proxyId) {
checkArgument(getServerIds().contains(proxyId), proxyId + " is not a valid proxy ID");
try (Jedis jedis = requestJedis()) {
Set<String> users = jedis.smembers("proxy:" + proxyId + ":usersOnline");
ImmutableSet.Builder<UUID> builder = ImmutableSet.builder();
for (String user : users) {
builder.add(UUID.fromString(user));
}
return builder.build();
}
}
@Override
public void sendProxyCommand(String serverId, String command) {
checkArgument(getServerIds().contains(serverId) || serverId.equals("allservers"), "proxyId is invalid");
sendChannelMessage("redisbungee-" + serverId, command);
}
@Override
public List<String> getServerIds() {
return serverIds; return serverIds;
} }
private List<String> getCurrentServerIds(boolean nag, boolean lagged) { @Override
try (Jedis jedis = pool.getResource()) { public List<String> getCurrentServerIds(boolean nag, boolean lagged) {
try (Jedis jedis = requestJedis()) {
long time = getRedisTime(jedis.time()); long time = getRedisTime(jedis.time());
int nagTime = 0; int nagTime = 0;
if (nag) { if (nag) {
@@ -126,97 +234,14 @@ public final class RedisBungee extends Plugin {
} }
} }
public Set<UUID> getPlayersOnProxy(String server) { @Override
checkArgument(getServerIds().contains(server), server + " is not a valid proxy ID"); public PubSubListener getPubSubListener() {
try (Jedis jedis = pool.getResource()) { return this.psl;
Set<String> users = jedis.smembers("proxy:" + server + ":usersOnline");
ImmutableSet.Builder<UUID> builder = ImmutableSet.builder();
for (String user : users) {
builder.add(UUID.fromString(user));
}
return builder.build();
}
} }
final Multimap<String, UUID> serversToPlayers() { @Override
try { public void sendChannelMessage(String channel, String message) {
return serverToPlayersCache.get(SERVER_TO_PLAYERS_KEY, new Callable<Multimap<String, UUID>>() { try (Jedis jedis = requestJedis()) {
@Override
public Multimap<String, UUID> call() throws Exception {
Collection<String> data = (Collection<String>) serverToPlayersScript.eval(ImmutableList.<String>of(), getServerIds());
ImmutableMultimap.Builder<String, UUID> builder = ImmutableMultimap.builder();
String key = null;
for (String s : data) {
if (key == null) {
key = s;
continue;
}
builder.put(key, UUID.fromString(s));
key = null;
}
return builder.build();
}
});
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
final int getCount() {
return globalPlayerCount.get();
}
final int getCurrentCount() {
Long count = (Long) getPlayerCountScript.eval(ImmutableList.<String>of(), ImmutableList.<String>of());
return count.intValue();
}
private Set<String> getLocalPlayersAsUuidStrings() {
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
for (ProxiedPlayer player : getProxy().getPlayers()) {
builder.add(player.getUniqueId().toString());
}
return builder.build();
}
final Set<UUID> getPlayers() {
ImmutableSet.Builder<UUID> setBuilder = ImmutableSet.builder();
if (pool != null) {
try (Jedis rsc = pool.getResource()) {
List<String> keys = new ArrayList<>();
for (String i : getServerIds()) {
keys.add("proxy:" + i + ":usersOnline");
}
if (!keys.isEmpty()) {
Set<String> users = rsc.sunion(keys.toArray(new String[keys.size()]));
if (users != null && !users.isEmpty()) {
for (String user : users) {
try {
setBuilder = setBuilder.add(UUID.fromString(user));
} catch (IllegalArgumentException ignored) {
}
}
}
}
} catch (JedisConnectionException e) {
// Redis server has disappeared!
getLogger().log(Level.SEVERE, "Unable to get connection from pool - did your Redis server go away?", e);
throw new RuntimeException("Unable to get all players online", e);
}
}
return setBuilder.build();
}
final void sendProxyCommand(@NonNull String proxyId, @NonNull String command) {
checkArgument(getServerIds().contains(proxyId) || proxyId.equals("allservers"), "proxyId is invalid");
sendChannelMessage("redisbungee-" + proxyId, command);
}
final void sendChannelMessage(String channel, String message) {
try (Jedis jedis = pool.getResource()) {
jedis.publish(channel, message); jedis.publish(channel, message);
} catch (JedisConnectionException e) { } catch (JedisConnectionException e) {
// Redis server has disappeared! // Redis server has disappeared!
@@ -225,12 +250,90 @@ public final class RedisBungee extends Plugin {
} }
} }
private long getRedisTime(List<String> timeRes) { @Override
return Long.parseLong(timeRes.get(0)); public void executeAsync(Runnable runnable) {
this.getProxy().getScheduler().runAsync(this, runnable);
} }
@Override @Override
public void onEnable() { public void executeAsyncAfter(Runnable runnable, TimeUnit timeUnit, int time) {
this.getProxy().getScheduler().schedule(this, runnable, time, timeUnit);
}
@Override
public void callEvent(Object event) {
this.getProxy().getPluginManager().callEvent((Event) event);
}
@Override
public boolean isOnlineMode() {
return this.getProxy().getConfig().isOnlineMode();
}
@Override
public void logInfo(String msg) {
this.getLogger().info(msg);
}
@Override
public void logWarn(String msg) {
this.getLogger().warning(msg);
}
@Override
public void logFatal(String msg) {
this.getLogger().severe(msg);
}
@Override
public ProxiedPlayer getPlayer(UUID uuid) {
return this.getProxy().getPlayer(uuid);
}
@Override
public ProxiedPlayer getPlayer(String name) {
return this.getProxy().getPlayer(name);
}
@Override
public UUID getPlayerUUID(String player) {
return this.getProxy().getPlayer(player).getUniqueId();
}
@Override
public String getPlayerName(UUID player) {
return this.getProxy().getPlayer(player).getName();
}
@Override
public String getPlayerServerName(ProxiedPlayer player) {
return player.getServer().getInfo().getName();
}
@Override
public boolean isPlayerOnAServer(ProxiedPlayer player) {
return player.getServer() != null;
}
@Override
public InetAddress getPlayerIp(ProxiedPlayer player) {
return player.getAddress().getAddress();
}
@Override
public void sendProxyCommand(String cmd) {
checkArgument(getServerIds().contains(this.configuration.getServerId()) || this.configuration.getServerId().equals("allservers"), "proxyId is invalid");
sendChannelMessage("redisbungee-" + this.configuration.getServerId(), cmd);
}
@Override
public long getRedisTime(List<String> timeRes) {
return Long.parseLong(timeRes.get(0));
}
@Override
public void enable() {
ThreadFactory factory = ((ThreadPoolExecutor) getExecutorService()).getThreadFactory(); ThreadFactory factory = ((ThreadPoolExecutor) getExecutorService()).getThreadFactory();
ScheduledExecutorService service = Executors.newScheduledThreadPool(24, factory); ScheduledExecutorService service = Executors.newScheduledThreadPool(24, factory);
try { try {
@@ -250,15 +353,18 @@ public final class RedisBungee extends Plugin {
} catch (JedisConnectionException e) { } catch (JedisConnectionException e) {
throw new RuntimeException("Unable to connect to your Redis server!", e); throw new RuntimeException("Unable to connect to your Redis server!", e);
} }
if (pool != null) { this.api = new RedisBungeeAPI(this);
try (Jedis tmpRsc = pool.getResource()) { // call old plugin class to support old plugins
new RedisBungee(api);
if (isJedisAvailable()) {
try (Jedis tmpRsc = requestJedis()) {
// This is more portable than INFO <section> // This is more portable than INFO <section>
String info = tmpRsc.info(); String info = tmpRsc.info();
for (String s : info.split("\r\n")) { for (String s : info.split("\r\n")) {
if (s.startsWith("redis_version:")) { if (s.startsWith("redis_version:")) {
String version = s.split(":")[1]; String version = s.split(":")[1];
getLogger().info(version + " <- redis version"); getLogger().info(version + " <- redis version");
if (!(usingLua = RedisUtil.canUseLua(version))) { if (!RedisUtil.isRedisVersionRight(version)) {
getLogger().warning("Your version of Redis (" + version + ") is not at least version 6.0 RedisBungee requires a newer version of Redis."); getLogger().warning("Your version of Redis (" + version + ") is not at least version 6.0 RedisBungee requires a newer version of Redis.");
throw new RuntimeException("Unsupported Redis version detected"); throw new RuntimeException("Unsupported Redis version detected");
} else { } else {
@@ -282,7 +388,7 @@ public final class RedisBungee extends Plugin {
heartbeatTask = service.scheduleAtFixedRate(new Runnable() { heartbeatTask = service.scheduleAtFixedRate(new Runnable() {
@Override @Override
public void run() { public void run() {
try (Jedis rsc = pool.getResource()) { try (Jedis rsc = requestJedis()) {
long redisTime = getRedisTime(rsc.time()); long redisTime = getRedisTime(rsc.time());
rsc.hset("heartbeats", configuration.getServerId(), String.valueOf(redisTime)); rsc.hset("heartbeats", configuration.getServerId(), String.valueOf(redisTime));
} catch (JedisConnectionException e) { } catch (JedisConnectionException e) {
@@ -298,28 +404,19 @@ public final class RedisBungee extends Plugin {
} }
} }
}, 0, 3, TimeUnit.SECONDS); }, 0, 3, TimeUnit.SECONDS);
dataManager = new DataManager(this); dataManager = new BungeeDataManager(this);
if (configuration.isRegisterBungeeCommands()) { // glist command
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.GlistCommand(this)); getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.GlistCommand(this));
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.FindCommand(this));
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.LastSeenCommand(this));
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.IpCommand(this));
}
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.SendToAll(this));
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.ServerId(this));
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.ServerIds());
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.PlayerProxyCommand(this));
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.PlistCommand(this));
getProxy().getPluginManager().registerCommand(this, new RedisBungeeCommands.DebugCommand(this));
api = new RedisBungeeAPI(this);
getProxy().getPluginManager().registerListener(this, new RedisBungeeListener(this, configuration.getExemptAddresses())); getProxy().getPluginManager().registerListener(this, new RedisBungeeListener(this, configuration.getExemptAddresses()));
getProxy().getPluginManager().registerListener(this, dataManager); getProxy().getPluginManager().registerListener(this, dataManager);
psl = new PubSubListener(); psl = new PubSubListener(this);
getProxy().getScheduler().runAsync(this, psl); getProxy().getScheduler().runAsync(this, psl);
integrityCheck = service.scheduleAtFixedRate(new Runnable() { integrityCheck = service.scheduleAtFixedRate(new Runnable() {
@Override @Override
public void run() { public void run() {
try (Jedis tmpRsc = pool.getResource()) { try (Jedis tmpRsc = requestJedis()) {
Set<String> players = getLocalPlayersAsUuidStrings(); Set<String> players = getLocalPlayersAsUuidStrings();
Set<String> playersInRedis = tmpRsc.smembers("proxy:" + configuration.getServerId() + ":usersOnline"); Set<String> playersInRedis = tmpRsc.smembers("proxy:" + configuration.getServerId() + ":usersOnline");
List<String> lagged = getCurrentServerIds(false, true); List<String> lagged = getCurrentServerIds(false, true);
@@ -370,7 +467,7 @@ public final class RedisBungee extends Plugin {
if (proxiedPlayer == null) if (proxiedPlayer == null)
continue; // We'll deal with it later. continue; // We'll deal with it later.
RedisUtil.createPlayer(proxiedPlayer, pipeline, true); RBUtils.createPlayer(proxiedPlayer, pipeline, true);
} }
pipeline.sync(); pipeline.sync();
@@ -385,15 +482,15 @@ public final class RedisBungee extends Plugin {
} }
@Override @Override
public void onDisable() { public void disable() {
if (pool != null) { if (isJedisAvailable()) {
// Poison the PubSub listener // Poison the PubSub listener
psl.poison(); psl.poison();
integrityCheck.cancel(true); integrityCheck.cancel(true);
heartbeatTask.cancel(true); heartbeatTask.cancel(true);
getProxy().getPluginManager().unregisterListeners(this); getProxy().getPluginManager().unregisterListeners(this);
try (Jedis tmpRsc = pool.getResource()) { try (Jedis tmpRsc = requestJedis()) {
tmpRsc.hdel("heartbeats", configuration.getServerId()); tmpRsc.hdel("heartbeats", configuration.getServerId());
if (tmpRsc.scard("proxy:" + configuration.getServerId() + ":usersOnline") > 0) { if (tmpRsc.scard("proxy:" + configuration.getServerId() + ":usersOnline") > 0) {
Set<String> players = tmpRsc.smembers("proxy:" + configuration.getServerId() + ":usersOnline"); Set<String> players = tmpRsc.smembers("proxy:" + configuration.getServerId() + ":usersOnline");
@@ -402,11 +499,12 @@ public final class RedisBungee extends Plugin {
} }
} }
pool.destroy(); this.jedisPool.destroy();
} }
} }
private void loadConfig() throws IOException, JedisConnectionException { @Override
public void loadConfig() throws IOException {
if (!getDataFolder().exists()) { if (!getDataFolder().exists()) {
getDataFolder().mkdir(); getDataFolder().mkdir();
} }
@@ -421,59 +519,51 @@ public final class RedisBungee extends Plugin {
} }
} }
final Configuration configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(file); final Configuration yamlConfiguration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(file);
final String redisServer = configuration.getString("redis-server", "localhost"); final String redisServer = yamlConfiguration.getString("redis-server", "localhost");
final int redisPort = configuration.getInt("redis-port", 6379); final int redisPort = yamlConfiguration.getInt("redis-port", 6379);
final boolean useSSL = configuration.getBoolean("useSSL"); final boolean useSSL = yamlConfiguration.getBoolean("useSSL", false);
String redisPassword = configuration.getString("redis-password"); String redisPassword = yamlConfiguration.getString("redis-password", "");
String serverId = configuration.getString("server-id"); String serverId = yamlConfiguration.getString("server-id");
final String randomUUID = UUID.randomUUID().toString(); final String randomUUID = UUID.randomUUID().toString();
// check redis password
if (redisPassword != null && (redisPassword.isEmpty() || redisPassword.equals("none"))) { if (redisPassword != null && (redisPassword.isEmpty() || redisPassword.equals("none"))) {
redisPassword = null; redisPassword = null;
getLogger().warning("INSECURE setup was detected Please set password for your redis instance.");
}
if (!useSSL) {
getLogger().warning("INSECURE setup was detected Please setup ssl for your redis instance.");
} }
// Configuration sanity checks. // Configuration sanity checks.
if (serverId == null || serverId.isEmpty()) { if (serverId == null || serverId.isEmpty()) {
/* /*
* this check causes the config comments to disappear somehow * this check causes the config comments to disappear somehow
* I think due snake yaml limitations so as todo: write our own yaml parser? * I think due snake yaml limitations so as todo: write our own yaml parser?
*/ */
String genId = UUID.randomUUID().toString(); String genId = UUID.randomUUID().toString();
getLogger().info("Generated server id " + genId + " and saving it to config."); getLogger().info("Generated server id " + genId + " and saving it to config.");
configuration.set("server-id", genId); yamlConfiguration.set("server-id", genId);
ConfigurationProvider.getProvider(YamlConfiguration.class).save(configuration, new File(getDataFolder(), "config.yml")); ConfigurationProvider.getProvider(YamlConfiguration.class).save(yamlConfiguration, new File(getDataFolder(), "config.yml"));
getLogger().info("Server id was generated: " + serverId);
} else { } else {
getLogger().info("Loaded server id " + serverId + '.'); getLogger().info("Loaded server id " + serverId + '.');
} }
this.configuration = new RedisBungeeConfiguration(serverId, yamlConfiguration.getStringList("exempt-ip-addresses"));
if (configuration.getBoolean("use-random-id-string", false)) {
serverId = configuration.getString("server-id") + "-" + randomUUID;
}
if (redisServer != null && !redisServer.isEmpty()) { if (redisServer != null && !redisServer.isEmpty()) {
final String finalRedisPassword = redisPassword;
FutureTask<JedisPool> task = new FutureTask<>(new Callable<JedisPool>() {
@Override
public JedisPool call() throws Exception {
// Create the pool...
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(configuration.getInt("max-redis-connections", 8));
return new JedisPool(config, redisServer, redisPort, 0, finalRedisPassword, useSSL);
}
});
getProxy().getScheduler().runAsync(this, task);
try { try {
pool = task.get(); JedisPoolConfig config = new JedisPoolConfig();
} catch (InterruptedException | ExecutionException e) { config.setMaxTotal(yamlConfiguration.getInt("max-redis-connections", 8));
this.jedisPool = new JedisPool(config, redisServer, redisPort, 0, redisPassword, useSSL);
} catch (JedisConnectionException e) {
throw new RuntimeException("Unable to create Redis pool", e); throw new RuntimeException("Unable to create Redis pool", e);
} }
// Test the connection // Test the connection
try (Jedis rsc = pool.getResource()) { try (Jedis rsc = requestJedis()) {
rsc.ping(); rsc.ping();
// If that worked, now we can check for an existing, alive Bungee: // If that worked, now we can check for an existing, alive Bungee:
File crashFile = new File(getDataFolder(), "restarted_from_crash.txt"); File crashFile = new File(getDataFolder(), "restarted_from_crash.txt");
@@ -493,31 +583,17 @@ public final class RedisBungee extends Plugin {
} }
} }
FutureTask<Void> task2 = new FutureTask<>(new Callable<Void>() {
@Override
public Void call() throws Exception {
httpClient = new OkHttpClient();
Dispatcher dispatcher = new Dispatcher(getExecutorService());
httpClient.setDispatcher(dispatcher);
NameFetcher.setHttpClient(httpClient);
UUIDFetcher.setHttpClient(httpClient);
RedisBungee.configuration = new RedisBungeeConfiguration(RedisBungee.this.getPool(), configuration, randomUUID);
return null;
}
});
getProxy().getScheduler().runAsync(this, task2); httpClient = new OkHttpClient();
Dispatcher dispatcher = new Dispatcher(getExecutorService());
try { httpClient.setDispatcher(dispatcher);
task2.get(); NameFetcher.setHttpClient(httpClient);
} catch (InterruptedException | ExecutionException e) { UUIDFetcher.setHttpClient(httpClient);
throw new RuntimeException("Unable to create HTTP client", e);
}
getLogger().log(Level.INFO, "Successfully connected to Redis."); getLogger().log(Level.INFO, "Successfully connected to Redis.");
} catch (JedisConnectionException e) { } catch (JedisConnectionException e) {
pool.destroy(); this.jedisPool.destroy();
pool = null; this.jedisPool = null;
throw e; throw e;
} }
} else { } else {
@@ -525,72 +601,33 @@ public final class RedisBungee extends Plugin {
} }
} }
@NoArgsConstructor(access = AccessLevel.PRIVATE) @Override
class PubSubListener implements Runnable { public void onEnable() {
private JedisPubSubHandler jpsh; enable();
private Set<String> addedChannels = new HashSet<String>();
@Override
public void run() {
boolean broken = false;
try (Jedis rsc = pool.getResource()) {
try {
jpsh = new JedisPubSubHandler();
addedChannels.add("redisbungee-" + configuration.getServerId());
addedChannels.add("redisbungee-allservers");
addedChannels.add("redisbungee-data");
rsc.subscribe(jpsh, addedChannels.toArray(new String[0]));
} catch (Exception e) {
// FIXME: Extremely ugly hack
// Attempt to unsubscribe this instance and try again.
getLogger().log(Level.INFO, "PubSub error, attempting to recover.", e);
try {
jpsh.unsubscribe();
} catch (Exception e1) {
/* This may fail with
- java.net.SocketException: Broken pipe
- redis.clients.jedis.exceptions.JedisConnectionException: JedisPubSub was not subscribed to a Jedis instance
*/
}
broken = true;
}
} catch (JedisConnectionException e) {
getLogger().log(Level.INFO, "PubSub error, attempting to recover in 5 secs.");
getProxy().getScheduler().schedule(RedisBungee.this, PubSubListener.this, 5, TimeUnit.SECONDS);
}
if (broken) {
run();
}
}
public void addChannel(String... channel) {
addedChannels.addAll(Arrays.asList(channel));
jpsh.subscribe(channel);
}
public void removeChannel(String... channel) {
addedChannels.removeAll(Arrays.asList(channel));
jpsh.unsubscribe(channel);
}
public void poison() {
addedChannels.clear();
jpsh.unsubscribe();
}
} }
private class JedisPubSubHandler extends JedisPubSub { @Override
@Override public void onDisable() {
public void onMessage(final String s, final String s2) { disable();
if (s2.trim().length() == 0) return; }
getProxy().getScheduler().runAsync(RedisBungee.this, new Runnable() {
@Override @Override
public void run() { public Class<?> getPubSubEventClass() {
getProxy().getPluginManager().callEvent(new PubSubMessageEvent(s, s2)); return PubSubMessageEvent.class;
} }
});
} @Override
public Class<?> getNetworkJoinEventClass() {
return PlayerJoinedNetworkEvent.class;
}
@Override
public Class<?> getServerChangeEventClass() {
return PlayerChangedServerNetworkEvent.class;
}
@Override
public Class<?> getNetworkQuitEventClass() {
return PlayerJoinedNetworkEvent.class;
} }
} }

View File

@@ -3,49 +3,41 @@ package com.imaginarycode.minecraft.redisbungee;
import com.google.common.base.Joiner; import com.google.common.base.Joiner;
import com.google.common.collect.HashMultimap; import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.google.common.collect.Multiset;
import com.google.common.io.ByteArrayDataInput; import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import com.imaginarycode.minecraft.redisbungee.internal.AbstractRedisBungeeListener;
import com.imaginarycode.minecraft.redisbungee.internal.DataManager;
import com.imaginarycode.minecraft.redisbungee.internal.RedisBungeePlugin;
import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent; import com.imaginarycode.minecraft.redisbungee.events.PubSubMessageEvent;
import com.imaginarycode.minecraft.redisbungee.util.RedisCallable; import com.imaginarycode.minecraft.redisbungee.internal.RedisUtil;
import lombok.AllArgsConstructor; import com.imaginarycode.minecraft.redisbungee.internal.util.RedisCallable;
import net.md_5.bungee.api.AbstractReconnectHandler; import net.md_5.bungee.api.AbstractReconnectHandler;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server; import net.md_5.bungee.api.connection.Server;
import net.md_5.bungee.api.event.*; import net.md_5.bungee.api.event.*;
import net.md_5.bungee.api.plugin.Listener; import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.event.EventHandler; import net.md_5.bungee.event.EventHandler;
import net.md_5.bungee.event.EventPriority;
import redis.clients.jedis.Jedis; import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline; import redis.clients.jedis.Pipeline;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.*; import java.util.*;
@AllArgsConstructor public class RedisBungeeListener extends AbstractRedisBungeeListener<LoginEvent, PostLoginEvent, PlayerDisconnectEvent, ServerConnectedEvent, ProxyPingEvent, PluginMessageEvent, PubSubMessageEvent> implements Listener {
public class RedisBungeeListener implements Listener {
private static final BaseComponent[] ALREADY_LOGGED_IN =
new ComponentBuilder("You are already logged on to this server.").color(ChatColor.RED)
.append("\n\nIt may help to try logging in again in a few minutes.\nIf this does not resolve your issue, please contact staff.")
.color(ChatColor.GRAY)
.create();
private static final BaseComponent[] ONLINE_MODE_RECONNECT =
new ComponentBuilder("Whoops! You need to reconnect.").color(ChatColor.RED)
.append("\n\nWe found someone online using your username. They were kicked and you may reconnect.\nIf this does not work, please contact staff.")
.color(ChatColor.GRAY)
.create();
private final RedisBungee plugin;
private final List<InetAddress> exemptAddresses;
@EventHandler(priority = EventPriority.LOWEST)
public void onLogin(final LoginEvent event) { public RedisBungeeListener(RedisBungeePlugin<?> plugin, List<InetAddress> exemptAddresses) {
event.registerIntent(plugin); super(plugin, exemptAddresses);
plugin.getProxy().getScheduler().runAsync(plugin, new RedisCallable<Void>(plugin) { }
@Override
@EventHandler
public void onLogin(LoginEvent event) {
event.registerIntent((Plugin) plugin);
plugin.executeAsync(new RedisCallable<Void>(plugin) {
@Override @Override
protected Void call(Jedis jedis) { protected Void call(Jedis jedis) {
try { try {
@@ -55,8 +47,8 @@ public class RedisBungeeListener implements Listener {
// We make sure they aren't trying to use an existing player's name. // We make sure they aren't trying to use an existing player's name.
// This is problematic for online-mode servers as they always disconnect old clients. // This is problematic for online-mode servers as they always disconnect old clients.
if (plugin.getProxy().getConfig().isOnlineMode()) { if (plugin.isOnlineMode()) {
ProxiedPlayer player = plugin.getProxy().getPlayer(event.getConnection().getName()); ProxiedPlayer player = (ProxiedPlayer) plugin.getPlayer(event.getConnection().getName());
if (player != null) { if (player != null) {
event.setCancelled(true); event.setCancelled(true);
@@ -76,15 +68,16 @@ public class RedisBungeeListener implements Listener {
} }
return null; return null;
} finally { } finally {
event.completeIntent(plugin); event.completeIntent((Plugin) plugin);
} }
} }
}); });
} }
@Override
@EventHandler @EventHandler
public void onPostLogin(final PostLoginEvent event) { public void onPostLogin(PostLoginEvent event) {
plugin.getProxy().getScheduler().runAsync(plugin, new RedisCallable<Void>(plugin) { plugin.executeAsync(new RedisCallable<Void>(plugin) {
@Override @Override
protected Void call(Jedis jedis) { protected Void call(Jedis jedis) {
// this code was moved out from login event due being async.. // this code was moved out from login event due being async..
@@ -92,21 +85,22 @@ public class RedisBungeeListener implements Listener {
// which will register the player into the redis database. // which will register the player into the redis database.
Pipeline pipeline = jedis.pipelined(); Pipeline pipeline = jedis.pipelined();
plugin.getUuidTranslator().persistInfo(event.getPlayer().getName(), event.getPlayer().getUniqueId(), pipeline); plugin.getUuidTranslator().persistInfo(event.getPlayer().getName(), event.getPlayer().getUniqueId(), pipeline);
RedisUtil.createPlayer(event.getPlayer(), pipeline, false); RBUtils.createPlayer(event.getPlayer(), pipeline, false);
pipeline.sync(); pipeline.sync();
// the end of moved code. // the end of moved code.
jedis.publish("redisbungee-data", RedisBungee.getGson().toJson(new DataManager.DataManagerMessage<>( jedis.publish("redisbungee-data", gson.toJson(new DataManager.DataManagerMessage(
event.getPlayer().getUniqueId(), DataManager.DataManagerMessage.Action.JOIN, event.getPlayer().getUniqueId(), plugin.getApi().getServerId(), DataManager.DataManagerMessage.Action.JOIN,
new DataManager.LoginPayload(event.getPlayer().getAddress().getAddress())))); new DataManager.LoginPayload(event.getPlayer().getAddress().getAddress()))));
return null; return null;
} }
}); });
} }
@Override
@EventHandler @EventHandler
public void onPlayerDisconnect(final PlayerDisconnectEvent event) { public void onPlayerDisconnect(PlayerDisconnectEvent event) {
plugin.getProxy().getScheduler().runAsync(plugin, new RedisCallable<Void>(plugin) { plugin.executeAsync(new RedisCallable<Void>(plugin) {
@Override @Override
protected Void call(Jedis jedis) { protected Void call(Jedis jedis) {
Pipeline pipeline = jedis.pipelined(); Pipeline pipeline = jedis.pipelined();
@@ -115,45 +109,47 @@ public class RedisBungeeListener implements Listener {
return null; return null;
} }
}); });
} }
@Override
@EventHandler @EventHandler
public void onServerChange(final ServerConnectedEvent event) { public void onServerChange(ServerConnectedEvent event) {
final String currentServer = event.getPlayer().getServer() == null ? null : event.getPlayer().getServer().getInfo().getName(); final String currentServer = event.getPlayer().getServer() == null ? null : event.getPlayer().getServer().getInfo().getName();
plugin.getProxy().getScheduler().runAsync(plugin, new RedisCallable<Void>(plugin) { plugin.executeAsync(new RedisCallable<Void>(plugin) {
@Override @Override
protected Void call(Jedis jedis) { protected Void call(Jedis jedis) {
jedis.hset("player:" + event.getPlayer().getUniqueId().toString(), "server", event.getServer().getInfo().getName()); jedis.hset("player:" + event.getPlayer().getUniqueId().toString(), "server", event.getServer().getInfo().getName());
jedis.publish("redisbungee-data", RedisBungee.getGson().toJson(new DataManager.DataManagerMessage<>( jedis.publish("redisbungee-data", gson.toJson(new DataManager.DataManagerMessage(
event.getPlayer().getUniqueId(), DataManager.DataManagerMessage.Action.SERVER_CHANGE, event.getPlayer().getUniqueId(), plugin.getApi().getServerId(), DataManager.DataManagerMessage.Action.SERVER_CHANGE,
new DataManager.ServerChangePayload(event.getServer().getInfo().getName(), currentServer)))); new DataManager.ServerChangePayload(event.getServer().getInfo().getName(), currentServer))));
return null; return null;
} }
}); });
} }
@EventHandler(priority = EventPriority.LOWEST) @Override
public void onPing(final ProxyPingEvent event) { @EventHandler
public void onPing(ProxyPingEvent event) {
if (exemptAddresses.contains(event.getConnection().getAddress().getAddress())) { if (exemptAddresses.contains(event.getConnection().getAddress().getAddress())) {
return; return;
} }
ServerInfo forced = AbstractReconnectHandler.getForcedHost(event.getConnection()); ServerInfo forced = AbstractReconnectHandler.getForcedHost(event.getConnection());
if (forced != null && event.getConnection().getListener().isPingPassthrough()) { if (forced != null && event.getConnection().getListener().isPingPassthrough()) {
return; return;
} }
event.getResponse().getPlayers().setOnline(plugin.getCount()); event.getResponse().getPlayers().setOnline(plugin.getCount());
} }
@Override
@SuppressWarnings("UnstableApiUsage") @SuppressWarnings("UnstableApiUsage")
@EventHandler @EventHandler
public void onPluginMessage(final PluginMessageEvent event) { public void onPluginMessage(PluginMessageEvent event) {
if ((event.getTag().equals("legacy:redisbungee") || event.getTag().equals("RedisBungee")) && event.getSender() instanceof Server) { if ((event.getTag().equals("legacy:redisbungee") || event.getTag().equals("RedisBungee")) && event.getSender() instanceof Server) {
final String currentChannel = event.getTag(); final String currentChannel = event.getTag();
final byte[] data = Arrays.copyOf(event.getData(), event.getData().length); final byte[] data = Arrays.copyOf(event.getData(), event.getData().length);
plugin.getProxy().getScheduler().runAsync(plugin, () -> { plugin.executeAsync(() -> {
ByteArrayDataInput in = ByteStreams.newDataInput(data); ByteArrayDataInput in = ByteStreams.newDataInput(data);
String subchannel = in.readUTF(); String subchannel = in.readUTF();
@@ -170,7 +166,7 @@ public class RedisBungeeListener implements Listener {
original = plugin.getPlayers(); original = plugin.getPlayers();
} else { } else {
try { try {
original = RedisBungee.getApi().getPlayersOnServer(type); original = plugin.getApi().getPlayersOnServer(type);
} catch (IllegalArgumentException ignored) { } catch (IllegalArgumentException ignored) {
} }
} }
@@ -188,7 +184,7 @@ public class RedisBungeeListener implements Listener {
} else { } else {
out.writeUTF(type); out.writeUTF(type);
try { try {
out.writeInt(RedisBungee.getApi().getPlayersOnServer(type).size()); out.writeInt(plugin.getApi().getPlayersOnServer(type).size());
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
out.writeInt(0); out.writeInt(0);
} }
@@ -198,12 +194,12 @@ public class RedisBungeeListener implements Listener {
String user = in.readUTF(); String user = in.readUTF();
out.writeUTF("LastOnline"); out.writeUTF("LastOnline");
out.writeUTF(user); out.writeUTF(user);
out.writeLong(RedisBungee.getApi().getLastOnline(plugin.getUuidTranslator().getTranslatedUuid(user, true))); out.writeLong(plugin.getApi().getLastOnline(plugin.getUuidTranslator().getTranslatedUuid(user, true)));
break; break;
case "ServerPlayers": case "ServerPlayers":
String type1 = in.readUTF(); String type1 = in.readUTF();
out.writeUTF("ServerPlayers"); out.writeUTF("ServerPlayers");
Multimap<String, UUID> multimap = RedisBungee.getApi().getServerToPlayers(); Multimap<String, UUID> multimap = plugin.getApi().getServerToPlayers();
boolean includesUsers; boolean includesUsers;
@@ -233,13 +229,13 @@ public class RedisBungeeListener implements Listener {
break; break;
case "Proxy": case "Proxy":
out.writeUTF("Proxy"); out.writeUTF("Proxy");
out.writeUTF(RedisBungee.getConfiguration().getServerId()); out.writeUTF(plugin.getConfiguration().getServerId());
break; break;
case "PlayerProxy": case "PlayerProxy":
String username = in.readUTF(); String username = in.readUTF();
out.writeUTF("PlayerProxy"); out.writeUTF("PlayerProxy");
out.writeUTF(username); out.writeUTF(username);
out.writeUTF(RedisBungee.getApi().getProxy(plugin.getUuidTranslator().getTranslatedUuid(username, true))); out.writeUTF(plugin.getApi().getProxy(plugin.getUuidTranslator().getTranslatedUuid(username, true)));
break; break;
default: default:
return; return;
@@ -250,42 +246,15 @@ public class RedisBungeeListener implements Listener {
} }
} }
private void serializeMultiset(Multiset<String> collection, ByteArrayDataOutput output) { @Override
output.writeInt(collection.elementSet().size());
for (Multiset.Entry<String> entry : collection.entrySet()) {
output.writeUTF(entry.getElement());
output.writeInt(entry.getCount());
}
}
@SuppressWarnings("SameParameterValue")
private 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());
if (includeNames) {
serializeCollection(entry.getValue(), output);
} else {
output.writeInt(entry.getValue().size());
}
}
}
private void serializeCollection(Collection<?> collection, ByteArrayDataOutput output) {
output.writeInt(collection.size());
for (Object o : collection) {
output.writeUTF(o.toString());
}
}
@EventHandler @EventHandler
public void onPubSubMessage(PubSubMessageEvent event) { public void onPubSubMessage(PubSubMessageEvent event) {
if (event.getChannel().equals("redisbungee-allservers") || event.getChannel().equals("redisbungee-" + RedisBungee.getApi().getServerId())) { if (event.getChannel().equals("redisbungee-allservers") || event.getChannel().equals("redisbungee-" + plugin.getApi().getServerId())) {
String message = event.getMessage(); String message = event.getMessage();
if (message.startsWith("/")) if (message.startsWith("/"))
message = message.substring(1); message = message.substring(1);
plugin.getLogger().info("Invoking command via PubSub: /" + message); plugin.logInfo("Invoking command via PubSub: /" + message);
plugin.getProxy().getPluginManager().dispatchCommand(RedisBungeeCommandSender.instance, message); plugin.sendProxyCommand(message);
} }
} }
} }

View File

@@ -0,0 +1,78 @@
package com.imaginarycode.minecraft.redisbungee.commands;
import com.google.common.base.Joiner;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.imaginarycode.minecraft.redisbungee.RedisBungeeAPI;
import com.imaginarycode.minecraft.redisbungee.internal.RedisBungeePlugin;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.plugin.Command;
import java.util.Map;
import java.util.TreeSet;
import java.util.UUID;
/**
* This class contains subclasses that are used for the commands RedisBungee overrides or includes: /glist, /find and /lastseen.
* <p>
* All classes use the {@link RedisBungeeAPI}.
*
* @author tuxed
* @since 0.2.3
*/
public class RedisBungeeCommands {
private static String playerPlural(int num) {
return num == 1 ? num + " player is" : num + " players are";
}
public static class GlistCommand extends Command {
private final RedisBungeePlugin<?> plugin;
public GlistCommand(RedisBungeePlugin<?> plugin) {
super("glist", "bungeecord.command.list", "redisbungee", "rglist");
this.plugin = plugin;
}
@Override
public void execute(final CommandSender sender, final String[] args) {
plugin.executeAsync(new Runnable() {
@Override
public void run() {
int count = plugin.getApi().getPlayerCount();
BaseComponent[] playersOnline = new ComponentBuilder("").color(ChatColor.YELLOW)
.append(playerPlural(count) + " currently online.").create();
if (args.length > 0 && args[0].equals("showall")) {
Multimap<String, UUID> serverToPlayers = plugin.getApi().getServerToPlayers();
Multimap<String, String> human = HashMultimap.create();
for (Map.Entry<String, UUID> entry : serverToPlayers.entries()) {
human.put(entry.getKey(), plugin.getUuidTranslator().getNameFromUuid(entry.getValue(), false));
}
for (String server : new TreeSet<>(serverToPlayers.keySet())) {
TextComponent serverName = new TextComponent();
serverName.setColor(ChatColor.GREEN);
serverName.setText("[" + server + "] ");
TextComponent serverCount = new TextComponent();
serverCount.setColor(ChatColor.YELLOW);
serverCount.setText("(" + serverToPlayers.get(server).size() + "): ");
TextComponent serverPlayers = new TextComponent();
serverPlayers.setColor(ChatColor.WHITE);
serverPlayers.setText(Joiner.on(", ").join(human.get(server)));
sender.sendMessage(serverName, serverCount, serverPlayers);
}
sender.sendMessage(playersOnline);
} else {
sender.sendMessage(playersOnline);
sender.sendMessage(new ComponentBuilder("To see all players online, use /glist showall.").color(ChatColor.YELLOW).create());
}
}
});
}
}
}

View File

@@ -1,5 +1,5 @@
name: ${project.name} name: RedisBungee
main: com.imaginarycode.minecraft.redisbungee.RedisBungee main: com.imaginarycode.minecraft.redisbungee.RedisBungeeBungeePlugin
version: ${project.version} version: ${project.version}
author: Chunkr and Govindas limework author: Chunkr and Govindas limework
authors: authors:

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>RedisBungee</artifactId>
<groupId>com.imaginarycode.minecraft</groupId>
<version>0.7.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>RedisBungee-BungeeEvents</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<repositories>
<repository>
<id>bungeecord-repo</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.3.2</version>
<configuration>
<source>8</source>
<reportOutputDirectory>../javadoc</reportOutputDirectory>
<destDir>${project.name}</destDir>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>1.17-R0.1-SNAPSHOT</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -1,6 +1,5 @@
package com.imaginarycode.minecraft.redisbungee.events; package com.imaginarycode.minecraft.redisbungee.events;
import lombok.ToString;
import net.md_5.bungee.api.plugin.Event; import net.md_5.bungee.api.plugin.Event;
import java.util.UUID; import java.util.UUID;
@@ -14,7 +13,6 @@ import java.util.UUID;
* *
* @since 0.3.4 * @since 0.3.4
*/ */
@ToString
public class PlayerChangedServerNetworkEvent extends Event { public class PlayerChangedServerNetworkEvent extends Event {
private final UUID uuid; private final UUID uuid;
private final String previousServer; private final String previousServer;

View File

@@ -1,6 +1,5 @@
package com.imaginarycode.minecraft.redisbungee.events; package com.imaginarycode.minecraft.redisbungee.events;
import lombok.ToString;
import net.md_5.bungee.api.plugin.Event; import net.md_5.bungee.api.plugin.Event;
import java.util.UUID; import java.util.UUID;
@@ -14,7 +13,6 @@ import java.util.UUID;
* *
* @since 0.3.4 * @since 0.3.4
*/ */
@ToString
public class PlayerJoinedNetworkEvent extends Event { public class PlayerJoinedNetworkEvent extends Event {
private final UUID uuid; private final UUID uuid;

View File

@@ -1,6 +1,5 @@
package com.imaginarycode.minecraft.redisbungee.events; package com.imaginarycode.minecraft.redisbungee.events;
import lombok.ToString;
import net.md_5.bungee.api.plugin.Event; import net.md_5.bungee.api.plugin.Event;
import java.util.UUID; import java.util.UUID;
@@ -14,7 +13,6 @@ import java.util.UUID;
* *
* @since 0.3.4 * @since 0.3.4
*/ */
@ToString
public class PlayerLeftNetworkEvent extends Event { public class PlayerLeftNetworkEvent extends Event {
private final UUID uuid; private final UUID uuid;

View File

@@ -1,7 +1,5 @@
package com.imaginarycode.minecraft.redisbungee.events; package com.imaginarycode.minecraft.redisbungee.events;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import net.md_5.bungee.api.plugin.Event; import net.md_5.bungee.api.plugin.Event;
/** /**
@@ -11,12 +9,16 @@ import net.md_5.bungee.api.plugin.Event;
* *
* @since 0.2.6 * @since 0.2.6
*/ */
@RequiredArgsConstructor
@ToString
public class PubSubMessageEvent extends Event { public class PubSubMessageEvent extends Event {
private final String channel; private final String channel;
private final String message; private final String message;
public PubSubMessageEvent(String channel, String message) {
this.channel = channel;
this.message = message;
}
public String getChannel() { public String getChannel() {
return channel; return channel;
} }

3
makeJavadocs.sh Normal file
View File

@@ -0,0 +1,3 @@
rm -rf javadoc
mkdir javadoc
mvn javadoc:javadoc -pl RedisBungee-API,RedisBungee-BungeeEvents -am

310
mvnw vendored
View File

@@ -1,310 +0,0 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
export JAVA_HOME="`/usr/libexec/java_home`"
else
export JAVA_HOME="/Library/Java/Home"
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
fi
fi
if [ -z "$M2_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
M2_HOME=`dirname "$PRG"`/..
# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --unix "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="`which javac`"
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=`which readlink`
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
if $darwin ; then
javaHome="`dirname \"$javaExecutable\"`"
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
else
javaExecutable="`readlink -f \"$javaExecutable\"`"
fi
javaHome="`dirname \"$javaExecutable\"`"
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="`which java`"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=`cd "$wdir/.."; pwd`
fi
# end of workaround
done
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' < "$1")"
fi
}
BASE_DIR=`find_maven_basedir "$(pwd)"`
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found .mvn/wrapper/maven-wrapper.jar"
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
fi
if [ -n "$MVNW_REPOURL" ]; then
jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
else
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
fi
while IFS="=" read key value; do
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
esac
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
if [ "$MVNW_VERBOSE" = true ]; then
echo "Downloading from: $jarUrl"
fi
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
if $cygwin; then
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
fi
if command -v wget > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found wget ... using wget"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget "$jarUrl" -O "$wrapperJarPath"
else
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
fi
elif command -v curl > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found curl ... using curl"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl -o "$wrapperJarPath" "$jarUrl" -f
else
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Falling back to using Java to download"
fi
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaClass=`cygpath --path --windows "$javaClass"`
fi
if [ -e "$javaClass" ]; then
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Compiling MavenWrapperDownloader.java ..."
fi
# Compiling the Java class
("$JAVA_HOME/bin/javac" "$javaClass")
fi
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
# Running the downloader
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Running MavenWrapperDownloader.java ..."
fi
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
if [ "$MVNW_VERBOSE" = true ]; then
echo $MAVEN_PROJECTBASEDIR
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
fi
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
exec "$JAVACMD" \
$MAVEN_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

182
mvnw.cmd vendored
View File

@@ -1,182 +0,0 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
if "%MVNW_VERBOSE%" == "true" (
echo Found %WRAPPER_JAR%
)
) else (
if not "%MVNW_REPOURL%" == "" (
SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %DOWNLOAD_URL%
)
powershell -Command "&{"^
"$webclient = new-object System.Net.WebClient;"^
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
"}"^
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension
@REM Provide a "standardized" way to retrieve the CLI args that will
@REM work with both Windows and non-Windows executions.
set MAVEN_CMD_LINE_ARGS=%*
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%" == "on" pause
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
exit /B %ERROR_CODE%

103
pom.xml
View File

@@ -6,114 +6,47 @@
<groupId>com.imaginarycode.minecraft</groupId> <groupId>com.imaginarycode.minecraft</groupId>
<artifactId>RedisBungee</artifactId> <artifactId>RedisBungee</artifactId>
<version>0.6.5</version> <packaging>pom</packaging>
<version>0.7.0-SNAPSHOT</version>
<repositories>
<repository>
<id>bungeecord-repo</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>
<inceptionYear>2013</inceptionYear>
<build> <build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<relocations>
<relocation>
<pattern>redis.clients.jedis</pattern>
<shadedPattern>com.imaginarycode.minecraft.redisbungee.internal.jedis
</shadedPattern>
</relocation>
<relocation>
<pattern>redis.clients.util</pattern>
<shadedPattern>com.imaginarycode.minecraft.redisbungee.internal.jedisutil
</shadedPattern>
</relocation>
<relocation>
<pattern>org.apache.commons.pool</pattern>
<shadedPattern>com.imaginarycode.minecraft.redisbungee.internal.commonspool
</shadedPattern>
</relocation>
<relocation>
<pattern>com.squareup.okhttp</pattern>
<shadedPattern>com.imaginarycode.minecraft.redisbungee.internal.okhttp
</shadedPattern>
</relocation>
<relocation>
<pattern>okio</pattern>
<shadedPattern>com.imaginarycode.minecraft.redisbungee.internal.okio
</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.3.0</version>
<configuration> <configuration>
<source>8</source> <source>8</source>
<target>8</target>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
<modules>
<module>RedisBungee-API</module>
<module>RedisBungee-Bungee</module>
<module>RedisBungee-BungeeEvents</module>
</modules>
<dependencies> <dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
<scope>compile</scope>
</dependency>
<dependency> <dependency>
<groupId>redis.clients</groupId> <groupId>redis.clients</groupId>
<artifactId>jedis</artifactId> <artifactId>jedis</artifactId>
<version>3.6.3</version> <version>4.2.1</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId> <artifactId>commons-pool2</artifactId>
<version>2.10.0</version> <version>2.11.1</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>1.17-R0.1-SNAPSHOT</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>com.squareup.okhttp</groupId> <groupId>com.squareup.okhttp</groupId>
<artifactId>okhttp</artifactId> <artifactId>okhttp</artifactId>

View File

@@ -1,77 +0,0 @@
package com.imaginarycode.minecraft.redisbungee;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.BaseComponent;
import java.util.Collection;
import java.util.Collections;
/**
* This class is the CommandSender that RedisBungee uses to dispatch commands to BungeeCord.
* <p>
* It inherits all permissions of the console command sender. Sending messages and modifying permissions are no-ops.
*
* @author tuxed
* @since 0.2.3
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class RedisBungeeCommandSender implements CommandSender {
static final RedisBungeeCommandSender instance = new RedisBungeeCommandSender();
@Override
public String getName() {
return "RedisBungee";
}
@Override
public void sendMessage(String s) {
// no-op
}
@Override
public void sendMessages(String... strings) {
// no-op
}
@Override
public void sendMessage(BaseComponent... baseComponents) {
// no-op
}
@Override
public void sendMessage(BaseComponent baseComponent) {
// no-op
}
@Override
public Collection<String> getGroups() {
return Collections.emptySet();
}
@Override
public void addGroups(String... strings) {
// no-op
}
@Override
public void removeGroups(String... strings) {
// no-op
}
@Override
public boolean hasPermission(String s) {
return true;
}
@Override
public void setPermission(String s, boolean b) {
// no-op
}
@Override
public Collection<String> getPermissions() {
return Collections.emptySet();
}
}

View File

@@ -1,356 +0,0 @@
package com.imaginarycode.minecraft.redisbungee;
import com.google.common.base.Joiner;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.plugin.Command;
import java.net.InetAddress;
import java.text.SimpleDateFormat;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
/**
* This class contains subclasses that are used for the commands RedisBungee overrides or includes: /glist, /find and /lastseen.
* <p>
* All classes use the {@link RedisBungeeAPI}.
*
* @author tuxed
* @since 0.2.3
*/
class RedisBungeeCommands {
private static final BaseComponent[] NO_PLAYER_SPECIFIED =
new ComponentBuilder("You must specify a player name.").color(ChatColor.RED).create();
private static final BaseComponent[] PLAYER_NOT_FOUND =
new ComponentBuilder("No such player found.").color(ChatColor.RED).create();
private static final BaseComponent[] NO_COMMAND_SPECIFIED =
new ComponentBuilder("You must specify a command to be run.").color(ChatColor.RED).create();
private static String playerPlural(int num) {
return num == 1 ? num + " player is" : num + " players are";
}
public static class GlistCommand extends Command {
private final RedisBungee plugin;
GlistCommand(RedisBungee plugin) {
super("glist", "bungeecord.command.list", "redisbungee", "rglist");
this.plugin = plugin;
}
@Override
public void execute(final CommandSender sender, final String[] args) {
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
int count = RedisBungee.getApi().getPlayerCount();
BaseComponent[] playersOnline = new ComponentBuilder("").color(ChatColor.YELLOW)
.append(playerPlural(count) + " currently online.").create();
if (args.length > 0 && args[0].equals("showall")) {
Multimap<String, UUID> serverToPlayers = RedisBungee.getApi().getServerToPlayers();
Multimap<String, String> human = HashMultimap.create();
for (Map.Entry<String, UUID> entry : serverToPlayers.entries()) {
human.put(entry.getKey(), plugin.getUuidTranslator().getNameFromUuid(entry.getValue(), false));
}
for (String server : new TreeSet<>(serverToPlayers.keySet())) {
TextComponent serverName = new TextComponent();
serverName.setColor(ChatColor.GREEN);
serverName.setText("[" + server + "] ");
TextComponent serverCount = new TextComponent();
serverCount.setColor(ChatColor.YELLOW);
serverCount.setText("(" + serverToPlayers.get(server).size() + "): ");
TextComponent serverPlayers = new TextComponent();
serverPlayers.setColor(ChatColor.WHITE);
serverPlayers.setText(Joiner.on(", ").join(human.get(server)));
sender.sendMessage(serverName, serverCount, serverPlayers);
}
sender.sendMessage(playersOnline);
} else {
sender.sendMessage(playersOnline);
sender.sendMessage(new ComponentBuilder("To see all players online, use /glist showall.").color(ChatColor.YELLOW).create());
}
}
});
}
}
public static class FindCommand extends Command {
private final RedisBungee plugin;
FindCommand(RedisBungee plugin) {
super("find", "bungeecord.command.find", "rfind");
this.plugin = plugin;
}
@Override
public void execute(final CommandSender sender, final String[] args) {
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
sender.sendMessage(PLAYER_NOT_FOUND);
return;
}
ServerInfo si = RedisBungee.getApi().getServerFor(uuid);
if (si != null) {
TextComponent message = new TextComponent();
message.setColor(ChatColor.BLUE);
message.setText(args[0] + " is on " + si.getName() + ".");
sender.sendMessage(message);
} else {
sender.sendMessage(PLAYER_NOT_FOUND);
}
} else {
sender.sendMessage(NO_PLAYER_SPECIFIED);
}
}
});
}
}
public static class LastSeenCommand extends Command {
private final RedisBungee plugin;
LastSeenCommand(RedisBungee plugin) {
super("lastseen", "redisbungee.command.lastseen", "rlastseen");
this.plugin = plugin;
}
@Override
public void execute(final CommandSender sender, final String[] args) {
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
sender.sendMessage(PLAYER_NOT_FOUND);
return;
}
long secs = RedisBungee.getApi().getLastOnline(uuid);
TextComponent message = new TextComponent();
if (secs == 0) {
message.setColor(ChatColor.GREEN);
message.setText(args[0] + " is currently online.");
} else if (secs != -1) {
message.setColor(ChatColor.BLUE);
message.setText(args[0] + " was last online on " + new SimpleDateFormat().format(secs) + ".");
} else {
message.setColor(ChatColor.RED);
message.setText(args[0] + " has never been online.");
}
sender.sendMessage(message);
} else {
sender.sendMessage(NO_PLAYER_SPECIFIED);
}
}
});
}
}
public static class IpCommand extends Command {
private final RedisBungee plugin;
IpCommand(RedisBungee plugin) {
super("ip", "redisbungee.command.ip", "playerip", "rip", "rplayerip");
this.plugin = plugin;
}
@Override
public void execute(final CommandSender sender, final String[] args) {
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
sender.sendMessage(PLAYER_NOT_FOUND);
return;
}
InetAddress ia = RedisBungee.getApi().getPlayerIp(uuid);
if (ia != null) {
TextComponent message = new TextComponent();
message.setColor(ChatColor.GREEN);
message.setText(args[0] + " is connected from " + ia.toString() + ".");
sender.sendMessage(message);
} else {
sender.sendMessage(PLAYER_NOT_FOUND);
}
} else {
sender.sendMessage(NO_PLAYER_SPECIFIED);
}
}
});
}
}
public static class PlayerProxyCommand extends Command {
private final RedisBungee plugin;
PlayerProxyCommand(RedisBungee plugin) {
super("pproxy", "redisbungee.command.pproxy");
this.plugin = plugin;
}
@Override
public void execute(final CommandSender sender, final String[] args) {
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
if (args.length > 0) {
UUID uuid = plugin.getUuidTranslator().getTranslatedUuid(args[0], true);
if (uuid == null) {
sender.sendMessage(PLAYER_NOT_FOUND);
return;
}
String proxy = RedisBungee.getApi().getProxy(uuid);
if (proxy != null) {
TextComponent message = new TextComponent();
message.setColor(ChatColor.GREEN);
message.setText(args[0] + " is connected to " + proxy + ".");
sender.sendMessage(message);
} else {
sender.sendMessage(PLAYER_NOT_FOUND);
}
} else {
sender.sendMessage(NO_PLAYER_SPECIFIED);
}
}
});
}
}
public static class SendToAll extends Command {
private final RedisBungee plugin;
SendToAll(RedisBungee plugin) {
super("sendtoall", "redisbungee.command.sendtoall", "rsendtoall");
this.plugin = plugin;
}
@Override
public void execute(CommandSender sender, String[] args) {
if (args.length > 0) {
String command = Joiner.on(" ").skipNulls().join(args);
RedisBungee.getApi().sendProxyCommand(command);
TextComponent message = new TextComponent();
message.setColor(ChatColor.GREEN);
message.setText("Sent the command /" + command + " to all proxies.");
sender.sendMessage(message);
} else {
sender.sendMessage(NO_COMMAND_SPECIFIED);
}
}
}
public static class ServerId extends Command {
private final RedisBungee plugin;
ServerId(RedisBungee plugin) {
super("serverid", "redisbungee.command.serverid", "rserverid");
this.plugin = plugin;
}
@Override
public void execute(CommandSender sender, String[] args) {
TextComponent textComponent = new TextComponent();
textComponent.setText("You are on " + RedisBungee.getApi().getServerId() + ".");
textComponent.setColor(ChatColor.YELLOW);
sender.sendMessage(textComponent);
}
}
public static class ServerIds extends Command {
public ServerIds() {
super("serverids", "redisbungee.command.serverids");
}
@Override
public void execute(CommandSender sender, String[] strings) {
TextComponent textComponent = new TextComponent();
textComponent.setText("All server IDs: " + Joiner.on(", ").join(RedisBungee.getApi().getAllServers()));
textComponent.setColor(ChatColor.YELLOW);
sender.sendMessage(textComponent);
}
}
public static class PlistCommand extends Command {
private final RedisBungee plugin;
PlistCommand(RedisBungee plugin) {
super("plist", "redisbungee.command.plist", "rplist");
this.plugin = plugin;
}
@Override
public void execute(final CommandSender sender, final String[] args) {
plugin.getProxy().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
String proxy = args.length >= 1 ? args[0] : RedisBungee.getConfiguration().getServerId();
if (!plugin.getServerIds().contains(proxy)) {
sender.sendMessage(new ComponentBuilder(proxy + " is not a valid proxy. See /serverids for valid proxies.").color(ChatColor.RED).create());
return;
}
Set<UUID> players = RedisBungee.getApi().getPlayersOnProxy(proxy);
BaseComponent[] playersOnline = new ComponentBuilder("").color(ChatColor.YELLOW)
.append(playerPlural(players.size()) + " currently on proxy " + proxy + ".").create();
if (args.length >= 2 && args[1].equals("showall")) {
Multimap<String, UUID> serverToPlayers = RedisBungee.getApi().getServerToPlayers();
Multimap<String, String> human = HashMultimap.create();
for (Map.Entry<String, UUID> entry : serverToPlayers.entries()) {
if (players.contains(entry.getValue())) {
human.put(entry.getKey(), plugin.getUuidTranslator().getNameFromUuid(entry.getValue(), false));
}
}
for (String server : new TreeSet<>(human.keySet())) {
TextComponent serverName = new TextComponent();
serverName.setColor(ChatColor.RED);
serverName.setText("[" + server + "] ");
TextComponent serverCount = new TextComponent();
serverCount.setColor(ChatColor.YELLOW);
serverCount.setText("(" + human.get(server).size() + "): ");
TextComponent serverPlayers = new TextComponent();
serverPlayers.setColor(ChatColor.WHITE);
serverPlayers.setText(Joiner.on(", ").join(human.get(server)));
sender.sendMessage(serverName, serverCount, serverPlayers);
}
sender.sendMessage(playersOnline);
} else {
sender.sendMessage(playersOnline);
sender.sendMessage(new ComponentBuilder("To see all players online, use /plist " + proxy + " showall.").color(ChatColor.YELLOW).create());
}
}
});
}
}
public static class DebugCommand extends Command {
private final RedisBungee plugin;
DebugCommand(RedisBungee plugin) {
super("rdebug", "redisbungee.command.debug");
this.plugin = plugin;
}
@Override
public void execute(final CommandSender sender, final String[] args) {
TextComponent poolActiveStat = new TextComponent("Currently active pool objects: " + plugin.getPool().getNumActive());
TextComponent poolIdleStat = new TextComponent("Currently idle pool objects: " + plugin.getPool().getNumIdle());
TextComponent poolWaitingStat = new TextComponent("Waiting on free objects: " + plugin.getPool().getNumWaiters());
sender.sendMessage(poolActiveStat);
sender.sendMessage(poolIdleStat);
sender.sendMessage(poolWaitingStat);
}
}
}

View File

@@ -1,44 +0,0 @@
package com.imaginarycode.minecraft.redisbungee;
import com.google.common.collect.ImmutableList;
import com.google.common.net.InetAddresses;
import lombok.Getter;
import net.md_5.bungee.config.Configuration;
import redis.clients.jedis.JedisPool;
import java.net.InetAddress;
import java.util.List;
import java.util.UUID;
public class RedisBungeeConfiguration {
@Getter
private final JedisPool pool;
@Getter
private final String serverId;
@Getter
private final boolean registerBungeeCommands;
@Getter
private final List<InetAddress> exemptAddresses;
public RedisBungeeConfiguration(JedisPool pool, Configuration configuration, String randomUUID) {
this.pool = pool;
if (configuration.getBoolean("use-random-id-string", false)) {
this.serverId = configuration.getString("server-id") + "-" + randomUUID;
} else {
this.serverId = configuration.getString("server-id");
}
this.registerBungeeCommands = configuration.getBoolean("register-bungee-commands", true);
List<String> stringified = configuration.getStringList("exempt-ip-addresses");
ImmutableList.Builder<InetAddress> addressBuilder = ImmutableList.builder();
for (String s : stringified) {
addressBuilder.add(InetAddresses.forString(s));
}
this.exemptAddresses = addressBuilder.build();
}
}

View File

@@ -1,70 +0,0 @@
package com.imaginarycode.minecraft.redisbungee;
import com.google.common.annotations.VisibleForTesting;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@VisibleForTesting
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class RedisUtil {
protected static void createPlayer(ProxiedPlayer player, Pipeline pipeline, boolean fireEvent) {
createPlayer(player.getPendingConnection(), pipeline, fireEvent);
if (player.getServer() != null)
pipeline.hset("player:" + player.getUniqueId().toString(), "server", player.getServer().getInfo().getName());
}
protected static void createPlayer(PendingConnection connection, Pipeline pipeline, boolean fireEvent) {
Map<String, String> playerData = new HashMap<>(4);
playerData.put("online", "0");
playerData.put("ip", connection.getAddress().getAddress().getHostAddress());
playerData.put("proxy", RedisBungee.getConfiguration().getServerId());
pipeline.sadd("proxy:" + RedisBungee.getApi().getServerId() + ":usersOnline", connection.getUniqueId().toString());
pipeline.hmset("player:" + connection.getUniqueId().toString(), playerData);
if (fireEvent) {
pipeline.publish("redisbungee-data", RedisBungee.getGson().toJson(new DataManager.DataManagerMessage<>(
connection.getUniqueId(), DataManager.DataManagerMessage.Action.JOIN,
new DataManager.LoginPayload(connection.getAddress().getAddress()))));
}
}
public static void cleanUpPlayer(String player, Jedis rsc) {
rsc.srem("proxy:" + RedisBungee.getApi().getServerId() + ":usersOnline", player);
rsc.hdel("player:" + player, "server", "ip", "proxy");
long timestamp = System.currentTimeMillis();
rsc.hset("player:" + player, "online", String.valueOf(timestamp));
rsc.publish("redisbungee-data", RedisBungee.getGson().toJson(new DataManager.DataManagerMessage<>(
UUID.fromString(player), DataManager.DataManagerMessage.Action.LEAVE,
new DataManager.LogoutPayload(timestamp))));
}
public static void cleanUpPlayer(String player, Pipeline rsc) {
rsc.srem("proxy:" + RedisBungee.getApi().getServerId() + ":usersOnline", player);
rsc.hdel("player:" + player, "server", "ip", "proxy");
long timestamp = System.currentTimeMillis();
rsc.hset("player:" + player, "online", String.valueOf(timestamp));
rsc.publish("redisbungee-data", RedisBungee.getGson().toJson(new DataManager.DataManagerMessage<>(
UUID.fromString(player), DataManager.DataManagerMessage.Action.LEAVE,
new DataManager.LogoutPayload(timestamp))));
}
public static boolean canUseLua(String redisVersion) {
// Need to use >=6.2 to use Lua optimizations.
String[] args = redisVersion.split("\\.");
if (args.length < 2) {
return false;
}
int major = Integer.parseInt(args[0]);
int minor = Integer.parseInt(args[1]);
return major >= 6 && minor >= 0;
}
}

View File

@@ -1,20 +0,0 @@
package com.imaginarycode.minecraft.redisbungee.test;
import com.imaginarycode.minecraft.redisbungee.RedisUtil;
import org.junit.Assert;
import org.junit.Test;
public class RedisUtilTest {
@Test
public void testRedisLuaCheck() {
Assert.assertTrue(RedisUtil.canUseLua("6.2.0"));
Assert.assertTrue(RedisUtil.canUseLua("6.1.0"));
Assert.assertTrue(RedisUtil.canUseLua("6.0.0"));
Assert.assertFalse(RedisUtil.canUseLua("2.6.0"));
Assert.assertFalse(RedisUtil.canUseLua("2.2.12"));
Assert.assertFalse(RedisUtil.canUseLua("1.2.4"));
Assert.assertFalse(RedisUtil.canUseLua("2.8.4"));
Assert.assertFalse(RedisUtil.canUseLua("3.0.0"));
Assert.assertFalse(RedisUtil.canUseLua("3.2.1"));
}
}

View File

@@ -1,47 +0,0 @@
package com.imaginarycode.minecraft.redisbungee.test;
import com.imaginarycode.minecraft.redisbungee.util.uuid.NameFetcher;
import com.imaginarycode.minecraft.redisbungee.util.uuid.UUIDFetcher;
import com.squareup.okhttp.OkHttpClient;
import org.junit.Test;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class UUIDNameTest {
private String[] uuidsToTest = {"68ec43f7234b41b48764dfb38b9ffe8c", "652a2bc4e8cd405db7b698156ee2dc09"};
private String[] namesToTest = {"vemacs"};
@Test
public void testUuidToName() throws IOException {
OkHttpClient httpClient = new OkHttpClient();
NameFetcher.setHttpClient(httpClient);
for (String uuid : uuidsToTest) {
List<String> names = NameFetcher.nameHistoryFromUuid(UUIDFetcher.getUUID(uuid));
String currentName = names.get(names.size() - 1);
System.out.println("Current name for UUID " + uuid + " is " + currentName);
}
}
@Test
public void testNameToUuid() throws IOException {
OkHttpClient httpClient = new OkHttpClient();
UUIDFetcher.setHttpClient(httpClient);
for (String name : namesToTest) {
Map<String, UUID> uuidMap1;
try {
uuidMap1 = new UUIDFetcher(Collections.singletonList(name)).call();
for (Map.Entry<String, UUID> entry : uuidMap1.entrySet()) {
if (entry.getKey().equalsIgnoreCase(name)) {
System.out.println("Current UUID for name " + name + " is " + entry.getValue());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}