Compare commits

...

13 Commits

Author SHA1 Message Date
Chaoscaot 916f9b2557 Merge pull request 'SteamWar CLI merge' (#330) from feature/steamwar-cli into main
SteamWarCI Build successful
Reviewed-on: #330
Reviewed-by: YoyoNow <yoyonow@noreply.localhost>
2026-05-13 22:42:59 +02:00
YoyoNow 34992344b2 Fix CheckCommand.accept
SteamWarCI Build successful
2026-05-12 16:47:30 +02:00
YoyoNow 1ea8dea381 Fix WhoisCommand.whois
SteamWarCI Build successful
2026-05-11 21:03:51 +02:00
Chaoscaot 15aa0572f3 Merge pull request 'Implement old bau behavior for WGS' (#332) from old-bau-behavior into main
SteamWarCI Build successful
Reviewed-on: #332
Reviewed-by: D4rkr34lm <dark@steamwar.de>
2026-05-10 18:55:25 +02:00
Chaoscaot 6a843f4a71 Implement old bau behavior for WGS
SteamWarCI Build successful
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-10 11:29:26 +02:00
Chaoscaot a63c1a94ca Fix TNT explosion logic to handle non-TNT entities correctly
SteamWarCI Build successful
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-09 21:10:43 +02:00
Chaoscaot 43263035d9 Merge pull request 'Add DB Indexes for future local Development deployments' (#262) from add-db-indexes into main
SteamWarCI Build successful
Reviewed-on: #262
Reviewed-by: YoyoNow <yoyonow@noreply.localhost>
2026-05-09 20:23:56 +02:00
Chaoscaot 42ab55d0f8 Merge branch 'main' into add-db-indexes
SteamWarCI Build successful
# Conflicts:
#	CommonCore/SQL/src/de/steamwar/sql/BannedUserIPs.kt
2026-05-09 20:22:15 +02:00
Chaoscaot 44846cce57 Unpack CLI distribution in release
SteamWarCI Build successful
- Remove CLI installDist from build step
- Copy the packaged sw.zip into /jars during release
- Use `rm -rf` for the existing `/jars/sw` cleanup
2026-05-09 16:51:04 +02:00
Chaoscaot 1451750bcb Add SteamWar CLI module
SteamWarCI Build successful
- add Clikt-based `sw` entrypoint and subcommands
- include database, user, dev, and profiler commands
- wire CLI build and CI install/release steps
2026-05-09 16:46:59 +02:00
YoyoNow 8ade5180cb Fix FightSystem
SteamWarCI Build successful
2026-05-08 20:55:17 +02:00
Chaoscaot 73f903fc23 Merge branch 'main' into add-db-indexes
SteamWarCI Build successful
2026-01-23 23:04:11 +01:00
Chaoscaot 22ed7e23da Add DB Indexes for future local Development deployments
SteamWarCI Build successful
Signed-off-by: Chaoscaot <max@maxsp.de>
2025-12-25 20:51:57 +01:00
46 changed files with 687 additions and 70 deletions
@@ -67,11 +67,7 @@ public class TNTListener implements Listener, ScoreboardElement {
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onExplode(EntityExplodeEvent event) {
if (!(event.getEntity() instanceof TNTPrimed)) {
event.blockList().clear();
return;
}
explode(event.blockList(), true);
explode(event.blockList(), event.getEntity() instanceof TNTPrimed);
}
@Override
+32
View File
@@ -0,0 +1,32 @@
plugins {
steamwar.kotlin
application
}
kotlin {
jvmToolchain(21)
}
java {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
application {
mainClass.set("de.steamwar.MainKt")
applicationName = "sw"
}
dependencies {
implementation(project(":CommonCore:SQL"))
implementation("com.github.ajalt.clikt:clikt:5.0.3")
implementation("com.github.ajalt.mordant:mordant:3.0.2")
implementation(libs.logback)
implementation("org.mariadb.jdbc:mariadb-java-client:3.3.1")
implementation(libs.exposedCore)
implementation(libs.exposedDao)
implementation(libs.exposedJdbc)
implementation(libs.exposedTime)
}
+22
View File
@@ -0,0 +1,22 @@
package de.steamwar
import com.github.ajalt.clikt.core.main
import com.github.ajalt.clikt.core.subcommands
import de.steamwar.commands.SteamWar
import de.steamwar.commands.database.DatabaseCommand
import de.steamwar.commands.database.InfoCommand
import de.steamwar.commands.database.ResetCommand
import de.steamwar.commands.dev.DevCommand
import de.steamwar.commands.profiler.ProfilerCommand
import de.steamwar.commands.user.UserCommand
import de.steamwar.commands.user.UserInfoCommand
import de.steamwar.commands.user.UserSearchCommand
fun main(args: Array<String>) = SteamWar()
.subcommands(
DatabaseCommand().subcommands(InfoCommand(), ResetCommand()),
UserCommand().subcommands(UserInfoCommand(), UserSearchCommand()),
DevCommand(),
ProfilerCommand()
)
.main(args)
+10
View File
@@ -0,0 +1,10 @@
package de.steamwar.commands
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.mordant.rendering.TextStyles
class SteamWar: CliktCommand(name = "sw") {
override fun run() {
echo(TextStyles.bold("SteamWar-CLI"))
}
}
@@ -0,0 +1,22 @@
package de.steamwar.commands.database
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.CliktError
import com.github.ajalt.clikt.core.Context
import com.github.ajalt.clikt.core.findOrSetObject
import com.github.ajalt.clikt.parameters.options.flag
import com.github.ajalt.clikt.parameters.options.option
import de.steamwar.db.Database
class DatabaseCommand: CliktCommand(name = "db") {
val useProduction by option().flag()
val db by findOrSetObject { Database }
override fun help(context: Context): String = "Run database commands"
override fun run() {
if (!useProduction && db.database == "production") {
throw CliktError("You should not use the production database!")
}
}
}
+25
View File
@@ -0,0 +1,25 @@
package de.steamwar.commands.database
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.requireObject
import com.github.ajalt.mordant.table.table
import de.steamwar.db.Database
import de.steamwar.db.execute
import de.steamwar.db.useDb
class InfoCommand: CliktCommand() {
val db by requireObject<Database>()
override fun run() = useDb {
val tables = execute("SHOW TABLES") { it.getString(1) }
echo(
table {
header { row("Name") }
body {
tables.map { row(it) }
}
}
)
}
}
+33
View File
@@ -0,0 +1,33 @@
package de.steamwar.commands.database
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.CliktError
import com.github.ajalt.clikt.core.requireObject
import com.github.ajalt.mordant.rendering.TextColors
import com.github.ajalt.mordant.rendering.TextStyles
import de.steamwar.db.Database
import de.steamwar.db.execute
import de.steamwar.db.useDb
import java.io.File
class ResetCommand: CliktCommand() {
val db by requireObject<Database>()
override fun run() = useDb {
val schemaFile = File("/var/Schema.sql")
if (!schemaFile.exists()) {
throw CliktError("Schema file not found!")
}
val schema = schemaFile.readText()
val tables = execute("SHOW TABLES;") { it.getString(1) }
for (table in tables) {
execute("DROP TABLE IF EXISTS $table;") { }
}
execute(schema) { }
echo(TextColors.brightGreen(TextStyles.bold("Database reset!")))
}
}
+179
View File
@@ -0,0 +1,179 @@
package de.steamwar.commands.dev
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.CliktError
import com.github.ajalt.clikt.core.Context
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.arguments.help
import com.github.ajalt.clikt.parameters.arguments.multiple
import com.github.ajalt.clikt.parameters.options.default
import com.github.ajalt.clikt.parameters.options.defaultLazy
import com.github.ajalt.clikt.parameters.options.flag
import com.github.ajalt.clikt.parameters.options.help
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.types.file
import com.github.ajalt.clikt.parameters.types.long
import com.github.ajalt.clikt.parameters.types.path
import com.sun.security.auth.module.UnixSystem
import java.io.File
import kotlin.io.path.absolute
import kotlin.io.path.absolutePathString
const val LOG4J_CONFIG = """<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" packages="com.mojang.util">
<Appenders>
<Console name="WINDOWS_COMPAT" target="SYSTEM_OUT"></Console>
<Queue name="TerminalConsole">
<PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg{nolookups}%n" />
</Queue>
<RollingRandomAccessFile name="File" fileName="$\{'sys:logPath'}/latest.log" filePattern="$\{'sys:logPath'}/%d{yyyy.MM.dd}.log.gz">
<PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %msg{nolookups}%n" />
<Policies>
<TimeBasedTriggeringPolicy />
</Policies>
<DefaultRolloverStrategy max="7"/>
</RollingRandomAccessFile>
</Appenders>
<Loggers>
<Root level="info">
<filters>
<MarkerFilter marker="NETWORK_PACKETS" onMatch="DENY" onMismatch="NEUTRAL" />
</filters>
<AppenderRef ref="WINDOWS_COMPAT" level="info"/>
<AppenderRef ref="File"/>
<AppenderRef ref="TerminalConsole" level="info"/>
</Root>
</Loggers>
</Configuration>"""
class DevCommand : CliktCommand("dev") {
override fun help(context: Context): String = "Start a dev Server"
override val treatUnknownOptionsAsArgs = true
val server by argument().help("Server Template")
val port by option("--port").long().defaultLazy { UnixSystem().uid + 1010 }.help("Port for Server")
val world by option("--world", "-w").path(canBeFile = false).help("User World")
val plugins by option("--plugins", "-p").path(true, canBeFile = false).help("Plugin Dir")
val profile by option().flag().help("Add Profiling Arguments")
val forceUpgrade by option().flag().help("Force Upgrade")
val jar by option().file(true, canBeDir = false).help("Jar File")
val jvm by option().file(true, canBeDir = false).help("Java Executable")
val jvmArgs by argument().multiple()
override val printHelpOnEmptyArgs = true
val workingDir = File("").absoluteFile
val log4jConfig = File(workingDir, "log4j2.xml")
override fun run() {
val args = mutableListOf<String>()
val serverDirectory = File(workingDir, server)
val serverDir =
if (serverDirectory.exists() && serverDirectory.isDirectory) serverDirectory else File(workingDir, server)
if (isVelocity(server)) {
runServer(args, jvmArgs, listOf(jar?.absolutePath ?: File("/jar/Velocity.jar").absolutePath), serverDir)
} else {
setLogConfig(args)
val version = findVersion(server) ?: throw CliktError("Unknown Server Version")
val worldFile = world?.absolute()?.toFile() ?: File(serverDir, "devtempworld")
val jarFile = jar?.absolutePath ?: additionalVersions[server]?.let { supportedVersionJars[it] } ?: supportedVersionJars[version]
?: throw CliktError("Unknown Server Version")
if (!worldFile.exists()) {
val templateFile = File(serverDir, "Bauwelt")
if (!templateFile.exists()) {
throw CliktError("World Template not found!")
}
templateFile.copyRecursively(worldFile)
}
val devFile = File("/configs/DevServer/${System.getProperty("user.name")}.$port.$version")
if (System.getProperty("user.name") != "minecraft") {
devFile.createNewFile()
}
runServer(
args, jvmArgs, listOf(
jarFile,
*(if (forceUpgrade) arrayOf("-forceUpgrade") else arrayOf()),
"--port", port.toString(),
"--level-name", worldFile.name,
"--world-dir", workingDir.absolutePath,
"--nogui",
*(if (plugins != null) arrayOf("--plugins", plugins!!.absolutePathString()) else arrayOf())
), serverDir
)
try {
devFile.delete()
} catch (_: Exception) { /* ignored */ }
}
}
val jvmDefaultParams = arrayOf(
"-Xmx1G",
"-Xgc:excessiveGCratio=80",
"-Xsyslog:none",
"-Xtrace:none",
"-Xnoclassgc",
"-Xdisableexplicitgc",
"-XX:+AlwaysPreTouch",
"-XX:+CompactStrings",
"-XX:-HeapDumpOnOutOfMemory",
"-XX:+ExitOnOutOfMemoryError"
)
val jvmArgOverrides = arrayOf("--add-opens", "java.base/jdk.internal.misc=ALL-UNNAMED")
val supportedVersionJars = mapOf(
8 to "/jars/paper-1.8.8.jar",
9 to "/jars/spigot-1.9.4.jar",
10 to "/jars/paper-1.10.2.jar",
12 to "/jars/spigot-1.12.2.jar",
14 to "/jars/spigot-1.14.4.jar",
15 to "/jars/spigot-1.15.2.jar",
18 to "/jars/paper-1.18.2.jar",
19 to "/jars/paper-1.19.3.jar",
20 to "/jars/paper-1.20.1.jar",
21 to "/jars/paper-1.21.6.jar"
)
val additionalVersions = mapOf(
"Tutorial" to 15,
"Lobby" to 20
)
fun findVersion(server: String): Int? = server.dropWhile { !it.isDigit() }.toIntOrNull()
fun isJava8(server: String): Boolean = findVersion(server)?.let { it <= 10 } ?: false
fun isVelocity(server: String): Boolean = server.endsWith("Velocity")
fun setLogConfig(args: MutableList<String>) {
args += "-DlogPath=${workingDir.absolutePath}/logs"
args += "-Dlog4j.configurationFile=${log4jConfig.absolutePath}"
if (!log4jConfig.exists()) {
log4jConfig.writeText(LOG4J_CONFIG)
}
}
fun runServer(args: List<String>, jvmArgs: List<String>, cmd: List<String>, serverDir: File) {
val process = ProcessBuilder(
jvm?.absolutePath ?: if (isJava8(server)) "/usr/lib/jvm/openj9-8/bin/java" else "java",
*jvmArgs.toTypedArray(),
*args.toTypedArray(),
*jvmDefaultParams,
*(if (isJava8(server)) arrayOf() else jvmArgOverrides),
*(if (profile) arrayOf("-javaagent:/jars/LixfelsProfiler.jar=start") else arrayOf()),
"-Xshareclasses:nonfatal,name=$server",
"-jar",
*cmd.toTypedArray()
).directory(serverDir).inheritIO().start()
Runtime.getRuntime().addShutdownHook(Thread { if (process.isAlive) process.destroyForcibly() })
process.waitFor()
}
}
@@ -0,0 +1,42 @@
package de.steamwar.commands.profiler
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.Context
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.arguments.help
import com.github.ajalt.clikt.parameters.arguments.optional
import com.github.ajalt.clikt.parameters.options.default
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.types.int
const val SPARK = "/jars/spark.jar"
class ProfilerCommand: CliktCommand("profiler") {
val pid by argument().help("Process id").int().optional()
val port by option("--port", "-p").int().default(8543)
override fun run() {
if (pid != null) {
ProcessBuilder()
.command("java", "-jar", SPARK, pid.toString(), "port=$port")
.start()
.waitFor()
Thread.sleep(1000)
ProcessBuilder()
.command("ssh", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-p", port.toString(), "spark@localhost")
.inheritIO()
.start()
.waitFor()
} else {
ProcessBuilder()
.command("java", "-jar", SPARK)
.inheritIO()
.start()
.waitFor()
}
}
override fun help(context: Context): String = "Start a profiler"
}
+9
View File
@@ -0,0 +1,9 @@
package de.steamwar.commands.user
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.Context
class UserCommand: CliktCommand("user") {
override fun run() = Unit
override fun help(context: Context): String = "User related commands"
}
+65
View File
@@ -0,0 +1,65 @@
package de.steamwar.commands.user
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.CliktError
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.arguments.help
import com.github.ajalt.mordant.table.table
import de.steamwar.db.findUser
import de.steamwar.db.useDb
import de.steamwar.sql.Punishment
import de.steamwar.sql.SessionTable
import de.steamwar.sql.SteamwarUser
import de.steamwar.sql.Team
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.jdbc.selectAll
import java.time.Duration
class UserInfoCommand : CliktCommand("info") {
val userId by argument().help("Id, Name, UUID or DiscordId")
val user by lazy { findUser(userId) ?: throw CliktError("User not found") }
override val printHelpOnEmptyArgs = true
override fun run() = useDb {
val sessions =
SessionTable.selectAll().where { SessionTable.userId eq user.id.value }
.map { it[SessionTable.startTime] to it[SessionTable.endTime] }
val totalPlayed = sessions.sumOf { Duration.between(it.first, it.second).toMinutes() } / 60.0
val firstJoin = sessions.minByOrNull { it.first }?.first
val lastJoin = sessions.maxByOrNull { it.second }?.second
val punishments = Punishment.getAllPunishmentsOfPlayer(user.id.value)
echo(
table {
body {
row("Name", user.userName)
row("UUID", user.uuid)
row("Team", Team.byId(user.team).teamName)
row("Leader", user.leader)
row("Locale", user.locale)
row("Beigetreten am", firstJoin)
row("Zuletzt gesehen am", lastJoin)
row("Spielzeit", totalPlayed.toString() + "h")
row("Punishments", if (punishments.isEmpty()) "Keine" else table {
header { row("Typ", "Ersteller", "Von", "Bis", "Grund") }
body {
punishments.map {
row(
it.type,
SteamwarUser.byId(it.punisher)?.userName ?: it.punisher,
it.startTime.toString(),
if (it.perma) "Perma" else it.endTime.toString(),
it.reason
)
}
}
})
}
}
)
}
}
@@ -0,0 +1,42 @@
package de.steamwar.commands.user
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.Context
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.arguments.help
import com.github.ajalt.mordant.table.table
import de.steamwar.db.joinedOr
import de.steamwar.db.useDb
import de.steamwar.sql.SteamwarUser
import de.steamwar.sql.SteamwarUserTable
import de.steamwar.sql.Team
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.core.like
class UserSearchCommand : CliktCommand("search") {
val query by argument().help("Name, Id, UUID or DiscordId")
override val printHelpOnEmptyArgs = true
override fun help(context: Context): String = "Search for users"
override fun run() = useDb {
val users = SteamwarUser.find {
joinedOr(
SteamwarUserTable.username like "%$query%",
SteamwarUserTable.uuid like "%$query%",
query.toLongOrNull()?.let { SteamwarUserTable.discordId eq it },
query.toIntOrNull()?.let { SteamwarUserTable.id eq it }
)
}
val teams = mutableMapOf<Int, Team>()
echo(table {
header { row("Id", "Username", "UUID", "Team", "DiscordId") }
body {
users.map { row(it.id.value, it.userName, it.uuid, teams.computeIfAbsent(it.team) { teamId -> Team.byId(teamId) }.teamName, it.discordId) }
}
})
}
}
+84
View File
@@ -0,0 +1,84 @@
package de.steamwar.db
import com.github.ajalt.clikt.core.BaseCliktCommand
import com.github.ajalt.clikt.core.CliktError
import de.steamwar.sql.SteamwarUser
import de.steamwar.sql.SteamwarUserTable
import org.jetbrains.exposed.v1.core.Expression
import org.jetbrains.exposed.v1.core.Op
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.core.or
import org.jetbrains.exposed.v1.jdbc.Database
import org.jetbrains.exposed.v1.jdbc.JdbcTransaction
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
import java.io.File
import java.sql.ResultSet
import java.util.Properties
object Database {
lateinit var host: String
lateinit var port: String
lateinit var database: String
lateinit var db: Database
fun ensureConnected() {
if (::db.isInitialized) {
return
}
val config = File(System.getProperty("user.home"), "mysql.properties")
if (!config.exists()) {
throw CliktError("Config file not found!")
}
val props = Properties();
props.load(config.inputStream())
host = props.getProperty("host")
port = props.getProperty("port")
database = props.getProperty("database")
val username = props.getProperty("user")
val password = props.getProperty("password")
val url = "jdbc:mariadb://$host:$port/$database"
db = Database.connect(url, driver = "org.mariadb.jdbc.Driver", user = username, password = password)
return
}
}
fun <T: BaseCliktCommand<T>> BaseCliktCommand<T>.findUser(query: String): SteamwarUser? = transaction {
SteamwarUser.find { joinedOr(query.toIntOrNull()?.let { SteamwarUserTable.id eq it }, (SteamwarUserTable.username eq query), SteamwarUserTable.uuid eq query, query.toLongOrNull()?.let { SteamwarUserTable.discordId eq it }) }
.firstOrNull()
?.let { return@transaction it }
}
fun joinedOr(vararg expressions: Expression<Boolean>?): Op<Boolean> =
expressions.filterNotNull().reduce { acc, expression -> acc or expression } as Op<Boolean>
fun <T> JdbcTransaction.execute(sql: String, transform: (ResultSet) -> T): List<T> {
val result = mutableListOf<T>()
exec(sql) { rs ->
while (rs.next()) {
result += transform(rs)
}
}
return result
}
fun <T> JdbcTransaction.executeSingle(sql: String, transform: (ResultSet) -> T): T? {
return execute(sql) { rs ->
if (!rs.next()) {
return@execute null
}
transform(rs)
}.single()
}
fun useDb(statement: JdbcTransaction.() -> Unit) {
de.steamwar.db.Database.ensureConnected()
transaction(de.steamwar.db.Database.db, statement)
}
+11
View File
@@ -0,0 +1,11 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="WARN">
<appender-ref ref="STDOUT" />
</root>
</configuration>
@@ -28,11 +28,11 @@ import org.jetbrains.exposed.v1.javatime.timestamp
import java.time.Instant
object AuditLogTable: IntIdTable("AuditLog", "AuditLogId") {
val time = timestamp("Time")
val server = varchar("ServerName", 255)
val serverOwner = reference("ServerOwner", SteamwarUserTable).nullable()
val actor = reference("Actor", SteamwarUserTable)
val action = enumerationByName("ActionType", 255, AuditLog.Type::class)
val time = timestamp("Time").index()
val server = varchar("ServerName", 255).index()
val serverOwner = reference("ServerOwner", SteamwarUserTable).nullable().index()
val actor = reference("Actor", SteamwarUserTable).index()
val action = enumerationByName("ActionType", 255, AuditLog.Type::class).index()
val actionText = text("ActionText")
}
@@ -33,8 +33,8 @@ import java.sql.Timestamp
import java.time.Instant
object BannedUserIPsTable: CompositeIdTable("BannedUserIPs") {
val userId = reference("UserID", SteamwarUserTable)
val timestamp = timestamp("Timestamp")
val userId = reference("UserID", SteamwarUserTable).index()
val timestamp = timestamp("Timestamp").index()
val ip = varchar("IP", 45).entityId()
init {
@@ -32,8 +32,8 @@ import org.jetbrains.exposed.v1.jdbc.insertIgnore
import java.util.*
object BauweltMemberTable: CompositeIdTable("BauweltMember") {
val bauweltId = reference("BauweltID", SteamwarUserTable)
val memberId = reference("MemberID", SteamwarUserTable)
val bauweltId = reference("BauweltID", SteamwarUserTable).index()
val memberId = reference("MemberID", SteamwarUserTable).index()
val build = bool("Build")
val worldEdit = bool("WorldEdit")
val world = bool("World")
@@ -20,6 +20,7 @@
package de.steamwar.sql
import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.core.ReferenceOption
import org.jetbrains.exposed.v1.core.SortOrder
import org.jetbrains.exposed.v1.core.and
import org.jetbrains.exposed.v1.core.dao.id.CompositeID
@@ -34,19 +35,23 @@ import org.jetbrains.exposed.v1.jdbc.insertIgnore
import java.sql.Timestamp
object CheckedSchematicTable: CompositeIdTable("CheckedSchematic") {
val nodeId = optReference("NodeId", SchematicNodeTable)
val nodeOwner = reference("NodeOwner", SteamwarUserTable)
val nodeName = varchar("NodeName", 64).entityId()
val validator = reference("Validator", SteamwarUserTable)
val startTime = timestamp("StartTime").entityId()
val nodeId = optReference("NodeId", SchematicNodeTable, onDelete = ReferenceOption.SET_NULL, onUpdate = ReferenceOption.SET_NULL).index()
val nodeOwner = reference("NodeOwner", SteamwarUserTable).index()
val nodeName = varchar("NodeName", 64).entityId().index()
val validator = reference("Validator", SteamwarUserTable).index()
val startTime = timestamp("StartTime").entityId().index()
val endTime = timestamp("EndTime")
val declineReason = text("DeclineReason")
val seen = bool("Seen")
val seen = bool("Seen").index()
val nodeType = varchar("NodeType", 16)
init {
addIdColumn(nodeOwner)
addIdColumn(nodeName)
index(false, nodeOwner, endTime)
index(false, startTime, endTime, nodeName)
index(false, seen, nodeOwner, startTime)
}
}
+2 -2
View File
@@ -36,8 +36,8 @@ import java.time.Instant
object EventTable : IntIdTable("Event", "EventId") {
val name = varchar("EventName", 100).uniqueIndex()
val deadline = timestamp("Deadline")
val start = timestamp("Start")
val end = timestamp("End")
val start = timestamp("Start").index()
val end = timestamp("End").index()
val maxPlayers = integer("MaximumTeamMembers")
val schemType = varchar("SchemType", 16).nullable()
val publicsOnly = bool("PublicSchemsOnly")
@@ -33,17 +33,17 @@ import java.time.Instant
import java.util.*
object EventFightTable : IntIdTable("EventFight", "FightID") {
val eventId = reference("EventID", EventTable)
val startTime = timestamp("StartTime")
val eventId = reference("EventID", EventTable).index()
val startTime = timestamp("StartTime").index()
val gamemode = text("Spielmodus")
val map = text("Map")
val groupId = optReference("GroupId", EventGroupTable)
val teamBlue = reference("TeamBlue", TeamTable)
val teamRed = reference("TeamRed", TeamTable)
val groupId = optReference("GroupId", EventGroupTable).index()
val teamBlue = reference("TeamBlue", TeamTable).index()
val teamRed = reference("TeamRed", TeamTable).index()
val spectatePort = integer("SpectatePort").nullable()
val bestOf = integer("BestOf")
val ergebnis = integer("Ergebnis")
val fight = optReference("Fight", FightTable)
val fight = optReference("Fight", FightTable).index()
}
class EventFight(id: EntityID<Int>) : IntEntity(id), Comparable<EventFight> {
@@ -34,6 +34,10 @@ object EventGroupTable : IntIdTable("EventGroup", "Id") {
val pointsPerWin = integer("PointsPerWin").default(3)
val pointsPerLoss = integer("PointsPerLoss").default(0)
val pointsPerDraw = integer("PointsPerDraw").default(1)
init {
uniqueIndex(event, name)
}
}
class EventGroup(id: EntityID<Int>) : IntEntity(id) {
@@ -29,7 +29,7 @@ import org.jetbrains.exposed.v1.dao.IntEntityClass
import org.jetbrains.exposed.v1.jdbc.select
object EventRelationTable : IntIdTable("EventRelation") {
val fightId = reference("FightId", EventFightTable)
val fightId = reference("FightId", EventFightTable).index()
val fightTeam = enumeration("FightTeam", EventRelation.FightTeam::class)
val fromType = enumeration("FromType", EventRelation.FromType::class)
val fromId = integer("FromId")
+5 -5
View File
@@ -34,14 +34,14 @@ import org.jetbrains.exposed.v1.jdbc.update
import java.sql.Timestamp
object FightTable : IntIdTable("Fight", "FightId") {
val gamemode = varchar("Gamemode", 30)
val gamemode = varchar("Gamemode", 30).index()
val server = text("Server")
val startTime = timestamp("StartTime")
val duration = integer("Duration")
val blueLeader = reference("BlueLeader", SteamwarUserTable)
val redLeader = reference("RedLeader", SteamwarUserTable)
val blueSchem = optReference("BlueSchem", SchematicNodeTable, onDelete = ReferenceOption.SET_NULL)
val redSchem = optReference("RedSchem", SchematicNodeTable, onDelete = ReferenceOption.SET_NULL)
val blueLeader = reference("BlueLeader", SteamwarUserTable).index()
val redLeader = reference("RedLeader", SteamwarUserTable).index()
val blueSchem = optReference("BlueSchem", SchematicNodeTable, onDelete = ReferenceOption.SET_NULL).index()
val redSchem = optReference("RedSchem", SchematicNodeTable, onDelete = ReferenceOption.SET_NULL).index()
val win = enumeration("Win", Fight.WinningTeam::class)
val winCondition = varchar("WinCondition", 100)
val replayAvailable = bool("ReplayAvailable")
@@ -30,7 +30,7 @@ import org.jetbrains.exposed.v1.jdbc.insertIgnore
object FightPlayerTable : CompositeIdTable("FightPlayer") {
val fightId = reference("FightId", FightTable)
val userId = reference("UserId", SteamwarUserTable)
val userId = reference("UserId", SteamwarUserTable).index()
val team = integer("Team")
val kit = varchar("Kit", 64)
val kills = integer("Kills")
@@ -30,8 +30,8 @@ import org.jetbrains.exposed.v1.dao.CompositeEntityClass
import java.util.*
object IgnoreSystemTable: CompositeIdTable("IgnoredPlayers") {
val ignorer = reference("Ignorer", SteamwarUserTable)
val ignored = reference("Ignored", SteamwarUserTable)
val ignorer = reference("Ignorer", SteamwarUserTable).index()
val ignored = reference("Ignored", SteamwarUserTable).index()
override val primaryKey = PrimaryKey(ignorer, ignored)
@@ -35,7 +35,7 @@ import java.io.InputStream
import java.util.zip.GZIPInputStream
object NodeDataTable: CompositeIdTable("NodeData") {
val nodeId = reference("NodeId", SchematicNodeTable)
val nodeId = reference("NodeId", SchematicNodeTable).index()
val createdAt = timestamp("CreatedAt").defaultExpression(CurrentTimestamp).entityId()
val nodeFormat = enumeration("NodeFormat", NodeData.SchematicFormat::class)
val schemData = blob("SchemData")
@@ -20,6 +20,7 @@
package de.steamwar.sql
import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.core.ReferenceOption
import org.jetbrains.exposed.v1.core.dao.id.EntityID
import org.jetbrains.exposed.v1.core.dao.id.IdTable
import org.jetbrains.exposed.v1.core.eq
@@ -32,8 +33,8 @@ import java.sql.Timestamp
import java.time.Instant
object NodeDownloadTable: IdTable<Int>("NodeDownload") {
override val id = reference("NodeId", SchematicNodeTable).uniqueIndex()
val link = varchar("Link", 255)
override val id = reference("NodeId", SchematicNodeTable, onDelete = ReferenceOption.CASCADE).uniqueIndex()
val link = varchar("Link", 255).uniqueIndex()
val timestamp = timestamp("Timestamp").defaultExpression(CurrentTimestamp)
}
@@ -20,6 +20,7 @@
package de.steamwar.sql
import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.core.ReferenceOption
import org.jetbrains.exposed.v1.core.and
import org.jetbrains.exposed.v1.core.dao.id.CompositeID
import org.jetbrains.exposed.v1.core.dao.id.CompositeIdTable
@@ -32,9 +33,9 @@ import java.util.*
import kotlin.jvm.optionals.getOrNull
object NodeMemberTable : CompositeIdTable("NodeMember") {
val node = reference("NodeId", SchematicNodeTable)
val userId = reference("UserId", SteamwarUserTable)
val parentNode = optReference("ParentId", SchematicNodeTable)
val node = reference("NodeId", SchematicNodeTable, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE).index()
val userId = reference("UserId", SteamwarUserTable).index()
val parentNode = optReference("ParentId", SchematicNodeTable).index()
override val primaryKey = PrimaryKey(node, userId)
@@ -41,6 +41,7 @@ object PersonalKitTable: CompositeIdTable("PersonalKit") {
init {
addIdColumn(userId)
index(false, userId, gamemode)
}
}
@@ -34,12 +34,16 @@ import java.util.function.Consumer
object PunishmentTable : IntIdTable("Punishments", "PunishmentId") {
val userId = reference("UserId", SteamwarUserTable)
val punisher = reference("Punisher", SteamwarUserTable)
val punisher = reference("Punisher", SteamwarUserTable).index()
val type = enumerationByName("Type", 32, Punishment.PunishmentType::class)
val startTime = timestamp("StartTime")
val endTime = timestamp("EndTime")
val perma = bool("Perma")
val reason = text("Reason")
init {
index(false, userId, type)
}
}
class Punishment(id: EntityID<Int>) : IntEntity(id) {
@@ -30,7 +30,7 @@ import org.jetbrains.exposed.v1.dao.CompositeEntityClass
object RefereeTable: CompositeIdTable("Referee") {
val eventId = reference("EventId", EventTable)
val userId = reference("UserId", SteamwarUserTable)
val userId = reference("UserId", SteamwarUserTable).index()
override val primaryKey = PrimaryKey(eventId, userId)
@@ -34,13 +34,17 @@ import java.util.*
import java.util.function.Consumer
object SchematicNodeTable : IntIdTable("SchematicNode", "NodeId") {
val owner = reference("NodeOwner", SteamwarUserTable)
val owner = reference("NodeOwner", SteamwarUserTable).index()
val name = varchar("NodeName", 64)
val parent = optReference("ParentNode", SchematicNodeTable)
val parent = optReference("ParentNode", SchematicNodeTable).index()
val lastUpdate = timestamp("LastUpdate").defaultExpression(CurrentTimestamp)
val item = text("NodeItem")
val type = varchar("NodeType", 16).nullable()
val type = varchar("NodeType", 16).nullable().index()
val config = integer("Config")
init {
uniqueIndex(parent, owner, name)
}
}
class SchematicNode(id: EntityID<Int>) : IntEntity(id) {
+5 -1
View File
@@ -28,9 +28,13 @@ import org.jetbrains.exposed.v1.dao.IntEntity
import org.jetbrains.exposed.v1.dao.IntEntityClass
object ScriptTable: IntIdTable("Script") {
val userId = reference("UserId", SteamwarUserTable)
val userId = reference("UserId", SteamwarUserTable).index()
val name = varchar("Name", 64)
val code = text("Code")
init {
uniqueIndex(userId, name)
}
}
class Script(id: EntityID<Int>) : IntEntity(id) {
@@ -28,7 +28,7 @@ import org.jetbrains.exposed.v1.jdbc.insert
import java.sql.Timestamp
object SessionTable: Table("Session") {
val userId = reference("UserId", SteamwarUserTable)
val userId = reference("UserId", SteamwarUserTable).index()
val startTime = timestamp("StartTime")
val endTime = timestamp("EndTime").defaultExpression(CurrentTimestamp)
}
@@ -37,15 +37,15 @@ import javax.crypto.SecretKeyFactory
import javax.crypto.spec.PBEKeySpec
object SteamwarUserTable : IntIdTable("UserData", "id") {
val uuid = varchar("UUID", 36)
val username = varchar("UserName", 32)
val team = reference("Team", TeamTable)
val uuid = varchar("UUID", 36).uniqueIndex()
val username = varchar("UserName", 32).index()
val team = reference("Team", TeamTable).index()
val leader = bool("Leader")
val locale = varchar("Locale", 16).nullable()
val manualLocale = bool("ManualLocale")
val bedrock = bool("Bedrock")
val password = text("Password").nullable()
val discordId = long("DiscordId").nullable()
val discordId = long("DiscordId").nullable().uniqueIndex()
}
class SteamwarUser(id: EntityID<Int>): IntEntity(id) {
+2 -2
View File
@@ -28,9 +28,9 @@ import org.jetbrains.exposed.v1.dao.IntEntityClass
import org.jetbrains.exposed.v1.jdbc.select
object TeamTable : IntIdTable("Team", "TeamID") {
val kuerzel = varchar("TeamKuerzel", 10)
val kuerzel = varchar("TeamKuerzel", 10).index()
val color = char("TeamColor", 1).default("8")
val name = varchar("TeamName", 16)
val name = varchar("TeamName", 16).index()
val deleted = bool("TeamDeleted").default(false)
}
@@ -32,8 +32,8 @@ import org.jetbrains.exposed.v1.jdbc.deleteWhere
import org.jetbrains.exposed.v1.jdbc.insertIgnore
object TeamTeilnahmeTable : CompositeIdTable("TeamTeilnahme") {
val teamId = reference("teamId", TeamTable)
val eventId = reference("eventId", EventTable)
val teamId = reference("teamId", TeamTable).index()
val eventId = reference("eventId", EventTable).index()
val placement = integer("Placement").nullable()
override val primaryKey = PrimaryKey(teamId, eventId)
+3 -3
View File
@@ -33,10 +33,10 @@ import java.sql.Timestamp
import java.util.*
object TokenTable: IntIdTable("Token") {
val name = varchar("Name", 64)
val owner = reference("Owner", SteamwarUserTable)
val name = varchar("Name", 64).uniqueIndex()
val owner = reference("Owner", SteamwarUserTable).index()
val created = timestamp("Created").defaultExpression(CurrentTimestamp)
val hash = varchar("Hash", 88)
val hash = varchar("Hash", 88).uniqueIndex()
}
class Token(id: EntityID<Int>): IntEntity(id) {
@@ -28,7 +28,7 @@ import org.jetbrains.exposed.v1.jdbc.insert
import org.jetbrains.exposed.v1.jdbc.selectAll
object UserPermTable: Table("UserPerm") {
val user = reference("User", SteamwarUserTable.id)
val user = reference("User", SteamwarUserTable.id).index()
val perm = enumerationByName("Perm", 32, UserPerm::class)
override val primaryKey = PrimaryKey(user, perm)
@@ -67,7 +67,7 @@ object KotlinDatabase {
}
}
fun <T: Any?> useDb(statement: JdbcTransaction.() -> T): T {
fun <T> useDb(statement: JdbcTransaction.() -> T): T {
KotlinDatabase.ensureConnected()
return TransactionManager.currentOrNull()?.statement() ?: transaction(KotlinDatabase.db) {
statement()
@@ -98,10 +98,12 @@ public class FightSystem extends JavaPlugin {
new StateDependentListener(ArenaMode.All, FightState.All, BountifulWrapper.impl.newDenyArrowPickupListener());
new OneShotStateDependent(ArenaMode.All, FightState.PreSchemSetup, () -> Fight.playSound(SWSound.BLOCK_NOTE_PLING.getSound(), 100.0f, 2.0f));
new OneShotStateDependent(ArenaMode.Test, FightState.All, WorldEditRendererCUIEditor::new);
try {
Bukkit.getWorlds().get(0).setGameRule(GameRule.REDUCED_DEBUG_INFO, ArenaMode.AntiTest.contains(Config.mode));
} catch (Exception e) {
// Ignore if failed!
if (Core.getVersion() >= 19) {
try {
Bukkit.getWorlds().get(0).setGameRule(GameRule.REDUCED_DEBUG_INFO, ArenaMode.AntiTest.contains(Config.mode));
} catch (Exception e) {
// Ignore if failed!
}
}
techHider = new TechHiderWrapper();
@@ -181,7 +181,12 @@ public class Permanent implements Listener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onExplosion(EntityExplodeEvent e) {
if (!(e.getEntity() instanceof TNTPrimed)) return;
if (!(e.getEntity() instanceof TNTPrimed)) {
if (Config.GameModeConfig.Schematic.Type.toDB().equals("wargearseason26")) {
e.blockList().clear();
}
return;
}
if (!Config.GameModeConfig.Arena.WaterDamage) return;
e.blockList().removeIf(block -> {
if(block.getType() == Material.TNT) {
@@ -316,8 +316,18 @@ public class CheckCommand extends SWCommand {
SchematicNode node = SchematicNode.createSchematic(-1, name, teamFolder.getNodeId());
NodeData.saveFromStream(node, data.schemData(false), data.getNodeFormat());
// Accept the team folder schematic and set other to Normal
// Accept the team folder schematic and set other to Normal as well as adding the original owner on the schematic
node.setSchemtype(GameModeConfig.getBySchematicType(schematic.getSchemtype()).Schematic.Type);
NodeMember.createNodeMember(node.getNodeId(), schematic.getOwner());
// Remove any added players from the schematic in the folder
for (SchematicNode schematicNode : SchematicNode.getSchematicNodeInNode(teamFolder.getNodeId())) {
if (schematicNode.getNodeId() == node.getNodeId()) continue;
for (NodeMember nodeMember : NodeMember.getNodeMembers(schematicNode.getNodeId())) {
NodeMember.createNodeMember(node.getNodeId(), nodeMember.getMember());
nodeMember.delete();
}
}
// Conclude by setting send in schematic to normal and broadcast
concludeCheckSession("freigegeben", SchematicType.Normal, () -> {
@@ -50,7 +50,7 @@ public class WhoisCommand extends SWCommand {
@Register(description = "WHOIS_USAGE")
public void whois(Chatter sender, long id, WhoisParameterTypes... parameters) {
if(!sender.user().hasPerm(UserPerm.ADMINISTRATION)) {
if(!sender.user().hasPerm(UserPerm.ADMINISTRATION) && !sender.user().hasPerm(UserPerm.PREFIX_DEVELOPER)) {
sender.system("UNKNOWN_PLAYER");
return;
}
+2
View File
@@ -183,6 +183,8 @@ include(
include("CommandFramework")
include("CLI")
include(
"CommonCore",
"CommonCore:Data",
+2
View File
@@ -33,4 +33,6 @@ artifacts:
"/jars/website-api.jar": "WebsiteBackend/build/libs/WebsiteBackend-all.jar"
release:
- "rm -rf /jars/sw"
- "unzip -o CLI/build/distributions/sw.zip -d /jars"
- "sudo systemctl restart api.service"