mirror of
https://github.com/WiIIiam278/HuskSync.git
synced 2025-12-26 18:19:10 +00:00
Initial project setup, building, setup planning, readme & redis
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 { }
|
||||
}
|
||||
Reference in New Issue
Block a user