Compare commits

...

17 Commits

Author SHA1 Message Date
mlus
c3d2b92805 1.3.4 release 2024-10-11 17:37:38 +08:00
mlus
8de71e885e
Delete .github/workflows/build-1.18.yml 2024-02-19 17:31:09 +08:00
mlus
3ef34e438d
Create build-1.18.yml 2024-02-19 17:28:55 +08:00
mlus
87346b1ac8
Delete .github/workflows/Build-1.18.yml 2024-02-19 17:23:31 +08:00
mlus
a9350bac16
Update Build-1.18.yml 2024-02-19 17:19:25 +08:00
mlus
c66f8c2f83
Create Build-1.18.yml 2024-02-19 17:18:48 +08:00
mlus
7970c87e37 1.3.0 update 2024-02-11 17:29:47 +08:00
mlus
3c64bf81fd
Update gradle.yml 2023-10-01 20:47:57 +08:00
mlus
fbc20a4e42
Update gradle.yml 2023-10-01 20:44:01 +08:00
mlus
427972f2e2
Update gradle.yml 2023-10-01 20:38:47 +08:00
mlus
cde4265d27
Update gradle.yml 2023-10-01 20:27:53 +08:00
mlus
2de68aeeae
Update gradle.yml 2023-10-01 20:16:18 +08:00
mlus
7257cc57b8
Update gradle.yml 2023-10-01 20:12:48 +08:00
mlus
6f39a07e70
Merge pull request #11 from mlus-asuka/mlus-asuka-patch-1
Create gradle.yml
2023-10-01 20:00:05 +08:00
mlus
16b23562a6
Create gradle.yml 2023-10-01 19:59:03 +08:00
mlus-Asuka
94a3541b80 i forgot 2023-09-29 16:59:41 +08:00
mlus-Asuka
e3a17e5b2a update 1.2.1 2023-09-29 16:48:23 +08:00
11 changed files with 296 additions and 114 deletions

42
.github/workflows/gradle.yml vendored Normal file
View File

@ -0,0 +1,42 @@
name: Build-1.19
on:
push:
branches: [ "1.19.2" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Make Gradle Wrapper Executable
run: chmod +x ./gradlew
working-directory: ${{ github.workspace }}
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Build
id: build
uses: gradle/gradle-build-action@v2
with:
arguments: build
env:
SNAPSHOT: true
- name: Build Artifact
uses: actions/upload-artifact@v3
with:
name: Player_Sync
path: |
build/libs/

View File

@ -2,6 +2,7 @@
This is a Minecraft forge mod using Mysql backend to make player data synchronization between different servers. This is a Minecraft forge mod using Mysql backend to make player data synchronization between different servers.
Such as equipment,inventory,effects,experience,food level.Any other mods support is also possible. Such as equipment,inventory,effects,experience,food level.Any other mods support is also possible.
Support version now: Support version now:
1.20.1
1.19-1.19.3 1.19-1.19.3
1.18.2 1.18.2
1.16.5 1.16.5

View File

@ -4,7 +4,7 @@ plugins {
id 'net.minecraftforge.gradle' version '5.1.+' id 'net.minecraftforge.gradle' version '5.1.+'
} }
version = '1.2.0' version = mod_version
group = 'vip.fubuki.playersync' // http://maven.apache.org/guides/mini/guide-naming-conventions.html group = 'vip.fubuki.playersync' // http://maven.apache.org/guides/mini/guide-naming-conventions.html
archivesBaseName = 'playersync' archivesBaseName = 'playersync'

View File

@ -1,4 +1,6 @@
# Sets default memory used for gradle commands. Can be overridden by user or command line properties. # Sets default memory used for gradle commands. Can be overridden by user or command line properties.
# This is required to provide enough memory for the Minecraft decompilation process. # This is required to provide enough memory for the Minecraft decompilation process.
org.gradle.jvmargs=-Xmx3G org.gradle.jvmargs=-Xmx3G
org.gradle.daemon=false org.gradle.daemon=false
mod_version=1.19.2-1.3.4

View File

