Chat Sync performance enhancement

This commit is contained in:
mlus 2024-08-04 14:20:25 +08:00
parent 201bf95ff5
commit 50e146d7fe
5 changed files with 143 additions and 66 deletions

View File

@ -41,7 +41,7 @@ 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 `playersync`",1); JDBCsetUp.executeUpdate("CREATE DATABASE IF NOT EXISTS "+JdbcConfig.DATABASE_NAME.get(),1);
JDBCsetUp.executeUpdate(""" JDBCsetUp.executeUpdate("""
CREATE TABLE IF NOT EXISTS `player_data` ( CREATE TABLE IF NOT EXISTS `player_data` (
@ -59,8 +59,6 @@ public class PlayerSync
`last_server` int DEFAULT NULL, `last_server` int DEFAULT NULL,
PRIMARY KEY (`uuid`) 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,

View File

@ -14,9 +14,13 @@ public class JdbcConfig {
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;
@ -29,9 +33,13 @@ public class JdbcConfig {
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,54 +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.Connection; import java.io.IOException;
import java.sql.PreparedStatement; import java.io.InputStream;
import java.sql.ResultSet; import java.io.OutputStream;
import java.sql.SQLException; 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 {
String sql = "INSERT INTO chat (player, message, timestamp) VALUES (?, ?, ?)"; static ServerSocket serverSocket;
try (Connection connection = JDBCsetUp.getConnection(); static Socket clientSocket;
PreparedStatement preparedStatement = connection.prepareStatement(sql)) { static Set<Socket> SocketList;
preparedStatement.setString(1, event.getUsername()); static ExecutorService executorService = Executors.newCachedThreadPool();
preparedStatement.setString(2, event.getRawText());
preparedStatement.setLong(3, current); public static void register(){
preparedStatement.executeUpdate(); if(JdbcConfig.IS_CHAT_SERVER.get())
new Thread(ChatSync::ServerSocket).start();
ClientSocket();
MinecraftForge.EVENT_BUS.register(ChatSync.class);
}
private static void ServerSocket() {
try {
serverSocket = new ServerSocket(JdbcConfig.CHAT_SERVER_PORT.get());
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();
}
} }
} }
@SubscribeEvent private static void handleClient(Socket socket) {
public static void Tick(net.minecraftforge.event.TickEvent.ServerTickEvent event) throws SQLException { try (InputStream inputStream = socket.getInputStream()) {
tick++; byte[] buffer = new byte[1024];
if(tick == 20) { int bytesRead;
ReadMessage(event.getServer().getPlayerList()); while ((bytesRead = inputStream.read(buffer)) != -1) {
String message = new String(buffer, 0, bytesRead);
broadcastMessage(socket, message);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
SocketList.remove(socket);
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
} }
} }
public static void ReadMessage(PlayerList playerList) throws SQLException { private static void broadcastMessage(Socket sender, String message) {
JDBCsetUp.QueryResult queryResult=JDBCsetUp.executeQuery("SELECT * FROM chat WHERE timestamp > " + current); for (Socket socket : SocketList) {
ResultSet resultSet= queryResult.getResultSet(); if (!socket.equals(sender)) {
current = System.currentTimeMillis(); try {
tick = 0; OutputStream outputStream = socket.getOutputStream();
while(resultSet.next()) { outputStream.write(message.getBytes());
String player = resultSet.getString("player"); } catch (IOException e) {
String message = resultSet.getString("message"); e.printStackTrace();
Component textComponents = Component.literal(player+": "+message); }
playerList.broadcastSystemMessage(textComponents, true); }
} }
resultSet.close(); }
queryResult.getConnection().close();
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

@ -43,7 +43,7 @@ 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());
@ -52,11 +52,11 @@ public class VanillaSync {
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 && lastServer != JdbcConfig.SERVER_ID.get()) { 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");

View File

@ -13,8 +13,9 @@ 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 `playersync`"); PreparedStatement useStatement = connection.prepareStatement("USE ?");
useStatement.setString(1, JdbcConfig.DATABASE_NAME.get());
useStatement.executeUpdate(); useStatement.executeUpdate();
PreparedStatement queryStatement = connection.prepareStatement(sql); PreparedStatement queryStatement = connection.prepareStatement(sql);
@ -22,42 +23,42 @@ public class JDBCsetUp {
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()) {
PreparedStatement useStatement = connection.prepareStatement("USE `playersync`"); PreparedStatement useStatement = connection.prepareStatement("USE ?");
useStatement.setString(1, JdbcConfig.DATABASE_NAME.get());
useStatement.executeUpdate(); useStatement.executeUpdate();
try (PreparedStatement updateStatement = connection.prepareStatement(sql)) { try (PreparedStatement updateStatement = connection.prepareStatement(sql)) {
return updateStatement.executeUpdate(); updateStatement.executeUpdate();
} }
} }
} }
public static int executeUpdate(String sql,int i) throws SQLException{ public static void Update(String sql, String... argument) throws SQLException{
Connection connection = getConnection();
PreparedStatement useStatement = connection.prepareStatement("USE ?");
useStatement.setString(1, JdbcConfig.DATABASE_NAME.get());
useStatement.executeUpdate();
PreparedStatement updateStatement = connection.prepareStatement(sql);
for (int i = 1; i <= argument.length; i++) {
updateStatement.setString(i,argument[i]);
}
updateStatement.executeUpdate();
}
public static void executeUpdate(String sql, int i) throws SQLException{
try (Connection connection = getConnection()) { try (Connection connection = getConnection()) {
try (PreparedStatement updateStatement = connection.prepareStatement(sql)) { try (PreparedStatement updateStatement = connection.prepareStatement(sql)) {
return updateStatement.executeUpdate(); updateStatement.executeUpdate();
} }
} }
} }
public static class QueryResult{ public record QueryResult(Connection connection, ResultSet resultSet) {
private final Connection connection;
private final ResultSet resultSet;
public QueryResult(Connection connection, ResultSet resultSet) {
this.connection = connection;
this.resultSet = resultSet;
}
public Connection getConnection() {
return connection;
}
public ResultSet getResultSet() {
return resultSet;
}
} }
} }