Compare commits

..

No commits in common. "master" and "1.3.4" have entirely different histories.

7 changed files with 130 additions and 240 deletions

View File

@ -1,23 +1,18 @@
[![RediSkript Build](https://github.com/Limework/RediSkript/actions/workflows/maven.yml/badge.svg?branch=master)](https://github.com/Limework/RediSkript/actions/workflows/maven.yml)
## RediSkript
Allows you to communicate between your Minecraft servers with use of Redis and Skript, it's very fast and easy to use.
RediSkript allows you to communicate between your servers with use of Redis, it's very fast and easy to use.
Skript: https://github.com/SkriptLang/Skript
RediSkript spigot page: https://www.spigotmc.org/resources/rediskript-communicate-between-servers-with-ease.85067/s
RediSkript spigot page: https://www.spigotmc.org/resources/rediskript-communicate-between-servers-with-ease.85067/
Minecraft server version supported: **1.8.8+** (On 1.8, may need to use a fork of Skript)
Jedis: https://github.com/redis/jedis
You can transfer any data in the form of text between your servers, you can program it to execute a set of instructions on the server depending on the redis message, etc. This can be used for making scripts that sync data between all servers and much more!
It is developed and maintained by Govindas & the team of Govindas Limework developers.
There is only one command: **/reloadredis** it fully reloads the configuration, you can reload IP, password, channels and everything else.
You only need to have matching configuration in every server for communication and a Redis server to connect to. I recommend using a VPS for hosting redis server, but there also are free redis hosting options available.
### Redis Message
Redis Message:
```
on redis message:
if redis channel is "world":
@ -29,14 +24,12 @@ command /sendredis <text> <text>:
send redis message arg 1 to channel arg 2
send redis message "hello world!" to channel "world"
```
### Managing variables
Managing variables:
```
set variables "test::1", "test::2", "test::3" in channel "global" to 100
#then use this in any server that listens to "global" redis channel and was online when the above line was executed:
send "%{test::*}%" #outputs 100, 100 and 100
add 100 to variables "test::1" and "test::2" in channel "global"
remove 10 from variable "test::1" in channel "global"
delete variables "test::*" in channel "global"
set variable "test::%uuid of player%" in channel "playerdata" to tool of player
@ -48,8 +41,11 @@ Syntax:
variable[s] %strings% in [redis] [channel] %string%
```
### Configuration
plugins/RediSkript/config.yml
There is only one command: **/reloadredis** it fully reloads the configuration, you can reload IP, password, channels and everything else.
You only need to have matching configuration in every server for communication and a Redis server to connect to. I recommend using VPS for hosting redis server, I personally use VPS from humbleservers.com.
Configuration:
```
Redis:
#a secure password that cannot be cracked, please change it!
@ -76,12 +72,13 @@ Redis:
#the encryption configuration must be the same across all servers in order to communicate
#use 16 characters long key for AES-128 encryption
#32 characters long key for AES-256 encryption (recommended)
#32 characters long key for AES-256 encryption
#AES-128 is faster, but less secure (but it is not crackable by today's technology as of 2020, may be crackable by quantum computers)
#the AES implementation used in RediSkript uses SIV mode, which makes the same key resistant to cracking for a big count of messages without the need of changing the key very often
EncryptMessages: true
#EncryptionKey and MacKey must be different
EncryptionKey: "32CHARACTERS KEY"
MacKey: "32CHARACTERS KEY"
EncryptionKey: "16CHARACTERS KEY"
MacKey: "16CHARACTERS KEY"
#the channels from which this server can receive messages

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>RediSkript</artifactId>
<groupId>net.limework</groupId>
<version>1.3.7-SNAPSHOT</version>
<version>1.3.4</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -57,7 +57,7 @@
<dependency>
<groupId>com.github.SkriptLang</groupId>
<artifactId>Skript</artifactId>
<version>2.6.1</version>
<version>2.5.3</version>
<type>jar</type>
<exclusions>
<exclusion>
@ -73,6 +73,11 @@
<version>1.16.5-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.7.0</version>
</dependency>
<dependency>
<groupId>net.limework</groupId>
<artifactId>RediSkript-core</artifactId>

View File

@ -19,7 +19,6 @@ import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
public class RedisController extends BinaryJedisPubSub implements Runnable {
@ -47,30 +46,18 @@ public class RedisController extends BinaryJedisPubSub implements Runnable {
int maxConnections = config.getInt("Redis.MaxConnections");
//do not allow less than 2 max connections as that causes issues
if (maxConnections < 2) {
maxConnections = 2;
}
if (maxConnections < 2) { maxConnections = 2; }
JConfig.setMaxTotal(maxConnections);
JConfig.setMaxIdle(maxConnections);
JConfig.setMinIdle(1);
JConfig.setBlockWhenExhausted(true);
final String password = config.getString("Redis.Password", "");
if (password.isEmpty()) {
this.jedisPool = new JedisPool(JConfig,
config.getString("Redis.Host", "127.0.0.1"),
config.getInt("Redis.Port", 6379),
config.getInt("Redis.TimeOut", 9000),
config.getBoolean("Redis.useTLS", false));
} else {
this.jedisPool = new JedisPool(JConfig,
config.getString("Redis.Host", "127.0.0.1"),
config.getInt("Redis.Port", 6379),
config.getInt("Redis.TimeOut", 9000),
password,
config.getBoolean("Redis.useTLS", false));
}
this.jedisPool = new JedisPool(JConfig,
config.getString("Redis.Host"),
config.getInt("Redis.Port"),
config.getInt("Redis.TimeOut"),
config.getString("Redis.Password"),
config.getBoolean("Redis.useTLS"));
encryption = new Encryption(config.getBoolean("Redis.EncryptMessages"),
config.getString("Redis.EncryptionKey"),
config.getString("Redis.MacKey"));
@ -114,201 +101,96 @@ public class RedisController extends BinaryJedisPubSub implements Runnable {
}
@Override
public void onMessage(byte[] channel, byte[] message) {
String channelString = new String(channel, StandardCharsets.UTF_8);
String receivedMessage = null;
try {
//if encryption is enabled, decrypt the message, else just convert binary to string
if (this.encryption.isEncryptionEnabled()) {
try {
receivedMessage = encryption.decrypt(message);
} catch (UnauthenticCiphertextException | IllegalBlockSizeException e) {
e.printStackTrace();
public void onMessage(byte[] channel, byte[] message){
String channelString = new String(channel, StandardCharsets.UTF_8);
String receivedMessage = null;
try {
//if encryption is enabled, decrypt the message, else just convert binary to string
if (this.encryption.isEncryptionEnabled()) {
try {
receivedMessage = encryption.decrypt(message);
} catch (UnauthenticCiphertextException | IllegalBlockSizeException e) {
e.printStackTrace();
}
} else {
//encryption is disabled, so let's just get the string
receivedMessage = new String(message, StandardCharsets.UTF_8);
}
} else {
//encryption is disabled, so let's just get the string
receivedMessage = new String(message, StandardCharsets.UTF_8);
}
if (receivedMessage != null) {
JSONObject j = new JSONObject(receivedMessage);
if (j.get("Type").equals("Skript")) {
JSONArray messages = j.getJSONArray("Messages");
RedisMessageEvent event;
for (int i = 0; i < messages.length(); i++) {
event = new RedisMessageEvent(channelString, messages.get(i).toString(), j.getLong("Date"));
//if plugin is disabling, don't call events anymore
if (plugin.isEnabled()) {
RedisMessageEvent finalEvent = event;
Bukkit.getScheduler().runTask(plugin, () -> plugin.getServer().getPluginManager().callEvent(finalEvent));
if (receivedMessage != null) {
JSONObject j = new JSONObject(receivedMessage);
if (j.get("Type").equals("Skript")) {
JSONArray messages = j.getJSONArray("Messages");
RedisMessageEvent event;
for (int i = 0; i < messages.length(); i++) {
event = new RedisMessageEvent(channelString, messages.get(i).toString(), j.getLong("Date"));
//if plugin is disabling, don't call events anymore
if (plugin.isEnabled()) {
RedisMessageEvent finalEvent = event;
Bukkit.getScheduler().runTask(plugin, () -> plugin.getServer().getPluginManager().callEvent(finalEvent));
}
}
}
} else if (j.get("Type").equals("SkriptVariables")) {
} else if (j.get("Type").equals("SkriptVariables")) {
//Transfer variables between servers
//Transfer variables between servers
JSONArray varNames = j.getJSONArray("Names");
Object inputValue;
String changeValue = null;
JSONArray varValues = null;
if (!j.isNull("Values")) {
varValues = j.getJSONArray("Values");
}
for (int i = 0; i < varNames.length(); i++) {
String varName = varNames.get(i).toString();
if (j.isNull("Values")) {
JSONArray variableNames = j.getJSONArray("Names");
Object inputValue;
String changeValue = null;
JSONArray variableValues = null;
if (!j.isNull("Values")) {
variableValues = j.getJSONArray("Values");
}
for (int i = 0; i < variableNames.length(); i++) {
// only check for SET here, because null has to be ignored in all other cases
if (j.getString("Operation").equals("SET")) {
Variables.setVariable(varName, null, null, false);
}
if (j.isNull("Values")) {
//only check for SET here, because null has to be ignored in all other cases
} else {
if (!varValues.isNull(i)) {
changeValue = varValues.get(i).toString();
}
String[] inputs = changeValue.split("\\^", 2);
inputValue = Classes.deserialize(inputs[0], Base64.getDecoder().decode(inputs[1]));
switch (j.getString("Operation")) {
case "ADD":
if (varName.charAt(varName.length() - 1) == '*') {
plugin.getLogger().log(Level.WARNING, "Adding to {::*} variables in RediSkript is not supported. Variable name: " + varName);
continue;
}
Object variable = Variables.getVariable(varName, null, false);
if (variable == null) {
Variables.setVariable(varName, inputValue, null, false);
} else if (variable instanceof Long) {
if (inputValue instanceof Integer) {
inputValue = Long.valueOf((Integer) inputValue);
}
if (inputValue instanceof Long) {
Variables.setVariable(varName, (Long) variable + (Long) inputValue, null, false);
} else if (inputValue instanceof Double) {
if (j.getString("Operation").equals("SET")) {
Variables.setVariable(variableNames.get(i).toString(), null, null, false);
}
// convert Long variable to Double
variable = Double.valueOf((Long) variable);
Variables.setVariable(varName, (Double) variable + (Double) inputValue, null, false);
} else {
// Not supported input type
plugin.getLogger().log(Level.WARNING, "Unsupported add action of data type (" + inputValue.getClass().getName() + ") on variable: " + varName);
continue;
}
} else if (variable instanceof Double) {
if (inputValue instanceof Integer) {
inputValue = Double.valueOf((Integer) inputValue);
}
if (inputValue instanceof Double) {
Variables.setVariable(varName, (Double) variable + (Double) inputValue, null, false);
} else if (inputValue instanceof Long) {
Variables.setVariable(varName, (Double) variable + ((Long) inputValue).doubleValue(), null, false);
} else {
// Not supported input type
plugin.getLogger().log(Level.WARNING, "Unsupported add action of data type (" + inputValue.getClass().getName() + ") on variable: " + varName);
continue;
}
} else if (variable instanceof Integer) {
if (inputValue instanceof Integer) {
Variables.setVariable(varName, (Integer) variable + (Integer) inputValue, null, false);
} else if (inputValue instanceof Double) {
// convert Integer variable to Double
variable = Double.valueOf((Integer) variable);
Variables.setVariable(varName, (Double) variable + (Double) inputValue, null, false);
} else if (inputValue instanceof Long) {
// convert Integer variable to Long
variable = Long.valueOf((Integer) variable);
Variables.setVariable(varName, (Long) variable + (Long) inputValue, null, false);
}
} else {
// Not supported input type
plugin.getLogger().log(Level.WARNING, "Unsupported variable type in add action (" + variable.getClass().getName() + ") on variable: " + varName);
continue;
}
break;
case "REMOVE":
if (varName.charAt(varName.length() - 1) == '*') {
plugin.getLogger().log(Level.WARNING, "Removing from {::*} variables in RediSkript is not supported. Variable name: " + varName);
continue;
}
variable = Variables.getVariable(varName, null, false);
if (variable == null) {
if (inputValue instanceof Long) {
Variables.setVariable(varName, -(Long) inputValue, null, false);
} else if (inputValue instanceof Double) {
Variables.setVariable(varName, -(Double) inputValue, null, false);
} else if (inputValue instanceof Integer) {
Variables.setVariable(varName, -(Integer) inputValue, null, false);
} else {
// Not supported input type
plugin.getLogger().log(Level.WARNING, "Unsupported remove action of data type (" + inputValue.getClass().getName() + ") on variable: " + varName);
continue;
}
} else if (variable instanceof Long) {
if (inputValue instanceof Integer) {
inputValue = Long.valueOf((Integer) inputValue);
}
if (inputValue instanceof Long) {
Variables.setVariable(varName, (Long) variable - (Long) inputValue, null, false);
} else if (inputValue instanceof Double) {
} else {
if (!variableValues.isNull(i)) {
changeValue = variableValues.get(i).toString();
}
String[] inputs = changeValue.split("\\^", 2);
inputValue = Classes.deserialize(inputs[0], Base64.getDecoder().decode(inputs[1]));
switch (j.getString("Operation")) {
case "ADD":
//I will add this once someone tells me how to remove from Skript variable
//because using SET operation has issues with inconvertible types (Double and Long)
//variable = (Variable) Variables.getVariable(variableNames.get(i).toString(), null, false);
// variable.change(null, (Object[]) inputValue, Changer.ChangeMode.REMOVE);
case "REMOVE":
//I will add this once someone tells me how to remove from Skript variable
//because using SET operation has issues with inconvertible types (Double and Long)
//variable = (Variable) Variables.getVariable(variableNames.get(i).toString(), null, false);
// variable.change(null, (Object[]) inputValue, Changer.ChangeMode.REMOVE);
break;
case "SET":
String variableName = variableNames.get(i).toString();
//this is needed, because setting a {variable::*} causes weird behavior, like
//1st set operation is no data, 2nd has data, etc.
//if you set it to null before action, it works correctly
// convert Long variable to Double
variable = Double.valueOf((Long) variable);
Variables.setVariable(varName, (Double) variable - (Double) inputValue, null, false);
} else {
// Not supported input type
plugin.getLogger().log(Level.WARNING, "Unsupported remove action of data type (" + inputValue.getClass().getName() + ") on variable: " + varName);
continue;
}
} else if (variable instanceof Double) {
if (inputValue instanceof Integer) {
inputValue = Double.valueOf((Integer) inputValue);
}
if (inputValue instanceof Double) {
Variables.setVariable(varName, (Double) variable - (Double) inputValue, null, false);
} else if (inputValue instanceof Long) {
Variables.setVariable(varName, (Double) variable - ((Long) inputValue).doubleValue(), null, false);
}
} else if (variable instanceof Integer) {
if (inputValue instanceof Integer) {
Variables.setVariable(varName, (Integer) variable - (Integer) inputValue, null, false);
} else if (inputValue instanceof Long) {
// convert Integer variable to Long
variable = Long.valueOf((Integer) variable);
Variables.setVariable(varName, (Long) variable - (Long) inputValue, null, false);
} else if (inputValue instanceof Double) {
// convert Integer variable to Double
variable = Double.valueOf((Integer) variable);
Variables.setVariable(varName, (Double) variable - (Double) inputValue, null, false);
}
} else {
// Not supported input type
plugin.getLogger().log(Level.WARNING, "Unsupported variable type in remove action (" + variable.getClass().getName() + ") on variable: " + varName);
continue;
}
break;
case "SET":
//this is needed, because setting a {variable::*} causes weird behavior, like
//1st set operation is no data, 2nd has data, etc.
//if you set it to null before action, it works correctly
if (varName.charAt(varName.length() - 1) == '*') {
Variables.setVariable(varName, null, null, false);
}
Variables.setVariable(varNames.get(i).toString(), inputValue, null, false);
break;
if (variableName.charAt(variableName.length()-1) == '*') {
Variables.setVariable(variableName, null, null, false);
}
Variables.setVariable(variableNames.get(i).toString(), inputValue, null, false);
}
}
}
}
}
} catch (Exception e) {
plugin.sendErrorLogs("&cI got a message that was empty from channel " + channelString + " please check your code that you used to send the message. Message content:");
plugin.sendErrorLogs(receivedMessage);
e.printStackTrace();
}
} catch (Exception e) {
plugin.sendErrorLogs("&cI got a message that was empty from channel " + channelString + " please check your code that you used to send the message. Message content:");
plugin.sendErrorLogs(receivedMessage);
e.printStackTrace();
}
}
public void sendMessage(String[] message, String channel) {
JSONObject json = new JSONObject();
@ -344,17 +226,17 @@ public class RedisController extends BinaryJedisPubSub implements Runnable {
//so to avoid issues, it's best to do it always on separate thread
if (plugin.isEnabled()) {
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
try (Jedis j = jedisPool.getResource()) {
try (BinaryJedis j = jedisPool.getResource()) {
j.publish(channel.getBytes(StandardCharsets.UTF_8), message);
} catch (Exception e) {
plugin.sendErrorLogs("Error sending redis message!");
e.printStackTrace();
}
}
});
} else {
//execute sending of redis message on the main thread if plugin is disabling
//so it can still process the sending
try (Jedis j = jedisPool.getResource()) {
try (BinaryJedis j = jedisPool.getResource()) {
j.publish(channel.getBytes(StandardCharsets.UTF_8), message);
} catch (Exception e) {
e.printStackTrace();

View File

@ -73,7 +73,7 @@ public class ExprVariableInChannel extends SimpleExpression<Object> {
@Override
public Class<?>[] acceptChange(Changer.ChangeMode mode) {
//if (mode == Changer.ChangeMode.DELETE || mode == Changer.ChangeMode.SET || mode == Changer.ChangeMode.ADD || mode == Changer.ChangeMode.REMOVE)
if (mode == Changer.ChangeMode.DELETE || mode == Changer.ChangeMode.SET || mode == Changer.ChangeMode.ADD || mode == Changer.ChangeMode.REMOVE)
if (mode == Changer.ChangeMode.DELETE || mode == Changer.ChangeMode.SET)
return CollectionUtils.array(Object.class);
return null;

View File

@ -23,13 +23,13 @@ Redis:
#the encryption configuration must be the same across all servers in order to communicate
#use 16 characters long key for AES-128 encryption
#32 characters long key for AES-256 encryption (recommended)
#32 characters long key for AES-256 encryption
#AES-128 is faster, but less secure (but it is not crackable by today's technology as of 2020, may be crackable by quantum computers)
#the AES implementation used in RediSkript uses SIV mode, which makes the same key resistant to cracking for a big count of messages without the need of changing the key very often
EncryptMessages: true
#EncryptionKey and MacKey must be different
EncryptionKey: "32CHARACTERS KEY"
MacKey: "32CHARACTERS KEY"
EncryptionKey: "16CHARACTERS KEY"
MacKey: "16CHARACTERS KEY"
#the channels from which this server can receive messages
@ -41,4 +41,4 @@ Redis:
Channels:
- "global"
- "servername"
- "Channel3"
- "Channel3"

View File

@ -5,10 +5,21 @@
<parent>
<artifactId>RediSkript</artifactId>
<groupId>net.limework</groupId>
<version>1.3.7-SNAPSHOT</version>
<version>1.3.4</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>RediSkript-core</artifactId>
</project>
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.5.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

15
pom.xml
View File

@ -6,7 +6,7 @@
<groupId>net.limework</groupId>
<artifactId>RediSkript</artifactId>
<version>1.3.7-SNAPSHOT</version>
<version>1.3.4</version>
<modules>
<module>RediSkript-core</module>
<module>RediSkript-bukkit</module>
@ -43,23 +43,18 @@
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20220320</version>
<version>20210307</version>
</dependency>
<dependency>
<groupId>org.cryptomator</groupId>
<artifactId>siv-mode</artifactId>
<version>1.5.0</version>
<version>1.4.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.11.1</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.4.6</version>
<version>2.6.2</version>
</dependency>
</dependencies>
</project>
</project>