forked from SteamWar/SteamWar
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb218c33ca | ||
|
|
7b01f11b5b | ||
|
|
fd57ba43e9 | ||
|
|
03005adcd8 | ||
|
|
679d373a1e | ||
|
|
828084a3d6 | ||
|
|
2b782585b1 | ||
|
|
b43965be91 | ||
|
|
d82f306094 | ||
|
|
30cdbe072e | ||
|
|
db7e1a5fc9 |
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.data;
|
||||
|
||||
public class SyncCommands {
|
||||
public static final String RELOAD_PLAYER = "reload_player";
|
||||
public static final String RELOAD_EVENT = "reload_event";
|
||||
}
|
||||
@@ -35,7 +35,8 @@ import java.util.stream.Collectors;
|
||||
public class Fight {
|
||||
|
||||
private static final Table<Fight> table = new Table<>(Fight.class);
|
||||
private static final SelectStatement<Fight> getPage = new SelectStatement<>(table, "SELECT f.*, (b.NodeId IS NULL OR b.AllowReplay) AND (r.NodeId IS NULL OR r.AllowReplay) AS ReplayAllowed FROM Fight f LEFT OUTER JOIN SchematicNode b ON f.BlueSchem = b.NodeId LEFT OUTER JOIN SchematicNode r ON f.RedSchem = r.NodeId ORDER BY FightID DESC LIMIT ?, ?");
|
||||
private static final SelectStatement<Fight> getPage = new SelectStatement<>(table, "SELECT f.*, (b.NodeId IS NULL OR b.Config & 2) AND (r.NodeId IS NULL OR r.Config & 2) AS ReplayAllowed FROM Fight f LEFT OUTER JOIN SchematicNode b ON f.BlueSchem = b.NodeId LEFT OUTER JOIN SchematicNode r ON f.RedSchem = r.NodeId ORDER BY FightID DESC LIMIT ?, ?");
|
||||
private static final SelectStatement<Fight> getById = new SelectStatement<>(table, "SELECT f.*, (b.NodeId IS NULL OR b.Config & 2) AND (r.NodeId IS NULL OR r.Config & 2) AS ReplayAllowed FROM Fight f LEFT OUTER JOIN SchematicNode b ON f.BlueSchem = b.NodeId LEFT OUTER JOIN SchematicNode r ON f.RedSchem = r.NodeId WHERE FightId = ?");
|
||||
private static final Statement insert = table.insertFields(true, "GameMode", "Server", "StartTime", "Duration", "BlueLeader", "RedLeader", "BlueSchem", "RedSchem", "Win", "WinCondition");
|
||||
private static final Statement updateReplayAvailable = table.update(Table.PRIMARY, "ReplayAvailable");
|
||||
|
||||
@@ -51,6 +52,10 @@ public class Fight {
|
||||
return fights;
|
||||
}
|
||||
|
||||
public static Fight getById(int fightID) {
|
||||
return getById.select(fightID);
|
||||
}
|
||||
|
||||
public static int create(String gamemode, String server, Timestamp starttime, int duration, int blueleader, int redleader, Integer blueschem, Integer redschem, int win, String wincondition){
|
||||
return insert.insertGetKey(gamemode, server, starttime, duration, blueleader, redleader, blueschem, redschem, win, wincondition);
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ public class NodeData {
|
||||
@Field(keys = {Table.PRIMARY})
|
||||
private final int nodeId;
|
||||
|
||||
@Field
|
||||
@Field(keys = {Table.PRIMARY})
|
||||
private Timestamp createdAt;
|
||||
|
||||
@Field
|
||||
|
||||
@@ -54,13 +54,13 @@ public enum UserPerm {
|
||||
emptyPrefix = new Prefix("§7", "");
|
||||
p.put(PREFIX_NONE, emptyPrefix);
|
||||
p.put(PREFIX_YOUTUBER, new Prefix("§7", "YT"));
|
||||
p.put(PREFIX_GUIDE, new Prefix("§x§E§5§F§F§8§4", "Guide")); // E5FF84
|
||||
p.put(PREFIX_GUIDE, new Prefix("§x§e§7§6§2§e§d", "Guide")); // E762ED
|
||||
|
||||
p.put(PREFIX_SUPPORTER, new Prefix("§x§6§0§9§5§F§F", "Sup")); // 6095FF
|
||||
p.put(PREFIX_MODERATOR, new Prefix("§x§F§F§A§2§5§0", "Mod")); // FFA250
|
||||
p.put(PREFIX_BUILDER, new Prefix("§x§6§0§F§F§6§9", "Arch")); // 60FF69
|
||||
p.put(PREFIX_DEVELOPER, new Prefix("§x§0§B§B§C§B§9", "Dev")); // 0BBCB9
|
||||
p.put(PREFIX_ADMIN, new Prefix("§x§F§F§2§B§2§B", "Admin")); // FF2B2B
|
||||
p.put(PREFIX_SUPPORTER, new Prefix("§x§6§0§9§5§F§B", "Sup")); // 6095FB
|
||||
p.put(PREFIX_MODERATOR, new Prefix("§x§F§F§A§2§5§C", "Mod")); // FFA25C
|
||||
p.put(PREFIX_BUILDER, new Prefix("§x§6§0§F§F§6§A", "Arch")); // 60FF6A
|
||||
p.put(PREFIX_DEVELOPER, new Prefix("§x§0§B§B§C§B§3", "Dev")); // 0BBCB3
|
||||
p.put(PREFIX_ADMIN, new Prefix("§x§F§F§2§B§2§4", "Admin")); // FF2B24
|
||||
prefixes = Collections.unmodifiableMap(p);
|
||||
}
|
||||
|
||||
|
||||
@@ -397,7 +397,7 @@ REPLAY_WINNER=§e§l{0} §7+§e{1}
|
||||
REPLAY_SOLO_LOSER=§e{0}
|
||||
REPLAY_LOSER=§e{0} §7+§e{1}
|
||||
REPLAY_TIME=§7{0}
|
||||
REPLAY_SERVER=§7{0}
|
||||
REPLAY_SERVER=§7{0} #{1}
|
||||
|
||||
#TutorialCommand
|
||||
TUTORIAL_TITLE=Tutorials
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
key.reset();
|
||||
}
|
||||
} catch (InterruptedException | ClosedWatchServiceException ignored) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (Exception e) {
|
||||
VelocityCore.getLogger().throwing(this.getClass().getName(), "run", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -38,31 +38,51 @@ public class ReplayCommand extends SWCommand {
|
||||
super("replay");
|
||||
}
|
||||
|
||||
@Register
|
||||
public void genericCommand(PlayerChatter sender, int replayId, @StaticValue(value = {"", "-a"}, allowISE=true) @OptionalValue("") boolean isAdmin, @OptionalValue("") String optionalMap) {
|
||||
Fight fight = Fight.getById(replayId);
|
||||
|
||||
if (fight == null) {
|
||||
sender.system("REPLAY_UNAVAILABLE");
|
||||
return;
|
||||
}
|
||||
|
||||
startReplay(sender, isAdmin, optionalMap, fight);
|
||||
}
|
||||
|
||||
@Register
|
||||
public void genericCommand(PlayerChatter sender, @OptionalValue("") String optionalMap) {
|
||||
if (PunishmentCommand.isPunishedWithMessage(sender, Punishment.PunishmentType.NoFightServer))
|
||||
return;
|
||||
|
||||
new SWStreamInv<>(sender, new Message("REPLAY_TITLE"), (click, fight) -> {
|
||||
ArenaMode mode = ArenaMode.getBySchemType(fight.getSchemType());
|
||||
ServerStarter starter = new ServerStarter().replay(fight.getFightID()).blueLeader(sender.getPlayer());
|
||||
|
||||
String map = mode.getRandomMap();
|
||||
if (!optionalMap.equals("")) {
|
||||
String tMap = mode.hasMap(optionalMap);
|
||||
if (tMap != null) map = tMap;
|
||||
}
|
||||
|
||||
if (sender.user().hasPerm(UserPerm.MODERATION) && click.isShiftClick() && fight.replayExists()) {
|
||||
starter.test(mode, map, sender.getPlayer()).start();
|
||||
} else if(!fight.replayAllowed()) {
|
||||
sender.system("REPLAY_UNAVAILABLE");
|
||||
} else {
|
||||
starter.arena(mode, map).start();
|
||||
}
|
||||
startReplay(sender, click.isShiftClick(), optionalMap, fight);
|
||||
}, page -> Fight.getPage(page, 45).stream().map(fight -> new SWListInv.SWListEntry<>(getFightItem(fight), fight)).toList()).open();
|
||||
}
|
||||
|
||||
private void startReplay(PlayerChatter sender, boolean isAdmin, String optionalMap, Fight fight) {
|
||||
if (PunishmentCommand.isPunishedWithMessage(sender, Punishment.PunishmentType.NoFightServer))
|
||||
return;
|
||||
|
||||
ArenaMode mode = ArenaMode.getBySchemType(fight.getSchemType());
|
||||
ServerStarter starter = new ServerStarter().replay(fight.getFightID()).blueLeader(sender.getPlayer());
|
||||
|
||||
String map = mode.getRandomMap();
|
||||
if (!optionalMap.isEmpty()) {
|
||||
String tMap = mode.hasMap(optionalMap);
|
||||
if (tMap != null) map = tMap;
|
||||
}
|
||||
|
||||
if (sender.user().hasPerm(UserPerm.MODERATION) && isAdmin && fight.replayExists()) {
|
||||
System.out.println("Starting admin replay");
|
||||
starter.test(mode, map, sender.getPlayer()).start();
|
||||
} else if(!fight.replayAllowed()) {
|
||||
sender.system("REPLAY_UNAVAILABLE");
|
||||
} else {
|
||||
starter.arena(mode, map).start();
|
||||
}
|
||||
}
|
||||
|
||||
private SWItem getFightItem(Fight fight) {
|
||||
SchematicType type = fight.getSchemType();
|
||||
SWItem item = new SWItem(type != null ? type.getMaterial() : "BARRIER", parseLeader(fight.getBlueLeader(), fight.getBluePlayers().size(), fight.getWin() == 1));
|
||||
@@ -71,7 +91,7 @@ public class ReplayCommand extends SWCommand {
|
||||
lore.add(parseLeader(fight.getRedLeader(), fight.getRedPlayers().size(), fight.getWin() == 2));
|
||||
lore.add(new Message("REPLAY_TIME", fight.getStartTime()));
|
||||
lore.add(new Message("SPACER"));
|
||||
lore.add(new Message("REPLAY_SERVER", fight.getServer()));
|
||||
lore.add(new Message("REPLAY_SERVER", fight.getServer(), Integer.toString(fight.getFightID())));
|
||||
if(!fight.replayAllowed())
|
||||
lore.add(new Message("REPLAY_UNAVAILABLE"));
|
||||
item.setLore(lore);
|
||||
|
||||
@@ -333,7 +333,7 @@ public class TeamCommand extends SWCommand {
|
||||
Team tm = all.get(i);
|
||||
|
||||
sender.prefixless("TEAM_LIST_TEAM", new Message("TEAM_LIST_TEAM_HOVER"),
|
||||
ClickEvent.runCommand("/team info " + tm.getTeamKuerzel()), tm.getTeamColor(), tm.getTeamKuerzel(), tm.getTeamName());
|
||||
ClickEvent.runCommand("/team info " + tm.getTeamName()), tm.getTeamColor(), tm.getTeamKuerzel(), tm.getTeamName());
|
||||
}
|
||||
|
||||
Component beforePage = Component
|
||||
|
||||
@@ -66,7 +66,7 @@ public class WhoisCommand extends SWCommand {
|
||||
|
||||
sender.system("WHOIS_USERNAME", user.getUserName());
|
||||
sender.system("WHOIS_PREFIX", user.prefix().getColorCode() + user.prefix().getChatPrefix());
|
||||
sender.system("WHOIS_TEAM", new Message("WHOIS_TEAM_HOVER", team.getTeamName()), ClickEvent.runCommand("/team info " + team.getTeamKuerzel()), team.getTeamColor(), team.getTeamKuerzel(), team.getTeamName());
|
||||
sender.system("WHOIS_TEAM", new Message("WHOIS_TEAM_HOVER", team.getTeamName()), ClickEvent.runCommand("/team info " + team.getTeamName()), team.getTeamColor(), team.getTeamKuerzel(), team.getTeamName());
|
||||
|
||||
if (!sender.user().hasPerm(UserPerm.TEAM))
|
||||
return;
|
||||
|
||||
@@ -338,7 +338,8 @@ public class PluginMessage extends BasicListener {
|
||||
"frozenlib:moving_restriction_sound_packet", "frozenlib:remove_entity_screen_shakes_packet",
|
||||
"frozenlib:remove_screen_shakes_packet", "frozenlib:screen_shake_entity_packet",
|
||||
"frozenlib:screen_shake_packet", "frozenlib:spotting_icon_packet", "frozenlib:wind_disturbance_packet",
|
||||
"frozenlib:spotting_icon_remove_packet", "frozenlib:starting_moving_restriction_looping_sound_packet"
|
||||
"frozenlib:spotting_icon_remove_packet", "frozenlib:starting_moving_restriction_looping_sound_packet",
|
||||
"imm_ptl:remote_cts" //https://www.curseforge.com/minecraft/mc-mods/immersive-portals-mod
|
||||
))
|
||||
channelRegisterHandlers.put(channel, player -> {});
|
||||
|
||||
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<String, Long>()
|
||||
|
||||
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())
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user