9
0
mirror of https://github.com/WiIIiam278/HuskSync.git synced 2026-01-06 15:41:56 +00:00

Userdata command, API expansion, editor interfaces

This commit is contained in:
William
2022-07-08 01:18:01 +01:00
parent 1c9d74f925
commit b7709f2d6c
43 changed files with 1044 additions and 446 deletions

View File

@@ -0,0 +1,20 @@
package net.william278.husksync.redis;
import org.jetbrains.annotations.NotNull;
public enum RedisKeyType {
CACHE(60 * 60 * 24),
DATA_UPDATE(10),
SERVER_SWITCH(10);
public final int timeToLive;
RedisKeyType(int timeToLive) {
this.timeToLive = timeToLive;
}
@NotNull
public String getKeyPrefix() {
return RedisManager.KEY_NAMESPACE.toLowerCase() + ":" + RedisManager.clusterId.toLowerCase() + ":" + name().toLowerCase();
}
}

View File

@@ -1,20 +1,16 @@
package net.william278.husksync.redis;
import net.william278.husksync.HuskSync;
import net.william278.husksync.config.Settings;
import net.william278.husksync.data.DataAdapter;
import net.william278.husksync.data.UserData;
import net.william278.husksync.player.User;
import net.william278.husksync.util.Logger;
import org.jetbrains.annotations.NotNull;
import org.xerial.snappy.Snappy;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.*;
import redis.clients.jedis.exceptions.JedisException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Optional;
import java.util.UUID;
@@ -25,30 +21,25 @@ import java.util.concurrent.CompletableFuture;
*/
public class RedisManager {
private static final String KEY_NAMESPACE = "husksync:";
private static String clusterId = "";
protected static final String KEY_NAMESPACE = "husksync:";
protected static String clusterId = "";
private final HuskSync plugin;
private final JedisPoolConfig jedisPoolConfig;
private final DataAdapter dataAdapter;
private final Logger logger;
private final String redisHost;
private final int redisPort;
private final String redisPassword;
private final boolean redisUseSsl;
private JedisPool jedisPool;
public RedisManager(@NotNull Settings settings, @NotNull DataAdapter dataAdapter, @NotNull Logger logger) {
clusterId = settings.getStringValue(Settings.ConfigOption.CLUSTER_ID);
this.dataAdapter = dataAdapter;
this.logger = logger;
public RedisManager(@NotNull HuskSync plugin) {
this.plugin = plugin;
clusterId = plugin.getSettings().getStringValue(Settings.ConfigOption.CLUSTER_ID);
// Set redis credentials
this.redisHost = settings.getStringValue(Settings.ConfigOption.REDIS_HOST);
this.redisPort = settings.getIntegerValue(Settings.ConfigOption.REDIS_PORT);
this.redisPassword = settings.getStringValue(Settings.ConfigOption.REDIS_PASSWORD);
this.redisUseSsl = settings.getBooleanValue(Settings.ConfigOption.REDIS_USE_SSL);
this.redisHost = plugin.getSettings().getStringValue(Settings.ConfigOption.REDIS_HOST);
this.redisPort = plugin.getSettings().getIntegerValue(Settings.ConfigOption.REDIS_PORT);
this.redisPassword = plugin.getSettings().getStringValue(Settings.ConfigOption.REDIS_PASSWORD);
this.redisUseSsl = plugin.getSettings().getBooleanValue(Settings.ConfigOption.REDIS_USE_SSL);
// Configure the jedis pool
this.jedisPoolConfig = new JedisPoolConfig();
@@ -74,10 +65,50 @@ public class RedisManager {
} catch (JedisException e) {
return false;
}
CompletableFuture.runAsync(this::subscribe);
return true;
});
}
private void subscribe() {
try (final Jedis subscriber = redisPassword.isBlank() ? new Jedis(redisHost, redisPort, 0, redisUseSsl) :
new Jedis(redisHost, redisPort, DefaultJedisClientConfig.builder()
.password(redisPassword).timeoutMillis(0).ssl(redisUseSsl).build())) {
subscriber.connect();
subscriber.subscribe(new JedisPubSub() {
@Override
public void onMessage(@NotNull String channel, @NotNull String message) {
RedisMessageType.getTypeFromChannel(channel).ifPresent(messageType -> {
if (messageType == RedisMessageType.UPDATE_USER_DATA) {
final RedisMessage redisMessage = RedisMessage.fromJson(message);
plugin.getOnlineUser(redisMessage.targetUserUuid).ifPresent(user -> {
final UserData userData = plugin.getDataAdapter().fromBytes(redisMessage.data);
user.setData(userData, plugin.getSettings(), plugin.getEventCannon()).thenRun(() -> {
plugin.getLocales().getLocale("data_update_complete")
.ifPresent(user::sendActionBar);
plugin.getEventCannon().fireSyncCompleteEvent(user);
});
});
}
});
}
}, Arrays.stream(RedisMessageType.values()).map(RedisMessageType::getMessageChannel).toArray(String[]::new));
}
}
protected void sendMessage(@NotNull String channel, @NotNull String message) {
try (Jedis jedis = jedisPool.getResource()) {
jedis.publish(channel, message);
}
}
public CompletableFuture<Void> sendUserDataUpdate(@NotNull User user, @NotNull UserData userData) {
return CompletableFuture.runAsync(() -> {
final RedisMessage redisMessage = new RedisMessage(user.uuid, plugin.getDataAdapter().toBytes(userData));
redisMessage.dispatch(this, RedisMessageType.UPDATE_USER_DATA);
});
}
/**
* Set a user's data to the Redis server
*
@@ -92,9 +123,10 @@ public class RedisManager {
// Set the user's data as a compressed byte array of the json using Snappy
jedis.setex(getKey(RedisKeyType.DATA_UPDATE, user.uuid),
RedisKeyType.DATA_UPDATE.timeToLive,
dataAdapter.toBytes(userData));
logger.debug("[" + user.username + "] Set " + RedisKeyType.DATA_UPDATE.name() + " key to redis at: " +
new SimpleDateFormat("mm:ss.SSS").format(new Date()));
plugin.getDataAdapter().toBytes(userData));
plugin.getLoggingAdapter().debug("[" + user.username + "] Set " + RedisKeyType.DATA_UPDATE.name()
+ " key to redis at: " +
new SimpleDateFormat("mm:ss.SSS").format(new Date()));
}
});
} catch (Exception e) {
@@ -108,8 +140,9 @@ public class RedisManager {
try (Jedis jedis = jedisPool.getResource()) {
jedis.setex(getKey(RedisKeyType.SERVER_SWITCH, user.uuid),
RedisKeyType.SERVER_SWITCH.timeToLive, new byte[0]);
logger.debug("[" + user.username + "] Set " + RedisKeyType.SERVER_SWITCH.name() + " key to redis at: " +
new SimpleDateFormat("mm:ss.SSS").format(new Date()));
plugin.getLoggingAdapter().debug("[" + user.username + "] Set " + RedisKeyType.SERVER_SWITCH.name()
+ " key to redis at: " +
new SimpleDateFormat("mm:ss.SSS").format(new Date()));
} catch (Exception e) {
e.printStackTrace();
}
@@ -126,8 +159,9 @@ public class RedisManager {
return CompletableFuture.supplyAsync(() -> {
try (Jedis jedis = jedisPool.getResource()) {
final byte[] key = getKey(RedisKeyType.DATA_UPDATE, user.uuid);
logger.debug("[" + user.username + "] Read " + RedisKeyType.DATA_UPDATE.name() + " key from redis at: " +
new SimpleDateFormat("mm:ss.SSS").format(new Date()));
plugin.getLoggingAdapter().debug("[" + user.username + "] Read " + RedisKeyType.DATA_UPDATE.name()
+ " key from redis at: " +
new SimpleDateFormat("mm:ss.SSS").format(new Date()));
final byte[] dataByteArray = jedis.get(key);
if (dataByteArray == null) {
return Optional.empty();
@@ -136,7 +170,7 @@ public class RedisManager {
jedis.del(key);
// Use Snappy to decompress the json
return Optional.of(dataAdapter.fromBytes(dataByteArray));
return Optional.of(plugin.getDataAdapter().fromBytes(dataByteArray));
} catch (Exception e) {
e.printStackTrace();
return Optional.empty();
@@ -148,8 +182,9 @@ public class RedisManager {
return CompletableFuture.supplyAsync(() -> {
try (Jedis jedis = jedisPool.getResource()) {
final byte[] key = getKey(RedisKeyType.SERVER_SWITCH, user.uuid);
logger.debug("[" + user.username + "] Read " + RedisKeyType.SERVER_SWITCH.name() + " key from redis at: " +
new SimpleDateFormat("mm:ss.SSS").format(new Date()));
plugin.getLoggingAdapter().debug("[" + user.username + "] Read " + RedisKeyType.SERVER_SWITCH.name()
+ " key from redis at: " +
new SimpleDateFormat("mm:ss.SSS").format(new Date()));
final byte[] readData = jedis.get(key);
if (readData == null) {
return false;
@@ -176,21 +211,4 @@ public class RedisManager {
return (keyType.getKeyPrefix() + ":" + uuid).getBytes(StandardCharsets.UTF_8);
}
public enum RedisKeyType {
CACHE(60 * 60 * 24),
DATA_UPDATE(10),
SERVER_SWITCH(10);
public final int timeToLive;
RedisKeyType(int timeToLive) {
this.timeToLive = timeToLive;
}
@NotNull
public String getKeyPrefix() {
return KEY_NAMESPACE.toLowerCase() + ":" + clusterId.toLowerCase() + ":" + name().toLowerCase();
}
}
}

View File

@@ -0,0 +1,33 @@
package net.william278.husksync.redis;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public class RedisMessage {
public UUID targetUserUuid;
public byte[] data;
public RedisMessage(@NotNull UUID targetUserUuid, byte[] message) {
this.targetUserUuid = targetUserUuid;
this.data = message;
}
public RedisMessage() {
}
public void dispatch(@NotNull RedisManager redisManager, @NotNull RedisMessageType type) {
CompletableFuture.runAsync(() -> redisManager.sendMessage(type.getMessageChannel(),
new GsonBuilder().create().toJson(this)));
}
@NotNull
public static RedisMessage fromJson(@NotNull String json) throws JsonSyntaxException {
return new GsonBuilder().create().fromJson(json, RedisMessage.class);
}
}

View File

@@ -0,0 +1,23 @@
package net.william278.husksync.redis;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Optional;
public enum RedisMessageType {
UPDATE_USER_DATA;
@NotNull
public String getMessageChannel() {
return RedisManager.KEY_NAMESPACE.toLowerCase() + ":" + RedisManager.clusterId.toLowerCase()
+ ":" + name().toLowerCase();
}
public static Optional<RedisMessageType> getTypeFromChannel(@NotNull String messageChannel) {
return Arrays.stream(values()).filter(messageType -> messageType.getMessageChannel()
.equalsIgnoreCase(messageChannel)).findFirst();
}
}