9
0
mirror of https://github.com/WiIIiam278/HuskSync.git synced 2025-12-26 10:09:10 +00:00

Initial project setup, building, setup planning, readme & redis

This commit is contained in:
William
2021-10-19 18:06:36 +01:00
parent 53bdad288a
commit 2a7371be31
30 changed files with 900 additions and 152 deletions

View File

@@ -0,0 +1,57 @@
package me.william278.crossserversync;
import java.io.Serializable;
import java.util.UUID;
public class PlayerData implements Serializable {
/**
* The UUID of the player who this data belongs to
*/
private final UUID playerUUID;
/**
* The unique version UUID of this data
*/
private final UUID dataVersionUUID;
/**
* Serialized inventory data
*/
private final String serializedInventory;
/**
* Serialized ender chest data
*/
private final String serializedEnderChest;
//todo add more stuff, like ender chest, player health, max health, hunger and status effects, et cetera
/**
* Create a new PlayerData object; a random data version UUID will be selected.
* @param playerUUID The UUID of the player
* @param serializedInventory The player's serialized inventory data
*/
public PlayerData(UUID playerUUID, String serializedInventory, String serializedEnderChest) {
this.dataVersionUUID = UUID.randomUUID();
this.playerUUID = playerUUID;
this.serializedInventory = serializedInventory;
this.serializedEnderChest = serializedEnderChest;
}
public UUID getPlayerUUID() {
return playerUUID;
}
public UUID getDataVersionUUID() {
return dataVersionUUID;
}
public String getSerializedInventory() {
return serializedInventory;
}
public String getSerializedEnderChest() {
return serializedEnderChest;
}
}

View File

@@ -0,0 +1,52 @@
package me.william278.crossserversync;
public class Settings {
/*
* General settings
*/
// The type of THIS server (Bungee or Bukkit)
public static ServerType serverType;
// Redis settings
public static String redisHost;
public static int redisPort;
public static String redisPassword;
/*
* Bungee / Proxy server-only settings
*/
// SQL settings
public static DataStorageType dataStorageType;
// MySQL specific settings
public static String mySQLHost;
public static String mySQLDatabase;
public static String mySQLUsername;
public static String mySQLPassword;
public static int mySQLPort;
public static String mySQLParams;
// Hikari connection pooling settings
public static int hikariMaximumPoolSize;
public static int hikariMinimumIdle;
public static long hikariMaximumLifetime;
public static long hikariKeepAliveTime;
public static long hikariConnectionTimeOut;
/*
* Enum definitions
*/
public enum ServerType {
BUKKIT,
BUNGEECORD
}
public enum DataStorageType {
MYSQL,
SQLITE
}
}

View File

@@ -0,0 +1,52 @@
package me.william278.crossserversync.redis;
import me.william278.crossserversync.Settings;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;
import java.util.logging.Level;
public abstract class RedisListener {
/**
* Handle an incoming {@link RedisMessage}
* @param message The {@link RedisMessage} to handle
*/
public abstract void handleMessage(RedisMessage message);
/**
* Log to console
* @param level The {@link Level} to log
* @param message Message to log
*/
public abstract void log(Level level, String message);
/**
* Start the Redis listener
*/
public final void listen() {
Jedis jedis = new Jedis(Settings.redisHost, Settings.redisPort);
final String jedisPassword = Settings.redisPassword;
if (!jedisPassword.equals("")) {
jedis.auth(jedisPassword);
}
jedis.connect();
if (jedis.isConnected()) {
log(Level.INFO,"Enabled Redis listener successfully!");
new Thread(() -> jedis.subscribe(new JedisPubSub() {
@Override
public void onMessage(String channel, String message) {
// Only accept messages to the CrossServerSync channel
if (!channel.equals(RedisMessage.REDIS_CHANNEL)) {
return;
}
// Handle the message
handleMessage(new RedisMessage(message));
}
}, RedisMessage.REDIS_CHANNEL), "Redis Subscriber").start();
} else {
log(Level.SEVERE, "Failed to initialize the redis listener!");
}
}
}

View File

@@ -0,0 +1,117 @@
package me.william278.crossserversync.redis;
import me.william278.crossserversync.Settings;
import redis.clients.jedis.Jedis;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.StringJoiner;
import java.util.UUID;
public class RedisMessage {
public static String REDIS_CHANNEL = "CrossServerSync";
public static String MESSAGE_META_SEPARATOR = "";
public static String MESSAGE_DATA_SEPARATOR = "";
private final String messageData;
private final MessageType messageType;
private MessageTarget messageTarget;
/**
* Create a new RedisMessage
* @param type The type of the message
* @param target Who will receive this message
* @param messageData The message data elements
*/
public RedisMessage(MessageType type, MessageTarget target, String... messageData) {
final StringJoiner messageDataJoiner = new StringJoiner(MESSAGE_DATA_SEPARATOR);
for (String dataElement : messageData) {
messageDataJoiner.add(dataElement);
}
this.messageData = messageDataJoiner.toString();
this.messageType = type;
this.messageTarget = target;
}
/**
* Get a new RedisMessage from an incoming message string
* @param messageString The message string to parse
*/
public RedisMessage(String messageString) {
String[] messageMetaElements = messageString.split(MESSAGE_META_SEPARATOR);
messageType = MessageType.valueOf(messageMetaElements[0]);
messageData = messageMetaElements[2];
try (ObjectInputStream stream = new ObjectInputStream(new ByteArrayInputStream(messageMetaElements[1].getBytes()))) {
messageTarget = (MessageTarget) stream.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* Returns the full, formatted message string with type, target & data
* @return The fully formatted message
*/
private String getFullMessage() {
return new StringJoiner(MESSAGE_META_SEPARATOR)
.add(messageType.toString()).add(messageTarget.toString()).add(messageData)
.toString();
}
/**
* Send the redis message
*/
public void send() {
try (Jedis publisher = new Jedis(Settings.redisHost, Settings.redisPort)) {
final String jedisPassword = Settings.redisPassword;
if (!jedisPassword.equals("")) {
publisher.auth(jedisPassword);
}
publisher.connect();
publisher.publish(REDIS_CHANNEL, getFullMessage());
}
}
public String getMessageData() {
return messageData;
}
public MessageType getMessageType() {
return messageType;
}
public MessageTarget getMessageTarget() {
return messageTarget;
}
/**
* Defines the type of the message
*/
public enum MessageType {
/**
* Sent by Bukkit servers to proxy when a player disconnects with a player's updated data, alongside the UUID of the last loaded {@link me.william278.crossserversync.PlayerData} for the user
*/
PLAYER_DATA_UPDATE,
/**
* Sent by Bukkit servers to proxy to request {@link me.william278.crossserversync.PlayerData} from the proxy.
*/
PLAYER_DATA_REQUEST,
/**
* Sent by the Proxy to reply to a {@code MessageType.PLAYER_DATA_REQUEST}, contains the latest {@link me.william278.crossserversync.PlayerData} for the requester.
*/
PLAYER_DATA_REPLY
}
/**
* A record that defines the target of a plugin message; a spigot server or the proxy server(s).
* For Bukkit servers, the name of the server must also be specified
*/
public record MessageTarget(Settings.ServerType targetServerType, UUID targetPlayerName) implements Serializable { }
}