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)
}
}