From 7b01f11b5b858540e45e602445746be36325004e Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Sun, 20 Jul 2025 00:06:19 +0200 Subject: [PATCH] Introduce backend synchronization system for Velocity and WebsiteBackend --- .../src/de/steamwar/data/SyncCommands.java | 25 +++++ .../de/steamwar/velocitycore/BackendSync.java | 96 +++++++++++++++++++ .../steamwar/velocitycore/VelocityCore.java | 5 + .../src/de/steamwar/data/VelocitySync.kt | 49 ++++++++++ .../src/de/steamwar/routes/EventFights.kt | 4 + .../src/de/steamwar/routes/UserPerms.kt | 7 ++ 6 files changed, 186 insertions(+) create mode 100644 CommonCore/Data/src/de/steamwar/data/SyncCommands.java create mode 100644 VelocityCore/src/de/steamwar/velocitycore/BackendSync.java create mode 100644 WebsiteBackend/src/de/steamwar/data/VelocitySync.kt diff --git a/CommonCore/Data/src/de/steamwar/data/SyncCommands.java b/CommonCore/Data/src/de/steamwar/data/SyncCommands.java new file mode 100644 index 00000000..b09665ec --- /dev/null +++ b/CommonCore/Data/src/de/steamwar/data/SyncCommands.java @@ -0,0 +1,25 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.data; + +public class SyncCommands { + public static final String RELOAD_PLAYER = "reload_player"; + public static final String RELOAD_EVENT = "reload_event"; +} diff --git a/VelocityCore/src/de/steamwar/velocitycore/BackendSync.java b/VelocityCore/src/de/steamwar/velocitycore/BackendSync.java new file mode 100644 index 00000000..146b6f87 --- /dev/null +++ b/VelocityCore/src/de/steamwar/velocitycore/BackendSync.java @@ -0,0 +1,96 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.velocitycore; + +import de.steamwar.data.SyncCommands; +import de.steamwar.sql.Event; +import de.steamwar.sql.EventFight; +import de.steamwar.sql.SteamwarUser; + +import java.io.File; +import java.io.IOException; +import java.nio.file.*; + +public class BackendSync implements Runnable { + private static final Path syncPath = new File("/run/sync").toPath(); + private final Thread thread; + private final WatchService watchService; + + public BackendSync() { + try { + watchService + = FileSystems.getDefault().newWatchService(); + } catch (IOException e) { + throw new SecurityException("Could not create watch service", e); + } + + + thread = new Thread(this, "BackendSync"); + thread.start(); + } + + public void stop() { + try { + watchService.close(); + thread.join(); + } catch (IOException | InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + private void handleCommand(String name) { + try { + String[] parts = name.split("\\."); + + switch (parts[0]) { + case SyncCommands.RELOAD_EVENT -> EventFight.loadAllComingFights(); + case SyncCommands.RELOAD_PLAYER -> SteamwarUser.invalidate(Integer.parseInt(parts[1])); + default -> VelocityCore.getLogger().warning("Unknown command: " + name); + } + } catch (Exception e) { + VelocityCore.getLogger().throwing(this.getClass().getName(), "handleCommand", e); + } + } + + @Override + public void run() { + try { + syncPath.register(watchService, + java.nio.file.StandardWatchEventKinds.ENTRY_CREATE); + + WatchKey key; + while ((key = watchService.take()) != null) { + for (WatchEvent event : key.pollEvents()) { + String command = event.context().toString(); + handleCommand(command); + + if (!VelocityCore.get().getConfig().isEventmode()) { + Path path = syncPath.resolve((Path) event.context()); + Files.delete(path); + } + } + } + } catch (InterruptedException | ClosedWatchServiceException ignored) { + Thread.currentThread().interrupt(); + } catch (Exception e) { + VelocityCore.getLogger().throwing(this.getClass().getName(), "run", e); + } + } +} diff --git a/VelocityCore/src/de/steamwar/velocitycore/VelocityCore.java b/VelocityCore/src/de/steamwar/velocitycore/VelocityCore.java index aa98a034..9082f122 100644 --- a/VelocityCore/src/de/steamwar/velocitycore/VelocityCore.java +++ b/VelocityCore/src/de/steamwar/velocitycore/VelocityCore.java @@ -95,6 +95,7 @@ public class VelocityCore implements ReloadablePlugin { private Config config; private ErrorLogger errorLogger; private TablistManager tablistManager; + private BackendSync backendSync; @Getter private TeamCommand teamCommand; @@ -147,6 +148,8 @@ public class VelocityCore implements ReloadablePlugin { new ReplayMod(); new FML2(); + backendSync = new BackendSync(); + new ConnectionListener(); new ChatListener(); new BanListener(); @@ -268,6 +271,8 @@ public class VelocityCore implements ReloadablePlugin { logger.log(Level.SEVERE, "Could not shutdown discord bot", e); } + backendSync.stop(); + if(tablistManager != null) tablistManager.disable(); errorLogger.unregister(); diff --git a/WebsiteBackend/src/de/steamwar/data/VelocitySync.kt b/WebsiteBackend/src/de/steamwar/data/VelocitySync.kt new file mode 100644 index 00000000..b3ea6f00 --- /dev/null +++ b/WebsiteBackend/src/de/steamwar/data/VelocitySync.kt @@ -0,0 +1,49 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2025 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.data + +import de.steamwar.sql.SteamwarUser +import java.io.File +import java.nio.file.Files + +object VelocitySync { + private const val SYNC_PATH = "/run/sync" + private val SYNC_FILE = File(SYNC_PATH) + private val isWindows = System.getProperty("os.name").lowercase().contains("win") + private val lastSendMap = mutableMapOf() + + private fun sendCommand(command: String, vararg args: String) { + if (isWindows) { return } + + val name = command + if (args.isNotEmpty()) "." + args.joinToString(".") else "" + + if (lastSendMap[name] != null || lastSendMap[name]!! > System.currentTimeMillis() - 1000) { + return + } + + lastSendMap[name] = System.currentTimeMillis() + + Files.createFile(File(SYNC_FILE, name).toPath()) + } + + fun reloadEvent() = sendCommand(SyncCommands.RELOAD_EVENT) + + fun reloadPlayer(user: SteamwarUser) = sendCommand(SyncCommands.RELOAD_PLAYER, user.id.toString()) +} \ No newline at end of file diff --git a/WebsiteBackend/src/de/steamwar/routes/EventFights.kt b/WebsiteBackend/src/de/steamwar/routes/EventFights.kt index 2f27b7e4..453128b1 100644 --- a/WebsiteBackend/src/de/steamwar/routes/EventFights.kt +++ b/WebsiteBackend/src/de/steamwar/routes/EventFights.kt @@ -20,6 +20,7 @@ package de.steamwar.routes import de.steamwar.ResponseError +import de.steamwar.data.VelocitySync import de.steamwar.sql.* import io.ktor.http.* import io.ktor.server.application.* @@ -112,6 +113,7 @@ fun Route.configureEventFightRoutes() { if (fight.group != null) { eventFight.setGroup(fight.group) } + VelocitySync.reloadEvent() call.respond(HttpStatusCode.Created, ResponseEventFight(eventFight)) } route("/{fight}") { @@ -143,11 +145,13 @@ fun Route.configureEventFightRoutes() { } fight.update(start, spielmodus, map, teamBlue, teamRed, spectatePort) + VelocitySync.reloadEvent() call.respond(HttpStatusCode.OK, ResponseEventFight(fight)) } delete { val fight = call.receiveFight() ?: return@delete fight.delete() + VelocitySync.reloadEvent() call.respond(HttpStatusCode.OK) } diff --git a/WebsiteBackend/src/de/steamwar/routes/UserPerms.kt b/WebsiteBackend/src/de/steamwar/routes/UserPerms.kt index c1558787..1ab68d59 100644 --- a/WebsiteBackend/src/de/steamwar/routes/UserPerms.kt +++ b/WebsiteBackend/src/de/steamwar/routes/UserPerms.kt @@ -19,6 +19,7 @@ package de.steamwar.routes +import de.steamwar.data.VelocitySync import de.steamwar.plugins.SWPermissionCheck import de.steamwar.plugins.getUser import de.steamwar.sql.SteamwarUser @@ -86,6 +87,8 @@ fun Route.configureUserPerms() { } UserPerm.addPerm(user, UserPerm.entries.find { it == prefix }!!) + + VelocitySync.reloadPlayer(user) call.respond(HttpStatusCode.Accepted) } put("/{perm}") { @@ -96,6 +99,8 @@ fun Route.configureUserPerms() { call.respond(HttpStatusCode.Accepted) return@put } + + VelocitySync.reloadPlayer(user) call.respond(HttpStatusCode.NoContent) } delete("/{perm}") { @@ -106,6 +111,8 @@ fun Route.configureUserPerms() { call.respond(HttpStatusCode.Accepted) return@delete } + + VelocitySync.reloadPlayer(user) call.respond(HttpStatusCode.NoContent) } }