@ -17,6 +17,7 @@ import vip.fubuki.playersync.sync.ChatSync;
import vip.fubuki.playersync.sync.VanillaSync; import vip.fubuki.playersync.sync.VanillaSync;
import vip.fubuki.playersync.util.JDBCsetUp; import vip.fubuki.playersync.util.JDBCsetUp;
import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
@Mod(PlayerSync.MODID) @Mod(PlayerSync.MODID)
@ -41,13 +42,47 @@ public class PlayerSync
@SubscribeEvent @SubscribeEvent
public void onServerStarting(ServerStartingEvent event) throws SQLException { public void onServerStarting(ServerStartingEvent event) throws SQLException {
JDBCsetUp.executeUpdate("CREATE DATABASE IF NOT EXISTS "+JdbcConfig.DATABASE_NAME.get()); JDBCsetUp.executeUpdate("CREATE DATABASE IF NOT EXISTS "+JdbcConfig.DATABASE_NAME.get(),1);
JDBCsetUp.executeUpdate("""
CREATE TABLE IF NOT EXISTS `player_data` (
`uuid` char(36) NOT NULL,
`inventory` mediumblob,
`armor` blob,
`advancements` blob,
`enderchest` mediumblob,
`effects` blob,
`left_hand` blob,
`cursors` blob,
`xp` int DEFAULT NULL,
`food_level` int DEFAULT NULL,
`score` int DEFAULT NULL,
`health` int DEFAULT NULL,
`online` tinyint(1) DEFAULT NULL,
`last_server` int DEFAULT NULL,
PRIMARY KEY (`uuid`)
);""");
JDBCsetUp.QueryResult queryResult = JDBCsetUp.executeQuery("""
SELECT COUNT(*) AS column_count
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'player_data';
""");
ResultSet resultSet = queryResult.resultSet();
int columnCount = 0;
if(resultSet.next()) {
columnCount = resultSet.getInt("column_count");
}
if(columnCount<14){
JDBCsetUp.executeUpdate("""
ALTER TABLE player_data
ADD COLUMN left_hand blob,
ADD COLUMN cursors blob;
""");
}
JDBCsetUp.executeUpdate("CREATE TABLE IF NOT EXISTS player_data (uuid CHAR(36) NOT NULL," +
"inventory MEDIUMBLOB,armor BLOB,advancements BLOB,enderchest MEDIUMBLOB,effects BLOB," +
"xp int,food_level int,score int,health int,online boolean, last_server int, PRIMARY KEY (uuid))");
JDBCsetUp.executeUpdate("CREATE TABLE IF NOT EXISTS chat (player CHAR(36) NOT NULL,message TEXT," +
"timestamp BIGINT)");
JDBCsetUp.executeUpdate(""" JDBCsetUp.executeUpdate("""
CREATE TABLE IF NOT EXISTS server_info ( CREATE TABLE IF NOT EXISTS server_info (
`id` INT NOT NULL, `id` INT NOT NULL,
@ -59,6 +94,7 @@ public class PlayerSync
"VALUES(" + JdbcConfig.SERVER_ID.get() + ",true," + current + ") " + "VALUES(" + JdbcConfig.SERVER_ID.get() + ",true," + current + ") " +
"ON DUPLICATE KEY UPDATE id= " + JdbcConfig.SERVER_ID.get() +",enable = 1," + "ON DUPLICATE KEY UPDATE id= " + JdbcConfig.SERVER_ID.get() +",enable = 1," +
"last_update=" + current + ";"); "last_update=" + current + ";");
JDBCsetUp.executeUpdate("UPDATE server_info SET enable= 1 WHERE id= "+ JdbcConfig.SERVER_ID.get());
if(ModList.get().isLoaded("curios")) { if(ModList.get().isLoaded("curios")) {
JDBCsetUp.executeUpdate("CREATE TABLE IF NOT EXISTS curios (uuid CHAR(36) NOT NULL,curios_item BLOB, PRIMARY KEY (uuid))"); JDBCsetUp.executeUpdate("CREATE TABLE IF NOT EXISTS curios (uuid CHAR(36) NOT NULL,curios_item BLOB, PRIMARY KEY (uuid))");

View File

@ -11,13 +11,16 @@ import java.util.Random;
public class JdbcConfig { public class JdbcConfig {
public static ForgeConfigSpec COMMON_CONFIG; public static ForgeConfigSpec COMMON_CONFIG;
public static ForgeConfigSpec.ConfigValue<String> HOST; public static ForgeConfigSpec.ConfigValue<String> HOST;
public static ForgeConfigSpec.ConfigValue<String> DATABASE_NAME;
public static ForgeConfigSpec.IntValue PORT; public static ForgeConfigSpec.IntValue PORT;
public static ForgeConfigSpec.ConfigValue<String> USERNAME; public static ForgeConfigSpec.ConfigValue<String> USERNAME;
public static ForgeConfigSpec.ConfigValue<String> PASSWORD; public static ForgeConfigSpec.ConfigValue<String> PASSWORD;
public static ForgeConfigSpec.ConfigValue<String> DATABASE_NAME;
public static ForgeConfigSpec.ConfigValue<List<String>> SYNC_WORLD; public static ForgeConfigSpec.ConfigValue<List<String>> SYNC_WORLD;
public static ForgeConfigSpec.BooleanValue USE_SSL; public static ForgeConfigSpec.BooleanValue USE_SSL;
public static ForgeConfigSpec.BooleanValue SYNC_CHAT; public static ForgeConfigSpec.BooleanValue SYNC_CHAT;
public static ForgeConfigSpec.BooleanValue IS_CHAT_SERVER;
public static ForgeConfigSpec.ConfigValue<String> CHAT_SERVER_IP;
public static ForgeConfigSpec.IntValue CHAT_SERVER_PORT;
public static ForgeConfigSpec.ConfigValue<Integer> SERVER_ID; public static ForgeConfigSpec.ConfigValue<Integer> SERVER_ID;
@ -26,14 +29,17 @@ public class JdbcConfig {
ForgeConfigSpec.Builder COMMON_BUILDER = new ForgeConfigSpec.Builder(); ForgeConfigSpec.Builder COMMON_BUILDER = new ForgeConfigSpec.Builder();
COMMON_BUILDER.comment("General settings").push("general"); COMMON_BUILDER.comment("General settings").push("general");
HOST=COMMON_BUILDER.comment("The host of the database").define("host", "localhost"); HOST=COMMON_BUILDER.comment("The host of the database").define("host", "localhost");
DATABASE_NAME= COMMON_BUILDER.comment("Database name").define("database_name", "playersync");
PORT = COMMON_BUILDER.comment("database port").defineInRange("db_port", 3306, 0, 65535); PORT = COMMON_BUILDER.comment("database port").defineInRange("db_port", 3306, 0, 65535);
USE_SSL = COMMON_BUILDER.comment("whether use SSL").define("use_ssl", false); USE_SSL = COMMON_BUILDER.comment("whether use SSL").define("use_ssl", false);
USERNAME = COMMON_BUILDER.comment("username").define("user_name", "root"); USERNAME = COMMON_BUILDER.comment("username").define("user_name", "root");
PASSWORD = COMMON_BUILDER.comment("password").define("password", "password"); PASSWORD = COMMON_BUILDER.comment("password").define("password", "password");
DATABASE_NAME = COMMON_BUILDER.comment("database name").define("db_name","playersync");
SERVER_ID = COMMON_BUILDER.comment("the server id should be unique").define("Server_id", new Random().nextInt(1,Integer.MAX_VALUE-1)); SERVER_ID = COMMON_BUILDER.comment("the server id should be unique").define("Server_id", new Random().nextInt(1,Integer.MAX_VALUE-1));
SYNC_WORLD = COMMON_BUILDER.comment("The worlds that will be synchronized.If running in server it is supposed to have only one").define("sync_world", new ArrayList<>()); SYNC_WORLD = COMMON_BUILDER.comment("The worlds that will be synchronized.If running in server it is supposed to have only one").define("sync_world", new ArrayList<>());
SYNC_CHAT= COMMON_BUILDER.comment("Whether synchronize chat").define("sync_chat", true); SYNC_CHAT= COMMON_BUILDER.comment("Whether synchronize chat").define("sync_chat", true);
IS_CHAT_SERVER = COMMON_BUILDER.comment("Whether recieve messages from other servers as host").define("IsChatServer",false);
CHAT_SERVER_IP = COMMON_BUILDER.define("ChatServerIP","127.0.0.1");
CHAT_SERVER_PORT = COMMON_BUILDER.defineInRange("ChatServerPort",7900,0,65535);
COMMON_BUILDER.pop(); COMMON_BUILDER.pop();
COMMON_CONFIG = COMMON_BUILDER.build(); COMMON_CONFIG = COMMON_BUILDER.build();
} }

View File

@ -2,45 +2,124 @@ package vip.fubuki.playersync.sync;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.server.players.PlayerList; import net.minecraft.server.players.PlayerList;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod; import vip.fubuki.playersync.config.JdbcConfig;
import vip.fubuki.playersync.util.JDBCsetUp;
import java.sql.ResultSet; import java.io.IOException;
import java.sql.SQLException; import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Objects;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Mod.EventBusSubscriber
public class ChatSync { public class ChatSync {
static int tick = 0;
static long current = System.currentTimeMillis();
public static void register(){}
@SubscribeEvent static PlayerList playerList;
public static void onPlayerChat(net.minecraftforge.event.ServerChatEvent event) throws SQLException {
JDBCsetUp.executeUpdate("INSERT INTO chat (player, message, timestamp) VALUES ('" + event.getUsername() + "', '" + event.getRawText() + "', '" + current + "')"); static ServerSocket serverSocket;
static Socket clientSocket;
static Set<Socket> SocketList;
static ExecutorService executorService = Executors.newCachedThreadPool();
public static void register(){
if(JdbcConfig.IS_CHAT_SERVER.get())
new Thread(ChatSync::ServerSocket).start();
ClientSocket();
MinecraftForge.EVENT_BUS.register(ChatSync.class);
} }
@SubscribeEvent
public static void Tick(net.minecraftforge.event.TickEvent.ServerTickEvent event) throws SQLException { private static void ServerSocket() {
tick++; try {
if(tick == 20) { serverSocket = new ServerSocket(JdbcConfig.CHAT_SERVER_PORT.get());
ReadMessage(event.getServer().getPlayerList()); while (true) {
Socket newSocket = serverSocket.accept();
SocketList.add(newSocket);
executorService.submit(() -> handleClient(newSocket));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
} }
} }
public static void ReadMessage(PlayerList playerList) throws SQLException { private static void handleClient(Socket socket) {
JDBCsetUp.QueryResult queryResult=JDBCsetUp.executeQuery("SELECT * FROM chat WHERE timestamp > " + current); try (InputStream inputStream = socket.getInputStream()) {
ResultSet resultSet= queryResult.getResultSet(); byte[] buffer = new byte[1024];
current = System.currentTimeMillis(); int bytesRead;
tick = 0; while ((bytesRead = inputStream.read(buffer)) != -1) {
while(resultSet.next()) { String message = new String(buffer, 0, bytesRead);
String player = resultSet.getString("player"); broadcastMessage(socket, message);
String message = resultSet.getString("message"); }
Component textComponents = Component.literal(player+": "+message); } catch (IOException e) {
playerList.broadcastSystemMessage(textComponents, true); e.printStackTrace();
} finally {
SocketList.remove(socket);
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
} }
resultSet.close(); }
queryResult.getConnection().close();
private static void broadcastMessage(Socket sender, String message) {
for (Socket socket : SocketList) {
if (!socket.equals(sender)) {
try {
OutputStream outputStream = socket.getOutputStream();
outputStream.write(message.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static void ClientSocket() {
try {
clientSocket = new Socket(JdbcConfig.CHAT_SERVER_IP.get(), JdbcConfig.CHAT_SERVER_PORT.get());
Scanner scanner = new Scanner(clientSocket.getInputStream());
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
Component textComponents = Component.nullToEmpty(line);
playerList.broadcastSystemMessage(textComponents,true);
}
} catch (IOException e) {
e.printStackTrace();
reconnectClient();
}
}
private static void reconnectClient() {
//TODO
}
@SubscribeEvent
public static void onPlayerChat(net.minecraftforge.event.ServerChatEvent event) throws IOException {
String message= event.getUsername()+":"+event.getMessage();
OutputStream outputStream = clientSocket.getOutputStream();
outputStream.write(message.getBytes());
}
@SubscribeEvent
public static void onPlayerJoin(PlayerEvent.PlayerLoggedInEvent event){
playerList= Objects.requireNonNull(event.getEntity().getServer()).getPlayerList();
}
@SubscribeEvent
public static void onPlayerLeave(PlayerEvent.PlayerLoggedOutEvent event){
playerList= Objects.requireNonNull(event.getEntity().getServer()).getPlayerList();
} }
} }

View File

@ -25,7 +25,7 @@ public class ModsSupport {
*/ */
LazyOptional<IItemHandlerModifiable> itemHandler = top.theillusivec4.curios.api.CuriosApi.getCuriosHelper().getEquippedCurios(player); LazyOptional<IItemHandlerModifiable> itemHandler = top.theillusivec4.curios.api.CuriosApi.getCuriosHelper().getEquippedCurios(player);
JDBCsetUp.QueryResult queryResult=JDBCsetUp.executeQuery("SELECT curios_item FROM curios WHERE uuid = '"+player.getUUID()+"'"); JDBCsetUp.QueryResult queryResult=JDBCsetUp.executeQuery("SELECT curios_item FROM curios WHERE uuid = '"+player.getUUID()+"'");
ResultSet resultSet = queryResult.getResultSet(); ResultSet resultSet = queryResult.resultSet();
if(resultSet.next()) { if(resultSet.next()) {
String curios_data=resultSet.getString("curios_item"); String curios_data=resultSet.getString("curios_item");
if(curios_data.length()>2) { if(curios_data.length()>2) {
@ -34,7 +34,7 @@ public class ModsSupport {
for (int i = 0; i < handler.getSlots(); i++) { for (int i = 0; i < handler.getSlots(); i++) {
try { try {
if (curios.get(i) == null) continue; if (curios.get(i) == null) continue;
handler.setStackInSlot(i, ItemStack.of(NbtUtils.snbtToStructure(curios.get(i).replace("|", ",")))); handler.setStackInSlot(i, ItemStack.of(NbtUtils.snbtToStructure(curios.get(i).replace("|", ",").replace("^","\"").replace("<","{").replace(">","}").replace("~", "'"))));
} catch (CommandSyntaxException e) { } catch (CommandSyntaxException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -42,7 +42,7 @@ public class ModsSupport {
}); });
} }
resultSet.close(); resultSet.close();
queryResult.getConnection().close(); queryResult.connection().close();
}else{ }else{
StoreCurios(player,true); StoreCurios(player,true);
} }
@ -61,7 +61,7 @@ public class ModsSupport {
itemHandler.ifPresent(handler -> { itemHandler.ifPresent(handler -> {
for (int i = 0; i < handler.getSlots(); i++) { for (int i = 0; i < handler.getSlots(); i++) {
if (!handler.getStackInSlot(i).isEmpty()) { if (!handler.getStackInSlot(i).isEmpty()) {
String sNBT= handler.getStackInSlot(i).serializeNBT().toString().replace(",", "|"); String sNBT= VanillaSync.serialize(handler.getStackInSlot(i).serializeNBT().toString());
curios.put(i, sNBT); curios.put(i, sNBT);
} }
} }

View File

@ -5,12 +5,14 @@ import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.NbtUtils;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.effect.MobEffect; import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.entity.player.PlayerEvent; import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.event.server.ServerStoppedEvent; import net.minecraftforge.event.server.ServerStoppedEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
@ -42,20 +44,20 @@ public class VanillaSync {
public static void doPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) throws SQLException, CommandSyntaxException, IOException { public static void doPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) throws SQLException, CommandSyntaxException, IOException {
String player_uuid = event.getEntity().getUUID().toString(); String player_uuid = event.getEntity().getUUID().toString();
JDBCsetUp.QueryResult queryResult=JDBCsetUp.executeQuery("SELECT online, last_server FROM player_data WHERE uuid='"+player_uuid+"'"); JDBCsetUp.QueryResult queryResult=JDBCsetUp.executeQuery("SELECT online, last_server FROM player_data WHERE uuid='"+player_uuid+"'");
ResultSet resultSet=queryResult.getResultSet(); ResultSet resultSet=queryResult.resultSet();
ServerPlayer serverPlayer = (ServerPlayer) event.getEntity(); ServerPlayer serverPlayer = (ServerPlayer) event.getEntity();
if(!resultSet.next()){ if(!resultSet.next()){
Store(event.getEntity(),true,Dist.CLIENT.isDedicatedServer()); store(event.getEntity(),true,Dist.CLIENT.isDedicatedServer());
return; return;
} }
boolean online = resultSet.getBoolean("online"); boolean online = resultSet.getBoolean("online");
int lastServer = resultSet.getInt("last_server"); int lastServer = resultSet.getInt("last_server");
queryResult=JDBCsetUp.executeQuery("SELECT * FROM player_data WHERE uuid='"+player_uuid+"'"); queryResult=JDBCsetUp.executeQuery("SELECT * FROM player_data WHERE uuid='"+player_uuid+"'");
resultSet= queryResult.getResultSet(); resultSet= queryResult.resultSet();
if(online) { if(online && lastServer != JdbcConfig.SERVER_ID.get()) {
queryResult=JDBCsetUp.executeQuery("SELECT last_update,enable FROM server_info WHERE id='"+lastServer+"'"); queryResult=JDBCsetUp.executeQuery("SELECT last_update,enable FROM server_info WHERE id='"+lastServer+"'");
ResultSet getServerInfo = queryResult.getResultSet(); ResultSet getServerInfo = queryResult.resultSet();
if(getServerInfo.next()){ if(getServerInfo.next()){
long last_update = getServerInfo.getLong("last_update"); long last_update = getServerInfo.getLong("last_update");
boolean enable = getServerInfo.getBoolean("enable"); boolean enable = getServerInfo.getBoolean("enable");
@ -64,7 +66,7 @@ public class VanillaSync {
serverPlayer.connection.disconnect(Component.translatable("playersync.already_online")); serverPlayer.connection.disconnect(Component.translatable("playersync.already_online"));
return; return;
} }
JDBCsetUp.executeUpdate("UPDATE server_info SET enable=false WHERE id=" + lastServer); JDBCsetUp.executeUpdate("UPDATE server_info SET enable= '0' WHERE id=" + lastServer);
} }
getServerInfo.close(); getServerInfo.close();
@ -72,7 +74,7 @@ public class VanillaSync {
} }
JDBCsetUp.executeUpdate("UPDATE server_info SET last_update=" + System.currentTimeMillis() + " WHERE id=" + JdbcConfig.SERVER_ID.get()); JDBCsetUp.executeUpdate("UPDATE server_info SET last_update=" + System.currentTimeMillis() + " WHERE id=" + JdbcConfig.SERVER_ID.get());
JDBCsetUp.executeUpdate("UPDATE player_data SET online=true,last_server=" + JdbcConfig.SERVER_ID.get() + " WHERE uuid='"+player_uuid+"'"); JDBCsetUp.executeUpdate("UPDATE player_data SET online= '1',last_server=" + JdbcConfig.SERVER_ID.get() + " WHERE uuid='"+player_uuid+"'");
if(resultSet.next()) { if(resultSet.next()) {
//Easy Part //Easy Part
serverPlayer.setHealth(resultSet.getInt("health")); serverPlayer.setHealth(resultSet.getInt("health"));
@ -82,23 +84,27 @@ public class VanillaSync {
serverPlayer.experienceProgress=0; serverPlayer.experienceProgress=0;
serverPlayer.giveExperiencePoints(resultSet.getInt("xp")); serverPlayer.giveExperiencePoints(resultSet.getInt("xp"));
serverPlayer.setScore(resultSet.getInt("score")); serverPlayer.setScore(resultSet.getInt("score"));
//Left Hand
serverPlayer.setItemInHand(InteractionHand.OFF_HAND,ItemStack.of(NbtUtils.snbtToStructure(resultSet.getString("left_hand").replace("|",",").replace("^","\"").replace("<","{").replace(">","}").replace("~", "'"))));
//Cursor
serverPlayer.containerMenu.setCarried(ItemStack.of(NbtUtils.snbtToStructure(resultSet.getString("cursors").replace("|",",").replace("^","\"").replace("<","{").replace(">","}").replace("~", "'"))));
//Equipment //Equipment
String armor_data=resultSet.getString("armor"); String armor_data=resultSet.getString("armor");
if(armor_data.length()>2) { if(armor_data.length()>2) {
Map<Integer, String> equipment = LocalJsonUtil.StringToEntryMap(armor_data); Map<Integer, String> equipment = LocalJsonUtil.StringToEntryMap(armor_data);
for (Map.Entry<Integer, String> entry : equipment.entrySet()) { for (Map.Entry<Integer, String> entry : equipment.entrySet()) {
serverPlayer.getInventory().armor.set(entry.getKey(), Deserialize(entry)); serverPlayer.getInventory().armor.set(entry.getKey(), deserialize(entry));
} }
} }
//Inventory //Inventory
Map<Integer,String> inventory = LocalJsonUtil.StringToEntryMap(resultSet.getString("inventory")); Map<Integer,String> inventory = LocalJsonUtil.StringToEntryMap(resultSet.getString("inventory"));
for (Map.Entry<Integer, String> entry : inventory.entrySet()) { for (Map.Entry<Integer, String> entry : inventory.entrySet()) {
serverPlayer.getInventory().setItem(entry.getKey(),Deserialize(entry)); serverPlayer.getInventory().setItem(entry.getKey(), deserialize(entry));
} }
//Ender chest //Ender chest
Map<Integer,String> ender_chest = LocalJsonUtil.StringToEntryMap(resultSet.getString("enderchest")); Map<Integer,String> ender_chest = LocalJsonUtil.StringToEntryMap(resultSet.getString("enderchest"));
for (Map.Entry<Integer, String> entry : ender_chest.entrySet()) { for (Map.Entry<Integer, String> entry : ender_chest.entrySet()) {
serverPlayer.getEnderChestInventory().setItem(entry.getKey(),Deserialize(entry)); serverPlayer.getEnderChestInventory().setItem(entry.getKey(), deserialize(entry));
} }
//Effects //Effects
String effectData=resultSet.getString("effects"); String effectData=resultSet.getString("effects");
@ -122,7 +128,7 @@ public class VanillaSync {
byte [] bytes=resultSet.getString("advancements").getBytes(); byte [] bytes=resultSet.getString("advancements").getBytes();
Files.write(advancements.toPath(),bytes); Files.write(advancements.toPath(),bytes);
}else{ }else{
File[] files= ScanAdvancementsFile(player_uuid, gameDir); File[] files= scanAdvancementsFile(player_uuid, gameDir);
for (File file : files) { for (File file : files) {
if(file==null) continue; if(file==null) continue;
byte [] bytes=resultSet.getString("advancements").getBytes(); byte [] bytes=resultSet.getString("advancements").getBytes();
@ -139,7 +145,7 @@ public class VanillaSync {
} }
@SubscribeEvent @SubscribeEvent
public static void OnPlayerJoin(PlayerEvent.PlayerLoggedInEvent event){ public static void onPlayerJoin(PlayerEvent.PlayerLoggedInEvent event){
executorService.submit(()->{ executorService.submit(()->{
try { try {
doPlayerJoin(event); doPlayerJoin(event);
@ -150,19 +156,20 @@ public class VanillaSync {
} }
private static ItemStack Deserialize(Map.Entry<Integer, String> entry) throws CommandSyntaxException { public static ItemStack deserialize(Map.Entry<Integer, String> entry) throws CommandSyntaxException {
String nbt= entry.getValue().replace("|",",").replace("^","\"").replace("<","{").replace(">","}").replace("~", "'"); String nbt= entry.getValue().replace("|",",").replace("^","\"").replace("<","{").replace(">","}").replace("~", "'");
CompoundTag compoundTag = NbtUtils.snbtToStructure(nbt); CompoundTag compoundTag = NbtUtils.snbtToStructure(nbt);
return ItemStack.of(compoundTag); return ItemStack.of(compoundTag);
} }
public static String serialize(String object){
return object.replace(",","|").replace("\"","^").replace("{","<").replace("}",">").replace("'","~");
}
public static void doPlayerSaveToFile(PlayerEvent.SaveToFile event) throws SQLException, IOException { public static void doPlayerSaveToFile(PlayerEvent.SaveToFile event) throws SQLException, IOException {
JDBCsetUp.executeUpdate("UPDATE server_info SET last_update=" + System.currentTimeMillis() + " WHERE id=" + JdbcConfig.SERVER_ID.get()); JDBCsetUp.executeUpdate("UPDATE server_info SET last_update=" + System.currentTimeMillis() + " WHERE id=" + JdbcConfig.SERVER_ID.get());
if(!event.getEntity().getTags().contains("player_synced")) return; if(!event.getEntity().getTags().contains("player_synced")) return;
Store(event.getEntity(),false,Dist.CLIENT.isDedicatedServer()); store(event.getEntity(),false,Dist.CLIENT.isDedicatedServer());
//Mod support
ModsSupport modsSupport = new ModsSupport();
modsSupport.onPlayerLeave(event.getEntity());
} }
@SubscribeEvent @SubscribeEvent
@ -178,22 +185,21 @@ public class VanillaSync {
@SubscribeEvent @SubscribeEvent
public static void onServerShutdown(ServerStoppedEvent event) throws SQLException { public static void onServerShutdown(ServerStoppedEvent event) throws SQLException {
JDBCsetUp.executeUpdate("UPDATE server_info SET enable=false WHERE id=" + JdbcConfig.SERVER_ID.get()); JDBCsetUp.executeUpdate("UPDATE server_info SET enable= '0' WHERE id=" + JdbcConfig.SERVER_ID.get());
} }
public static void doPlayerLogout(PlayerEvent.PlayerLoggedOutEvent event) throws SQLException, IOException { public static void doPlayerLogout(PlayerEvent.PlayerLoggedOutEvent event) throws SQLException, IOException {
if(!event.getEntity().getTags().contains("player_synced")) return;
String player_uuid = event.getEntity().getUUID().toString(); String player_uuid = event.getEntity().getUUID().toString();
JDBCsetUp.executeUpdate("UPDATE player_data SET online=false WHERE uuid='"+player_uuid+"'"); JDBCsetUp.executeUpdate("UPDATE player_data SET online= '0' WHERE uuid='"+player_uuid+"'");
Store(event.getEntity(),false,Dist.CLIENT.isDedicatedServer()); store(event.getEntity(),false,Dist.CLIENT.isDedicatedServer());
//Mod support
ModsSupport modsSupport = new ModsSupport();
modsSupport.onPlayerLeave(event.getEntity());
event.getEntity().removeTag("player_synced");
} }
@SubscribeEvent @SubscribeEvent
public static void onPlayerLogout(PlayerEvent.PlayerLoggedOutEvent event) { public static void onPlayerLogout(PlayerEvent.PlayerLoggedOutEvent event) throws SQLException {
//Mod support
ModsSupport modsSupport = new ModsSupport();
modsSupport.onPlayerLeave(event.getEntity());
executorService.submit(()->{ executorService.submit(()->{
try { try {
doPlayerLogout(event); doPlayerLogout(event);
@ -204,32 +210,36 @@ public class VanillaSync {
} }
public static void Store(Player player, boolean init,boolean isServer) throws SQLException, IOException { public static void store(Player player, boolean init, boolean isServer) throws SQLException, IOException {
String player_uuid = player.getUUID().toString(); String player_uuid = player.getUUID().toString();
//Easy part //Easy part
int XP = player.totalExperience; int XP = player.totalExperience;
int score=player.getScore(); int score=player.getScore();
int food_level=player.getFoodData().getFoodLevel(); int food_level=player.getFoodData().getFoodLevel();
int health=(int) player.getHealth(); int health=(int) player.getHealth();
//Left hand
String left_hand = serialize(player.getItemInHand(InteractionHand.OFF_HAND).serializeNBT().toString());
//Cursor
String cursors = serialize(player.containerMenu.getCarried().serializeNBT().toString());
//Equipment //Equipment
Map<Integer,String> equipment =new HashMap<>() ; Map<Integer,String> equipment =new HashMap<>() ;
for (int i = 0; i < player.getInventory().armor.size(); i++) { for (int i = 0; i < player.getInventory().armor.size(); i++) {
ItemStack itemStack = player.getInventory().armor.get(i); ItemStack itemStack = player.getInventory().armor.get(i);
if(itemStack.isEmpty()) continue; if(itemStack.isEmpty()) continue;
equipment.put(i,itemStack.serializeNBT().toString().replace(",","|").replace("\"","^").replace("{","<").replace("}",">").replace("'","~")); equipment.put(i,serialize(itemStack.serializeNBT().toString()));
} }
//inventory //inventory
Inventory inventory = player.getInventory(); Inventory inventory = player.getInventory();
Map<Integer,String> inventoryMap=new HashMap<>(); Map<Integer,String> inventoryMap = new HashMap<>();
for (int i = 0; i < inventory.items.size(); i++) { for (int i = 0; i < inventory.items.size(); i++) {
CompoundTag itemNBT = inventory.items.get(i).serializeNBT(); CompoundTag itemNBT = inventory.items.get(i).serializeNBT();
inventoryMap.put(i,itemNBT.toString().replace(",","|").replace("\"","^").replace("{","<").replace("}",">").replace("'","~")); inventoryMap.put(i,serialize(itemNBT.toString()));
} }
//EnderChest //EnderChest
Map<Integer, String> ender_chest=new HashMap<>(); Map<Integer, String> ender_chest = new HashMap<>();
for (int i=0;i< player.getEnderChestInventory().getContainerSize();i++) { for (int i=0;i< player.getEnderChestInventory().getContainerSize();i++) {
CompoundTag itemNBT = player.getEnderChestInventory().getItem(i).serializeNBT(); CompoundTag itemNBT = player.getEnderChestInventory().getItem(i).serializeNBT();
ender_chest.put(i,itemNBT.toString().replace(",","|").replace("\"","^").replace("{","<").replace("}",">").replace("'","~")); ender_chest.put(i,serialize(itemNBT.toString()));
} }
//Effects //Effects
Map<MobEffect,MobEffectInstance> effects= player.getActiveEffectsMap(); Map<MobEffect,MobEffectInstance> effects= player.getActiveEffectsMap();
@ -246,7 +256,7 @@ public class VanillaSync {
advancements = new File(gameDir, JdbcConfig.SYNC_WORLD.get().get(0)+"/advancements"+"/"+player_uuid+".json"); advancements = new File(gameDir, JdbcConfig.SYNC_WORLD.get().get(0)+"/advancements"+"/"+player_uuid+".json");
}else{ }else{
// File gameDir = Minecraft.getInstance().gameDirectory; // File gameDir = Minecraft.getInstance().gameDirectory;
File[] files=ScanAdvancementsFile(player_uuid, gameDir); File[] files= scanAdvancementsFile(player_uuid, gameDir);
//Get LastModified //Get LastModified
long latestModifiedDate = 0; long latestModifiedDate = 0;
for (File file : files) { for (File file : files) {
@ -265,11 +275,11 @@ public class VanillaSync {
//SQL Operation //SQL Operation
if(init){ if(init){
JDBCsetUp.executeUpdate("INSERT INTO player_data (uuid,armor,inventory,enderchest,advancements,effects,xp,food_level,health,score,online) VALUES ('"+player_uuid+"','"+equipment+"','"+inventoryMap+"','"+ender_chest+"','"+advancements+"','"+effectMap+"','"+XP+"','"+food_level+"','"+health+"','"+score+"',online=true)"); JDBCsetUp.executeUpdate("INSERT INTO player_data (uuid,armor,inventory,enderchest,advancements,effects,xp,food_level,health,score,left_hand,cursors,online) VALUES ('"+player_uuid+"','"+equipment+"','"+inventoryMap+"','"+ender_chest+"','"+advancements+"','"+effectMap+"','"+XP+"','"+food_level+"','"+health+"','"+score+"','"+left_hand+"','"+cursors+"',online=true)");
}else JDBCsetUp.executeUpdate("UPDATE player_data SET inventory = '"+inventoryMap+"',armor='"+equipment+"' ,xp='"+XP+"',effects='"+effectMap+"',enderchest='"+ender_chest+"',score='"+score+"',food_level='"+food_level+"',health='"+health+"',advancements='"+json+"' WHERE uuid = '"+player_uuid+"'"); }else JDBCsetUp.executeUpdate("UPDATE player_data SET inventory = '"+inventoryMap+"',armor='"+equipment+"' ,xp='"+XP+"',effects='"+effectMap+"',enderchest='"+ender_chest+"',score='"+score+"',food_level='"+food_level+"',health='"+health+"',advancements='"+json+"',left_hand='"+left_hand+"',cursors='"+cursors+"' WHERE uuid = '"+player_uuid+"'");
} }
private static File[] ScanAdvancementsFile(String player_uuid, File gameDir) { private static File[] scanAdvancementsFile(String player_uuid, File gameDir) {
File[] files = new File[JdbcConfig.SYNC_WORLD.get().size()]; File[] files = new File[JdbcConfig.SYNC_WORLD.get().size()];
for (int i = 0; i < JdbcConfig.SYNC_WORLD.get().size(); i++) { for (int i = 0; i < JdbcConfig.SYNC_WORLD.get().size(); i++) {
File advanceFile=new File(gameDir, "saves/"+JdbcConfig.SYNC_WORLD.get().get(i)+"/advancements"+"/"+player_uuid+".json"); File advanceFile=new File(gameDir, "saves/"+JdbcConfig.SYNC_WORLD.get().get(i)+"/advancements"+"/"+player_uuid+".json");
@ -279,15 +289,17 @@ public class VanillaSync {
return files; return files;
} }
// @SubscribeEvent static int tick = 0;
// public void RegisterCommand(RegisterCommandsEvent event){
// CommandDispatcher<CommandSourceStack> dispatcher=event.getDispatcher(); @SubscribeEvent
// LiteralCommandNode<CommandSourceStack> cmd = dispatcher.register( public static void onUpdate(TickEvent.LevelTickEvent event) throws SQLException {
// Commands.literal("serializeNBT").executes(context -> {context.getSource().sendSuccess(Component.literal(context.getSource().getPlayer().getItemInHand(InteractionHand.MAIN_HAND).serializeNBT().toString()),true); tick++;
// return 0; if(tick == 1800) {
// }) tick=0;
// ); long current = System.currentTimeMillis();
// } JDBCsetUp.executeUpdate("UPDATE server_info SET last_update ="+current+" WHERE id= "+ JdbcConfig.SERVER_ID.get());
}
}
} }

View File

@ -13,45 +13,52 @@ public class JDBCsetUp {
} }
public static QueryResult executeQuery(String sql) throws SQLException{ public static QueryResult executeQuery(String sql) throws SQLException{
Connection connection = getConnection(); Connection connection = getConnection();
PreparedStatement useStatement = connection.prepareStatement("USE " + JdbcConfig.DATABASE_NAME.get()); try (Statement useStatement = connection.createStatement()) {
useStatement.executeUpdate(); useStatement.execute("USE " + JdbcConfig.DATABASE_NAME.get());
}
PreparedStatement queryStatement = connection.prepareStatement(sql); PreparedStatement queryStatement = connection.prepareStatement(sql);
ResultSet resultSet =queryStatement.executeQuery(); ResultSet resultSet = queryStatement.executeQuery();
return new QueryResult(connection,resultSet); return new QueryResult(connection,resultSet);
} }
public static int executeUpdate(String sql) throws SQLException{ public static void executeUpdate(String sql) throws SQLException{
try (Connection connection = getConnection()) { try (Connection connection = getConnection()) {
try (PreparedStatement useStatement = connection.prepareStatement("USE " + JdbcConfig.DATABASE_NAME.get())) { try (Statement useStatement = connection.createStatement()) {
useStatement.executeUpdate(); useStatement.execute("USE " + JdbcConfig.DATABASE_NAME.get());
} }
try (PreparedStatement updateStatement = connection.prepareStatement(sql)) { try (PreparedStatement updateStatement = connection.prepareStatement(sql)) {
return updateStatement.executeUpdate(); updateStatement.executeUpdate();
} }
} }
} }
public static class QueryResult{ public static void update(String sql, String... argument) throws SQLException{
private final Connection connection; Connection connection = getConnection();
private final ResultSet resultSet;
public QueryResult(Connection connection, ResultSet resultSet) { try (Statement useStatement = connection.createStatement()) {
this.connection = connection; useStatement.execute("USE " + JdbcConfig.DATABASE_NAME.get());
this.resultSet = resultSet;
} }
public Connection getConnection() { PreparedStatement updateStatement = connection.prepareStatement(sql);
return connection; for (int i = 1; i <= argument.length; i++) {
} updateStatement.setString(i,argument[i]);
}
updateStatement.executeUpdate();
}
public ResultSet getResultSet() { public static void executeUpdate(String sql, int i) throws SQLException{
return resultSet; try (Connection connection = getConnection()) {
try (PreparedStatement updateStatement = connection.prepareStatement(sql)) {
updateStatement.executeUpdate();
}
} }
} }
public record QueryResult(Connection connection, ResultSet resultSet) {
}
} }

View File

@ -16,10 +16,7 @@ license="GPL-3.0 license"
[[mods]] #mandatory [[mods]] #mandatory
# The modid of the mod # The modid of the mod
modId="playersync" #mandatory modId="playersync" #mandatory
# The version number of the mod - there's a few well known ${} variables useable here or just hardcode it version="${file.jarVersion}" #mandatory
# ${file.jarVersion} will substitute the value of the Implementation-Version as read from the mod's JAR file metadata
# see the associated build.gradle script for how to populate this completely automatically during a build
version="1.2.0" #mandatory
# A display name for the mod # A display name for the mod
displayName="PlayerSync" #mandatory displayName="PlayerSync" #mandatory
# A URL to query for updates for this mod. See the JSON update specification https://mcforge.readthedocs.io/en/latest/gettingstarted/autoupdate/ # A URL to query for updates for this mod. See the JSON update specification https://mcforge.readthedocs.io/en/latest/gettingstarted/autoupdate/