Merge pull request #127 from mlus-asuka/1.20.4-dev
Mannualy Bump for 1.20.4
This commit is contained in:
commit
d64de0bdb1
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
|
|
@ -19,7 +19,7 @@ jobs:
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Validate Gradle Wrapper
|
- name: Validate Gradle Wrapper
|
||||||
uses: gradle/actions/wrapper-validation@v4
|
uses: gradle/actions/wrapper-validation@v5
|
||||||
|
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
|
|
@ -28,7 +28,7 @@ jobs:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
|
|
||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/actions/setup-gradle@v4
|
uses: gradle/actions/setup-gradle@v5
|
||||||
|
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
run: ./gradlew build
|
run: ./gradlew build
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ mod_name=PlayerSync
|
||||||
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
|
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
|
||||||
mod_license=GPL-3.0 license
|
mod_license=GPL-3.0 license
|
||||||
# The mod version. See https://semver.org/
|
# The mod version. See https://semver.org/
|
||||||
mod_version=2.1.3
|
mod_version=2.1.4
|
||||||
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
|
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
|
||||||
# This should match the base package used for the mod sources.
|
# This should match the base package used for the mod sources.
|
||||||
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html
|
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import net.minecraft.commands.Commands;
|
||||||
import net.neoforged.bus.api.SubscribeEvent;
|
import net.neoforged.bus.api.SubscribeEvent;
|
||||||
import net.neoforged.fml.common.Mod;
|
import net.neoforged.fml.common.Mod;
|
||||||
import net.neoforged.neoforge.event.RegisterCommandsEvent;
|
import net.neoforged.neoforge.event.RegisterCommandsEvent;
|
||||||
import vip.fubuki.playersync.sync.chat.ChatSyncClient;
|
|
||||||
|
|
||||||
@Mod.EventBusSubscriber()
|
@Mod.EventBusSubscriber()
|
||||||
public class CommandInit {
|
public class CommandInit {
|
||||||
|
|
@ -18,7 +17,6 @@ public class CommandInit {
|
||||||
.requires(cs->cs.hasPermission(2))
|
.requires(cs->cs.hasPermission(2))
|
||||||
.then(Commands.literal("reconnect")
|
.then(Commands.literal("reconnect")
|
||||||
.executes(context -> {
|
.executes(context -> {
|
||||||
new ChatSyncClient().run();
|
|
||||||
// context.getSource().sendSuccess(()->MutableComponent.create(new TranslatableContents("playersync.command.reconnect")),true);
|
// context.getSource().sendSuccess(()->MutableComponent.create(new TranslatableContents("playersync.command.reconnect")),true);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import net.neoforged.fml.config.ModConfig;
|
||||||
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent;
|
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||||
import net.neoforged.neoforge.common.NeoForge;
|
import net.neoforged.neoforge.common.NeoForge;
|
||||||
import net.neoforged.neoforge.event.server.ServerStartingEvent;
|
import net.neoforged.neoforge.event.server.ServerStartingEvent;
|
||||||
|
import net.neoforged.neoforge.event.server.ServerStoppingEvent;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import vip.fubuki.playersync.config.JdbcConfig;
|
import vip.fubuki.playersync.config.JdbcConfig;
|
||||||
import vip.fubuki.playersync.sync.ChatSync;
|
import vip.fubuki.playersync.sync.ChatSync;
|
||||||
|
|
@ -186,6 +187,11 @@ public class PlayerSync {
|
||||||
LOGGER.info("PlayerSync is ready!");
|
LOGGER.info("PlayerSync is ready!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public void onServerStopping(ServerStoppingEvent event){
|
||||||
|
ChatSync.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
private static void addColumnIfNotExists(String tableName, String columnName, String dataTypeDefaultNullness,
|
private static void addColumnIfNotExists(String tableName, String columnName, String dataTypeDefaultNullness,
|
||||||
boolean makePrimaryKey) throws SQLException {
|
boolean makePrimaryKey) throws SQLException {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,28 +11,45 @@ import java.io.IOException;
|
||||||
|
|
||||||
public class ChatSync {
|
public class ChatSync {
|
||||||
public static final Logger LOGGER = LogUtils.getLogger();
|
public static final Logger LOGGER = LogUtils.getLogger();
|
||||||
|
private static ChatSyncServer chatSyncServer;
|
||||||
|
private static ChatSyncClient chatSyncClient;
|
||||||
|
|
||||||
public static void register(){
|
public static void register(){
|
||||||
if(JdbcConfig.IS_CHAT_SERVER.get()) {
|
if(JdbcConfig.IS_CHAT_SERVER.get()) {
|
||||||
LOGGER.info("Trying to setup chat server at port " + JdbcConfig.CHAT_SERVER_PORT.get());
|
LOGGER.info("Trying to setup chat server at port " + JdbcConfig.CHAT_SERVER_PORT.get());
|
||||||
new Thread(()->{
|
new Thread(()->{
|
||||||
ChatSyncServer chatSyncServer = new ChatSyncServer();
|
chatSyncServer = new ChatSyncServer();
|
||||||
try {
|
try {
|
||||||
chatSyncServer.run();
|
chatSyncServer.run();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOGGER.error("Unable to start chat server", e);
|
LOGGER.error("Unable to start chat server", e);
|
||||||
}
|
}
|
||||||
}).start();
|
}, "ChatSync-Server").start();
|
||||||
}
|
}
|
||||||
|
|
||||||
new Thread(()->{
|
new Thread(()->{
|
||||||
|
try {
|
||||||
|
Thread.sleep(2000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
LOGGER.info("Trying to connect to chat server "
|
LOGGER.info("Trying to connect to chat server "
|
||||||
+ JdbcConfig.CHAT_SERVER_IP.get()
|
+ JdbcConfig.CHAT_SERVER_IP.get()
|
||||||
+ ":"
|
+ ":"
|
||||||
+ JdbcConfig.CHAT_SERVER_PORT.get());
|
+ JdbcConfig.CHAT_SERVER_PORT.get());
|
||||||
ChatSyncClient chatSyncClient = new ChatSyncClient();
|
chatSyncClient = new ChatSyncClient();
|
||||||
chatSyncClient.run();
|
chatSyncClient.run();
|
||||||
}).start();
|
}, "ChatSync-Client").start();
|
||||||
NeoForge.EVENT_BUS.register(ChatSyncClient.class);
|
NeoForge.EVENT_BUS.register(ChatSyncClient.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void shutdown() {
|
||||||
|
if (chatSyncServer != null) {
|
||||||
|
chatSyncServer.shutdown();
|
||||||
|
}
|
||||||
|
if (chatSyncClient != null) {
|
||||||
|
chatSyncClient.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -145,6 +145,11 @@ public class VanillaSync {
|
||||||
JDBCsetUp.QueryResult qr1 = JDBCsetUp.executeQuery("SELECT online, last_server FROM player_data WHERE uuid='" + player_uuid + "'");
|
JDBCsetUp.QueryResult qr1 = JDBCsetUp.executeQuery("SELECT online, last_server FROM player_data WHERE uuid='" + player_uuid + "'");
|
||||||
ResultSet rs1 = qr1.resultSet();
|
ResultSet rs1 = qr1.resultSet();
|
||||||
ServerPlayer serverPlayer = (ServerPlayer) event.getEntity();
|
ServerPlayer serverPlayer = (ServerPlayer) event.getEntity();
|
||||||
|
|
||||||
|
// Mod support
|
||||||
|
ModsSupport modsSupport = new ModsSupport();
|
||||||
|
modsSupport.onPlayerJoin(serverPlayer);
|
||||||
|
|
||||||
if (!rs1.next()){
|
if (!rs1.next()){
|
||||||
store(event.getEntity(), true);
|
store(event.getEntity(), true);
|
||||||
return;
|
return;
|
||||||
|
|
@ -229,9 +234,6 @@ public class VanillaSync {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mod support
|
|
||||||
ModsSupport modsSupport = new ModsSupport();
|
|
||||||
modsSupport.onPlayerJoin(serverPlayer);
|
|
||||||
serverPlayer.addTag("player_synced");
|
serverPlayer.addTag("player_synced");
|
||||||
|
|
||||||
rs2.close();
|
rs2.close();
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,12 @@ import net.neoforged.neoforge.event.ServerChatEvent;
|
||||||
import net.neoforged.neoforge.event.entity.player.PlayerEvent;
|
import net.neoforged.neoforge.event.entity.player.PlayerEvent;
|
||||||
import vip.fubuki.playersync.PlayerSync;
|
import vip.fubuki.playersync.PlayerSync;
|
||||||
import vip.fubuki.playersync.config.JdbcConfig;
|
import vip.fubuki.playersync.config.JdbcConfig;
|
||||||
import vip.fubuki.playersync.sync.ChatSync;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.*;
|
||||||
import java.io.IOException;
|
import java.net.ConnectException;
|
||||||
import java.io.InputStreamReader;
|
import java.net.InetSocketAddress;
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.net.SocketTimeoutException;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class ChatSyncClient {
|
public class ChatSyncClient {
|
||||||
|
|
@ -21,31 +20,135 @@ public class ChatSyncClient {
|
||||||
static Socket clientSocket;
|
static Socket clientSocket;
|
||||||
static PrintWriter out;
|
static PrintWriter out;
|
||||||
|
|
||||||
|
private static volatile boolean running = true;
|
||||||
|
private static final int RECONNECT_DELAY = 5000;
|
||||||
|
private static final int MAX_RECONNECT_ATTEMPTS = 10;
|
||||||
|
|
||||||
|
private static volatile long lastHeartbeat = System.currentTimeMillis();
|
||||||
|
private static final long HEARTBEAT_INTERVAL = 15000;
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
int reconnectAttempts = 0;
|
||||||
clientSocket = new Socket(JdbcConfig.CHAT_SERVER_IP.get(), JdbcConfig.CHAT_SERVER_PORT.get());
|
|
||||||
out = new PrintWriter(clientSocket.getOutputStream(),true);
|
|
||||||
|
|
||||||
BufferedReader in = new BufferedReader(
|
while (running && reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
|
||||||
new InputStreamReader(clientSocket.getInputStream()));
|
try {
|
||||||
|
PlayerSync.LOGGER.info("Connecting to chat server {}:{}",
|
||||||
|
JdbcConfig.CHAT_SERVER_IP.get(),
|
||||||
|
JdbcConfig.CHAT_SERVER_PORT.get());
|
||||||
|
|
||||||
String serverMessage;
|
clientSocket = new Socket();
|
||||||
while ((serverMessage = in.readLine()) != null) {
|
clientSocket.setReuseAddress(true);
|
||||||
PlayerSync.LOGGER.info("Received message from chat server: " + serverMessage);
|
clientSocket.setKeepAlive(true);
|
||||||
Component textComponents = Component.nullToEmpty(serverMessage);
|
clientSocket.setTcpNoDelay(true);
|
||||||
if(playerList!=null){
|
|
||||||
playerList.broadcastSystemMessage(textComponents,false);
|
clientSocket.connect(
|
||||||
|
new InetSocketAddress(
|
||||||
|
JdbcConfig.CHAT_SERVER_IP.get(),
|
||||||
|
JdbcConfig.CHAT_SERVER_PORT.get()
|
||||||
|
),
|
||||||
|
15000
|
||||||
|
);
|
||||||
|
|
||||||
|
clientSocket.setSoTimeout(30000);
|
||||||
|
|
||||||
|
out = new PrintWriter(new BufferedWriter(
|
||||||
|
new OutputStreamWriter(clientSocket.getOutputStream())), true);
|
||||||
|
|
||||||
|
PlayerSync.LOGGER.info("Successfully connected to chat server");
|
||||||
|
reconnectAttempts = 0;
|
||||||
|
lastHeartbeat = System.currentTimeMillis();
|
||||||
|
|
||||||
|
startHeartbeatMonitor();
|
||||||
|
|
||||||
|
BufferedReader in = new BufferedReader(
|
||||||
|
new InputStreamReader(clientSocket.getInputStream()));
|
||||||
|
|
||||||
|
String serverMessage;
|
||||||
|
while (running && (serverMessage = in.readLine()) != null) {
|
||||||
|
lastHeartbeat = System.currentTimeMillis();
|
||||||
|
|
||||||
|
if ("<heartbeat>".equals(serverMessage)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerSync.LOGGER.info("Received message from chat server: " + serverMessage);
|
||||||
|
Component textComponents = Component.nullToEmpty(serverMessage);
|
||||||
|
if(playerList != null){
|
||||||
|
playerList.getServer().execute(() ->
|
||||||
|
playerList.broadcastSystemMessage(textComponents, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (SocketTimeoutException e) {
|
||||||
|
PlayerSync.LOGGER.warn("Chat server read timeout, reconnecting...");
|
||||||
|
} catch (ConnectException e) {
|
||||||
|
PlayerSync.LOGGER.warn("Cannot connect to chat server: {}", e.getMessage());
|
||||||
|
} catch (IOException e) {
|
||||||
|
PlayerSync.LOGGER.error("Chat client connection error: {}", e.getMessage());
|
||||||
|
} finally {
|
||||||
|
closeConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (running && reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
|
||||||
|
reconnectAttempts++;
|
||||||
|
PlayerSync.LOGGER.warn("Attempting to reconnect to chat server ({}/{})",
|
||||||
|
reconnectAttempts, MAX_RECONNECT_ATTEMPTS);
|
||||||
|
|
||||||
|
try {
|
||||||
|
long delay = Math.min(RECONNECT_DELAY * (long)Math.pow(2, reconnectAttempts-1), 60000);
|
||||||
|
Thread.sleep(delay);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
reconnectClient();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reconnectClient() {
|
private void startHeartbeatMonitor() {
|
||||||
ChatSync.LOGGER.warn("TODO: implement reconnectClient()");
|
Thread heartbeatThread = new Thread(() -> {
|
||||||
//TODO
|
while (running && clientSocket != null && !clientSocket.isClosed()) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(10000); // 每10秒检查一次
|
||||||
|
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
if (now - lastHeartbeat > HEARTBEAT_INTERVAL) {
|
||||||
|
PlayerSync.LOGGER.warn("No heartbeat for {}ms, sending test message",
|
||||||
|
now - lastHeartbeat);
|
||||||
|
|
||||||
|
// 发送测试消息检查连接
|
||||||
|
if (out != null) {
|
||||||
|
out.println("<heartbeat>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, "ChatSync-Heartbeat");
|
||||||
|
heartbeatThread.setDaemon(true);
|
||||||
|
heartbeatThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeConnection() {
|
||||||
|
try {
|
||||||
|
if (out != null) {
|
||||||
|
out.close();
|
||||||
|
out = null;
|
||||||
|
}
|
||||||
|
if (clientSocket != null && !clientSocket.isClosed()) {
|
||||||
|
clientSocket.close();
|
||||||
|
clientSocket = null;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
PlayerSync.LOGGER.error("Error closing connection: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutdown() {
|
||||||
|
running = false;
|
||||||
|
closeConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SubscribeEvent
|
@SubscribeEvent
|
||||||
|
|
|
||||||
|
|
@ -1,63 +1,171 @@
|
||||||
package vip.fubuki.playersync.sync.chat;
|
package vip.fubuki.playersync.sync.chat;
|
||||||
|
|
||||||
|
import vip.fubuki.playersync.PlayerSync;
|
||||||
import vip.fubuki.playersync.config.JdbcConfig;
|
import vip.fubuki.playersync.config.JdbcConfig;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.*;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.net.SocketTimeoutException;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class ChatSyncServer {
|
public class ChatSyncServer {
|
||||||
static ServerSocket serverSocket;
|
static ServerSocket serverSocket;
|
||||||
static final Set<Socket> SocketList = ConcurrentHashMap.newKeySet();
|
static final Set<Socket> SocketList = ConcurrentHashMap.newKeySet();
|
||||||
static final ExecutorService executorService = Executors.newCachedThreadPool();
|
static final ExecutorService executorService = Executors.newCachedThreadPool();
|
||||||
|
private volatile boolean running = true;
|
||||||
|
|
||||||
public void run() throws IOException {
|
public void run() throws IOException {
|
||||||
serverSocket = new ServerSocket(JdbcConfig.CHAT_SERVER_PORT.get());
|
try {
|
||||||
while (!Thread.currentThread().isInterrupted()) {
|
serverSocket = new ServerSocket(JdbcConfig.CHAT_SERVER_PORT.get());
|
||||||
Socket newSocket = serverSocket.accept();
|
serverSocket.setReuseAddress(true);
|
||||||
SocketList.add(newSocket);
|
PlayerSync.LOGGER.info("Chat server started successfully on port {}", JdbcConfig.CHAT_SERVER_PORT.get());
|
||||||
executorService.submit(() -> handleClient(newSocket));
|
|
||||||
|
startHeartbeatBroadcast();
|
||||||
|
|
||||||
|
while (running && !Thread.currentThread().isInterrupted()) {
|
||||||
|
try {
|
||||||
|
Socket newSocket = serverSocket.accept();
|
||||||
|
newSocket.setSoTimeout(30000);
|
||||||
|
SocketList.add(newSocket);
|
||||||
|
executorService.submit(() -> handleClient(newSocket));
|
||||||
|
PlayerSync.LOGGER.info("New client connected, total clients: {}", SocketList.size());
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (running) {
|
||||||
|
PlayerSync.LOGGER.error("Error accepting client connection: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
shutdown();
|
||||||
}
|
}
|
||||||
serverSocket.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleClient(Socket socket) {
|
private void handleClient(Socket socket) {
|
||||||
try (InputStream inputStream = socket.getInputStream()) {
|
String clientInfo = socket.getInetAddress() + ":" + socket.getPort();
|
||||||
byte[] buffer = new byte[1024];
|
|
||||||
int bytesRead;
|
try (BufferedReader reader = new BufferedReader(
|
||||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
new InputStreamReader(socket.getInputStream()))) {
|
||||||
String message = new String(buffer, 0, bytesRead);
|
|
||||||
|
String message;
|
||||||
|
while (running && (message = reader.readLine()) != null) {
|
||||||
|
PlayerSync.LOGGER.info("Received message from {}: {}", clientInfo, message);
|
||||||
broadcastMessage(socket, message);
|
broadcastMessage(socket, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} catch (SocketTimeoutException e) {
|
||||||
|
PlayerSync.LOGGER.warn("Client {} timeout", clientInfo);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
PlayerSync.LOGGER.error("Error handling client {}: {}", clientInfo, e.getMessage());
|
||||||
} finally {
|
} finally {
|
||||||
SocketList.remove(socket);
|
SocketList.remove(socket);
|
||||||
try {
|
try {
|
||||||
socket.close();
|
if (!socket.isClosed()) {
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
PlayerSync.LOGGER.error("Error closing client socket: {}", e.getMessage());
|
||||||
}
|
}
|
||||||
|
PlayerSync.LOGGER.info("Client disconnected, remaining clients: {}", SocketList.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void broadcastMessage(Socket sender, String message) {
|
private void broadcastMessage(Socket sender, String message) {
|
||||||
for (Socket socket : SocketList) {
|
Iterator<Socket> iterator = SocketList.iterator();
|
||||||
if (!socket.equals(sender)) {
|
while (iterator.hasNext()) {
|
||||||
|
Socket socket = iterator.next();
|
||||||
|
if (!socket.equals(sender) && !socket.isClosed()) {
|
||||||
try {
|
try {
|
||||||
OutputStream outputStream = socket.getOutputStream();
|
PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
|
||||||
outputStream.write(message.getBytes());
|
writer.println(message);
|
||||||
outputStream.flush();
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
PlayerSync.LOGGER.error("Error broadcasting to client, removing: {}", e.getMessage());
|
||||||
|
iterator.remove();
|
||||||
|
try {
|
||||||
|
socket.close();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void startHeartbeatBroadcast() {
|
||||||
|
Thread heartbeatThread = new Thread(() -> {
|
||||||
|
while (running) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(20000);
|
||||||
|
broadcastHeartbeat();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, "ChatSync-Server-Heartbeat");
|
||||||
|
heartbeatThread.setDaemon(true);
|
||||||
|
heartbeatThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void broadcastHeartbeat() {
|
||||||
|
Iterator<Socket> iterator = SocketList.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
Socket socket = iterator.next();
|
||||||
|
if (!socket.isClosed()) {
|
||||||
|
try {
|
||||||
|
PrintWriter writer = new PrintWriter(
|
||||||
|
new BufferedWriter(
|
||||||
|
new OutputStreamWriter(socket.getOutputStream())), true);
|
||||||
|
writer.println("<heartbeat>");
|
||||||
|
} catch (IOException e) {
|
||||||
|
PlayerSync.LOGGER.warn("Failed to send heartbeat to client, removing: {}", e.getMessage());
|
||||||
|
iterator.remove();
|
||||||
|
try {
|
||||||
|
socket.close();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutdown() {
|
||||||
|
running = false;
|
||||||
|
try {
|
||||||
|
if (serverSocket != null && !serverSocket.isClosed()) {
|
||||||
|
serverSocket.close();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
PlayerSync.LOGGER.error("Error closing server socket: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Socket socket : SocketList) {
|
||||||
|
try {
|
||||||
|
if (!socket.isClosed()) {
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SocketList.clear();
|
||||||
|
|
||||||
|
executorService.shutdown();
|
||||||
|
try {
|
||||||
|
if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) {
|
||||||
|
executorService.shutdownNow();
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
executorService.shutdownNow();
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user