Compare commits

..
Author SHA1 Message Date
Chaoscaot 1cb2e70ede Revert "Remove deprecated world management commands and associated SQL logic"
This reverts commit a7adfe37

Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-31 20:21:30 +02:00
Chaoscaot 8ce25ec19f Merge branch 'main' into world-system 2026-05-31 20:16:17 +02:00
Chaoscaot a7adfe378f Remove deprecated world management commands and associated SQL logic
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-20 16:44:32 +02:00
Chaoscaot 9aa363de6f Add world-based permissions and migration commands
- Switch bau logic from owner-scoped to world-scoped data
- Add CLI commands for world migration, templating, and archiving
- Extend world storage with team worlds and lock state support
2026-05-19 20:53:32 +02:00
Chaoscaot 5a5090c74d Remove unused import and fix incorrect import path in BuilderCloudCommand and World classes.
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-19 19:36:43 +02:00
Chaoscaot 99ca7205fb Merge branch 'main' into world-system
# Conflicts:
#	VelocityCore/src/de/steamwar/velocitycore/ServerStarter.java
#	VelocityCore/src/de/steamwar/velocitycore/commands/BauCommand.java
#	VelocityCore/src/de/steamwar/velocitycore/commands/BuilderCloudCommand.java
2026-05-19 19:30:30 +02:00
Chaoscaot e85604fdc4 Merge branch 'main' into world-system 2026-05-16 14:49:18 +02:00
Chaoscaot 936ee38503 Migrate builder and bau worlds to shared world records
- add SQL-backed world entities with archive and rename support
- route builder, bau, deploy, and GDPR flows through world storage
- keep legacy folder support for existing worlds
2026-05-09 16:38:13 +02:00
238 changed files with 4112 additions and 8153 deletions
-1
View File
@@ -51,7 +51,6 @@ jobs:
rm -rf deploy rm -rf deploy
mkdir -p deploy mkdir -p deploy
cp "AccessWidener/build/libs/AccessWidener-all.jar" "deploy/AccessWidener.jar"
cp "BauSystem/build/libs/BauSystem-all.jar" "deploy/BauSystem.jar" cp "BauSystem/build/libs/BauSystem-all.jar" "deploy/BauSystem.jar"
cp "FightSystem/build/libs/FightSystem-all.jar" "deploy/FightSystem.jar" cp "FightSystem/build/libs/FightSystem-all.jar" "deploy/FightSystem.jar"
cp "KotlinCore/build/libs/KotlinCore-all.jar" "deploy/KotlinCore.jar" cp "KotlinCore/build/libs/KotlinCore-all.jar" "deploy/KotlinCore.jar"
-5
View File
@@ -15,11 +15,6 @@ bin/
.vscode .vscode
.settings .settings
# Language Server
**/.project
**/.factorypath
**/.classpath
# Other # Other
lib lib
/WebsiteBackend/data /WebsiteBackend/data
-45
View File
@@ -1,45 +0,0 @@
/*
* 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/>.
*/
plugins {
`java-library`
alias(libs.plugins.shadow)
}
dependencies {
implementation("org.ow2.asm:asm:9.7")
implementation("org.ow2.asm:asm-commons:9.7")
}
tasks.shadowJar {
manifest {
attributes(
"Manifest-Version" to "1.0",
"Build-Jdk-Spec" to "21",
"Main-Class" to "de.steamwar.Main",
"Premain-Class" to "de.steamwar.Agent",
"Can-Retransform-Classes" to "true",
"Can-Redefine-Classes" to "true",
)
}
}
tasks.build {
dependsOn(tasks.shadowJar)
}
@@ -1,49 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 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;
/**
* A single parsed line from a .accesswidener file.
* <p>
* Examples:
* accessible class net/minecraft/server/level/ServerPlayer
* accessible method net/minecraft/server/level/ServerPlayer getStats ()V
* mutable field net/minecraft/world/entity/Entity id I
* extendable class net/minecraft/world/level/chunk/LevelChunk
*/
public record AccessWidenerEntry(
/** accessible | mutable | extendable (may have "transitive-" prefix) */
String directive,
/** class | method | field */
String memberType,
/** Internal class name, e.g. net/minecraft/server/level/ServerPlayer */
String target,
/** Method/field name, null for class entries */
String name,
/** Descriptor, null for class entries */
String descriptor) {
/**
* Returns true if this entry targets the class with the given internal name.
*/
public boolean targets(String internalName) {
return target.equals(internalName);
}
}
@@ -1,104 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 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;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
/**
* Parses Fabric-compatible .accesswidener files.
* <p>
* Supported format:
* <pre>
* accessWidener v2 named
*
* # comments are supported
* accessible class net/minecraft/Foo
* accessible method net/minecraft/Foo someMethod ()V
* accessible field net/minecraft/Foo someField I
* mutable field net/minecraft/Foo someField I
* extendable class net/minecraft/Foo
* extendable method net/minecraft/Foo someMethod ()V
*
* # transitive variants (expose widening to dependents)
* transitive-accessible class net/minecraft/Foo
* </pre>
*/
public final class AccessWidenerParser {
private AccessWidenerParser() {
}
public static List<AccessWidenerEntry> parse(InputStream in) throws IOException {
List<AccessWidenerEntry> entries = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) {
String line;
boolean headerSeen = false;
while ((line = reader.readLine()) != null) {
// Strip inline comments
int commentIdx = line.indexOf('#');
if (commentIdx >= 0) line = line.substring(0, commentIdx);
line = line.strip();
if (line.isEmpty()) continue;
if (!headerSeen) {
// First non-blank, non-comment line must be the header
if (!line.startsWith("accessWidener")) {
throw new IOException("Missing accessWidener header, got: " + line);
}
headerSeen = true;
continue;
}
AccessWidenerEntry entry = parseLine(line);
if (entry != null) entries.add(entry);
}
}
return entries;
}
private static AccessWidenerEntry parseLine(String line) {
String[] parts = line.split("\\s+");
if (parts.length < 3) return null;
String directive = parts[0]; // accessible / mutable / extendable / transitive-*
String memberType = parts[1]; // class / method / field
String target = parts[2]; // internal class name
return switch (memberType) {
case "class" -> new AccessWidenerEntry(directive, "class", target, null, null);
case "method", "field" -> {
if (parts.length < 5) yield null;
yield new AccessWidenerEntry(directive, memberType, target, parts[3], parts[4]);
}
default -> null;
};
}
}
@@ -1,75 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 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;
import java.io.File;
import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
/**
* Java agent entry point.
* <p>
* At JVM startup: java -javaagent:paper-access-widener-agent.jar -jar server.jar
* <p>
* On attach the agent:
* <ol>
* <li>Find all .jar files inside the plugins folder</li>
* <li>Scan all found jars for *.accesswidener resources</li>
* <li>Transform any class during loading</li>
* </ol>
*/
public class Agent {
private Agent() {
throw new IllegalStateException("Utility class");
}
private static final Logger LOG = Logger.getLogger("AccessWidenerAgent");
// -javaagent: startup
public static void premain(String args, Instrumentation inst) {
init(inst);
}
private static void init(Instrumentation inst) {
LOG.info("[AccessWidener] Agent initialising.");
List<AccessWidenerEntry> entries = new ArrayList<>();
File file = new File(new File(".").getAbsoluteFile(), "plugins/");
File[] files = file.listFiles();
if (files == null) files = new File[0];
for (File jarFile : files) {
if (!jarFile.isFile()) continue;
if (!jarFile.getName().endsWith(".jar")) continue;
try {
entries.addAll(Utils.findAndParseAccessWideners(jarFile.toPath()));
} catch (IOException e) {
LOG.warning("Failed to parse access wideners from " + jarFile.getAbsolutePath());
}
}
LOG.info("[AccessWidener] Loaded " + entries.size() + " access wideners.");
inst.addTransformer(new WideningTransformer(entries), false);
LOG.info("[AccessWidener] Agent ready.");
}
}
@@ -1,83 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 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;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Uses ASM to patch class bytecode according to a list of access widener entries.
*
* Returns {@code null} if the class is not targeted by any entry (no-op signal
* to the caller so it can skip the write).
*/
public class ClassPatcher {
private static final Logger LOG = Logger.getLogger("ClassPatcher");
private final List<AccessWidenerEntry> entries;
/** Pre-computed set of targeted internal names for fast filtering. */
private final Set<String> targets;
private final Set<String> targetsPublicConstructor;
public ClassPatcher(List<AccessWidenerEntry> entries) {
this.entries = entries;
this.targets = entries.stream()
.map(AccessWidenerEntry::target)
.flatMap(s -> {
if (!s.contains("$")) return Stream.of(s);
int index = s.lastIndexOf('$');
return Stream.of(s, s.substring(0, index));
})
.collect(Collectors.toSet());
this.targetsPublicConstructor = entries.stream()
.filter(entry -> entry.directive().equals("transitive-extendable"))
.map(AccessWidenerEntry::target)
.collect(Collectors.toSet());
}
/**
* Patches {@code classBytes} if {@code className} is targeted.
*
* @return patched bytes, or {@code null} if no changes were needed
*/
public byte[] patch(String className, byte[] classBytes) {
if (!targets.contains(className)) return null;
try {
ClassReader cr = new ClassReader(classBytes);
ClassWriter cw = new ClassWriter(cr, 0);
cr.accept(new ClassTransformer(cw, className, entries, targetsPublicConstructor.contains(className)), ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
return cw.toByteArray();
} catch (Exception e) {
LOG.warning("[AccessWidener] Failed to transform " + className + ": " + e.getMessage());
return null;
}
}
}
@@ -1,125 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 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;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.util.List;
public class ClassTransformer extends ClassVisitor {
private final String internalName;
private final List<AccessWidenerEntry> entries;
private final boolean appendPublicConstructor;
public ClassTransformer(ClassVisitor cv, String internalName, List<AccessWidenerEntry> entries, boolean appendPublicConstructor) {
super(Opcodes.ASM9, cv);
this.internalName = internalName;
this.entries = entries;
this.appendPublicConstructor = appendPublicConstructor;
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
int newAccess = access;
for (AccessWidenerEntry e : entries) {
if (!e.targets(internalName) || !"class".equals(e.memberType())) continue;
newAccess = applyDirective(e.directive(), newAccess, false);
}
if (appendPublicConstructor) {
MethodVisitor methodVisitor = visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
methodVisitor.visitCode();
methodVisitor.visitVarInsn(Opcodes.ALOAD, 0);
methodVisitor.visitMethodInsn(
Opcodes.INVOKESPECIAL,
"java/lang/Object",
"<init>",
"()V",
false
);
methodVisitor.visitInsn(Opcodes.RETURN);
methodVisitor.visitMaxs(1, 1);
methodVisitor.visitEnd();
}
super.visit(version, newAccess, name, signature, superName, interfaces);
}
@Override
public void visitInnerClass(String name, String outerName, String innerName, int access) {
int newAccess = access;
for (AccessWidenerEntry e : entries) {
if (!e.target().equals(name) || !"class".equals(e.memberType())) continue;
newAccess = applyDirective(e.directive(), newAccess, false);
}
super.visitInnerClass(name, outerName, innerName, newAccess);
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
int newAccess = access;
for (AccessWidenerEntry e : entries) {
if (!e.targets(internalName) || !"method".equals(e.memberType())) continue;
if (!name.equals(e.name()) || !descriptor.equals(e.descriptor())) continue;
newAccess = applyDirective(e.directive(), newAccess, false);
}
return super.visitMethod(newAccess, name, descriptor, signature, exceptions);
}
@Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
int newAccess = access;
for (AccessWidenerEntry e : entries) {
if (!e.targets(internalName) || !"field".equals(e.memberType())) continue;
if (!name.equals(e.name())) continue;
newAccess = applyDirective(e.directive(), newAccess, true);
}
return super.visitField(newAccess, name, descriptor, signature, value);
}
/**
* Apply a directive to an access bitmask.
*
* @param directive accessible / mutable / extendable (with optional "transitive-" prefix)
* @param access current access flags
* @param isField true when processing a field (mutable removes final)
*/
private static int applyDirective(String directive, int access, boolean isField) {
// Strip transitive- prefix — the widening itself is the same
String effective = directive.startsWith("transitive-") ? directive.substring("transitive-".length()) : directive;
return switch (effective) {
case "accessible" -> makePublic(access);
case "extendable" -> makePublic(removeFinal(access));
case "mutable" -> isField ? removeFinal(access) : access;
default -> access;
};
}
private static int makePublic(int access) {
return (access & ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED)) | Opcodes.ACC_PUBLIC;
}
private static int removeFinal(int access) {
return access & ~Opcodes.ACC_FINAL;
}
}
@@ -1,125 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 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;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.List;
/**
* Command-line tool that produces a widened copy of a JAR for use as a
* compile-time stub in IntelliJ / Gradle.
*
* Usage:
* java -jar jar-widener.jar <input.jar> <output.jar> [file.accesswidener ...]
*
* The output JAR is identical to the input JAR except that every class
* targeted by the access widener entries has its access flags patched:
* accessible → public
* extendable → public + non-final
* mutable → non-final field
*
* Intended for use as a Gradle task so IntelliJ sees the already-widened
* class when you Ctrl+click NMS code, and javac compiles without complaints.
*/
public class Main {
public static void main(String[] args) throws Exception {
if (args.length < 2) {
System.err.println("Usage: jar-widener <input.jar> <output.jar> [*.accesswidener ...]");
System.exit(1);
}
Path inputJar = Path.of(args[0]);
Path outputJar = Path.of(args[1]);
if (!Files.exists(inputJar)) {
System.err.println("Input JAR not found: " + inputJar);
System.exit(1);
}
// --- Collect all access widener entries ---
List<AccessWidenerEntry> entries = new ArrayList<>();
if (args.length > 2) {
for (int i = 2; i < args.length; i++) {
Path awFile = Path.of(args[i]);
if (!Files.exists(awFile)) {
System.err.println("Warning: access widener file not found, skipping: " + awFile);
continue;
}
try (InputStream in = Files.newInputStream(awFile)) {
List<AccessWidenerEntry> parsed = AccessWidenerParser.parse(in);
System.out.println("Loaded " + parsed.size() + " entries from " + awFile.getFileName());
entries.addAll(parsed);
}
}
}
if (entries.isEmpty()) {
System.out.println("No access widener entries found — copying JAR unchanged.");
Files.createDirectories(outputJar.getParent());
Files.copy(inputJar, outputJar, StandardCopyOption.REPLACE_EXISTING);
return;
}
System.out.println("Widening " + inputJar.getFileName()
+ " with " + entries.size() + " total entr"
+ (entries.size() == 1 ? "y" : "ies") + "...");
// --- Copy input → output, transforming .class files in place ---
Files.createDirectories(outputJar.getParent());
Files.copy(inputJar, outputJar, StandardCopyOption.REPLACE_EXISTING);
ClassPatcher patcher = new ClassPatcher(entries);
try (FileSystem fs = FileSystems.newFileSystem(outputJar)) {
// Walk every .class entry in the JAR
try (var stream = Files.walk(fs.getPath("/"))) {
stream.filter(p -> p.toString().endsWith(".class"))
.forEach(classPath -> patchClass(fs, classPath, patcher));
}
}
System.out.println("Done. Widened JAR written to " + outputJar);
}
private static void patchClass(FileSystem fs, Path classPath, ClassPatcher patcher) {
// Derive internal class name from path e.g. /net/minecraft/Foo.class → net/minecraft/Foo
String internalName = classPath.toString()
.replaceFirst("^/", "")
.replace(".class", "");
try {
byte[] original = Files.readAllBytes(classPath);
byte[] patched = patcher.patch(internalName, original);
if (patched != null) {
Files.write(classPath, patched);
System.out.println(" Widened: " + internalName);
}
} catch (IOException e) {
System.err.println(" Warning: failed to patch " + internalName + ": " + e.getMessage());
}
}
}
@@ -1,60 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 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;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class Utils {
private static final Logger LOG = Logger.getLogger("AccessWidenerAgent");
private Utils() {
throw new IllegalStateException("Utility class");
}
public static List<AccessWidenerEntry> findAndParseAccessWideners(Path jarPath) throws IOException {
List<AccessWidenerEntry> results = new ArrayList<>();
try (ZipFile zip = new ZipFile(jarPath.toFile())) {
Enumeration<? extends ZipEntry> entries = zip.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
if (entry.isDirectory() || !entry.getName().endsWith(".accesswidener")) continue;
try (InputStream in = zip.getInputStream(entry)) {
results.addAll(AccessWidenerParser.parse(in));
} catch (IOException e) {
LOG.warning("[AccessWidener] Failed to parse " + entry.getName()
+ " in " + jarPath.getFileName() + ": " + e.getMessage());
}
}
}
return results;
}
}
@@ -1,44 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 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;
import java.lang.instrument.ClassFileTransformer;
import java.security.ProtectionDomain;
import java.util.List;
/**
* Transforms class bytecode to apply access widening rules.
* <p>
* Also monitors for new plugin ClassLoaders appearing (when plugins load after
* the agent attaches) and automatically picks up their .accesswidener files.
*/
public class WideningTransformer implements ClassFileTransformer {
private final ClassPatcher patcher;
public WideningTransformer(List<AccessWidenerEntry> entries) {
patcher = new ClassPatcher(entries);
}
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
return patcher.patch(className, classfileBuffer);
}
}
@@ -19,7 +19,6 @@
plugins { plugins {
steamwar.java steamwar.java
widener
} }
tasks.compileJava { tasks.compileJava {
@@ -35,7 +34,6 @@ dependencies {
compileOnly(libs.classindex) compileOnly(libs.classindex)
annotationProcessor(libs.classindex) annotationProcessor(libs.classindex)
compileOnly(project(":SpigotCore", "default")) compileOnly(project(":SpigotCore", "default"))
compileOnly(project(":KotlinCore", "default"))
compileOnly(libs.axiom) compileOnly(libs.axiom)
compileOnly(libs.authlib) compileOnly(libs.authlib)
@@ -49,7 +47,3 @@ dependencies {
implementation(libs.luaj) implementation(libs.luaj)
implementation(files("$projectDir/../libs/YAPION-SNAPSHOT.jar")) implementation(files("$projectDir/../libs/YAPION-SNAPSHOT.jar"))
} }
widener {
fromCatalog(libs.nms)
}
@@ -834,10 +834,6 @@ SKIN_NO_REGION = §7You are not in a region with a changealbe skin
SKIN_ALREADY_EXISTS = §cThis skin already exists like this SKIN_ALREADY_EXISTS = §cThis skin already exists like this
SKIN_MESSAGE = §7Skin created SKIN_MESSAGE = §7Skin created
SKIN_MESSAGE_HOVER = §eClick to copy for YoyoNow and send SKIN_MESSAGE_HOVER = §eClick to copy for YoyoNow and send
# Blast Resistance
BLASTRESISTANCE_HELP = §8/§eblastresistance §8-§7 Calculate min/max and average blast resistance of current clipboard
BLASTRESISTANCE_NO_CLIPBOARD = §cYou currently do not have a clipboard to be used.
BLASTRESISTANCE_RESULT = §7BlastResistance §8>>§7 Min§8: §e{0}§7 Max§8: §e{1}§7 Avg§8: §e{2}
# Panzern # Panzern
PANZERN_HELP = §8/§epanzern §8[§7Block§8] §8[§7Slab§8] §8- §7Armor your WorldEdit selection PANZERN_HELP = §8/§epanzern §8[§7Block§8] §8[§7Slab§8] §8- §7Armor your WorldEdit selection
PANZERN_PREPARE1 = §71. Check, if barrels reach until border of armor. PANZERN_PREPARE1 = §71. Check, if barrels reach until border of armor.
@@ -772,10 +772,6 @@ SKIN_NO_REGION = §7Du steht in keiner Region, welche mit einem Skin versehen we
SKIN_ALREADY_EXISTS = §cDieser Skin existiert in der Form bereits SKIN_ALREADY_EXISTS = §cDieser Skin existiert in der Form bereits
SKIN_MESSAGE = §7Skin erstellt SKIN_MESSAGE = §7Skin erstellt
SKIN_MESSAGE_HOVER = §eKlicken zum kopieren für YoyoNow und an diesen senden SKIN_MESSAGE_HOVER = §eKlicken zum kopieren für YoyoNow und an diesen senden
# Blast Resistance
BLASTRESISTANCE_HELP = §8/§eblastresistance §8-§7 Minimal-, Maximal- und durchschnittliche Sprengfestigkeit des aktuellen Inhalts der Zwischenablage berechnen
BLASTRESISTANCE_NO_CLIPBOARD = §cDerzeit steht Ihnen keine Zwischenablage zur Verfügung.
BLASTRESISTANCE_RESULT = §7BlastResistance §8>>§7 Min§8: §e{0}§7 Max§8: §e{1}§7 Avg§8: §e{2}
# Panzern # Panzern
PANZERN_HELP = §8/§epanzern §8[§7Block§8] §8[§7Slab§8] §8- §7Panzer deine WorldEdit Auswahl PANZERN_HELP = §8/§epanzern §8[§7Block§8] §8[§7Slab§8] §8- §7Panzer deine WorldEdit Auswahl
PANZERN_PREPARE1 = §71. Gucke nochmal nach, ob Läufe auch bis zur Panzergrenze führen. PANZERN_PREPARE1 = §71. Gucke nochmal nach, ob Läufe auch bis zur Panzergrenze führen.
@@ -1,13 +0,0 @@
accessWidener v2 named
# For NoClipCommand
accessible field net/minecraft/server/level/ServerPlayerGameMode gameModeForPlayer Lnet/minecraft/world/level/GameType;
# For PlaceItemUtils
accessible field org/bukkit/craftbukkit/block/CraftBlockState position Lnet/minecraft/core/BlockPos;
mutable field org/bukkit/craftbukkit/block/CraftBlockState position Lnet/minecraft/core/BlockPos;
accessible field org/bukkit/craftbukkit/block/CraftBlockState world Lorg/bukkit/craftbukkit/CraftWorld;
mutable field org/bukkit/craftbukkit/block/CraftBlockState world Lorg/bukkit/craftbukkit/CraftWorld;
# For TickManager
accessible field net/minecraft/server/ServerTickRateManager remainingSprintTicks J
@@ -32,7 +32,6 @@ import de.steamwar.bausystem.features.tracer.TraceManager;
import de.steamwar.bausystem.features.tracer.TraceRecorder; import de.steamwar.bausystem.features.tracer.TraceRecorder;
import de.steamwar.bausystem.features.world.BauScoreboard; import de.steamwar.bausystem.features.world.BauScoreboard;
import de.steamwar.bausystem.linkage.BauGuiItem; import de.steamwar.bausystem.linkage.BauGuiItem;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.region.RegionSystem; import de.steamwar.bausystem.region.RegionSystem;
import de.steamwar.bausystem.utils.ScoreboardElement; import de.steamwar.bausystem.utils.ScoreboardElement;
import de.steamwar.bausystem.utils.TickManager; import de.steamwar.bausystem.utils.TickManager;
@@ -41,9 +40,8 @@ import de.steamwar.command.AbstractValidator;
import de.steamwar.command.SWCommandUtils; import de.steamwar.command.SWCommandUtils;
import de.steamwar.core.CRIUSleepEvent; import de.steamwar.core.CRIUSleepEvent;
import de.steamwar.core.Core; import de.steamwar.core.Core;
import de.steamwar.core.WorldEditRendererCUIEditor;
import de.steamwar.core.WorldIdentifier; import de.steamwar.core.WorldIdentifier;
import de.steamwar.core.worldedit.SteamEdit;
import de.steamwar.core.worldedit.WorldEditRendererCUIEditor;
import de.steamwar.linkage.AbstractLinker; import de.steamwar.linkage.AbstractLinker;
import de.steamwar.linkage.SpigotLinker; import de.steamwar.linkage.SpigotLinker;
import de.steamwar.message.Message; import de.steamwar.message.Message;
@@ -51,7 +49,6 @@ import de.steamwar.providers.BauServerInfo;
import lombok.Getter; import lombok.Getter;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.GameRule; import org.bukkit.GameRule;
import org.bukkit.Location;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@@ -132,23 +129,16 @@ public class BauSystem extends JavaPlugin implements Listener {
TraceManager.instance.init(); TraceManager.instance.init();
TraceRecorder.instance.init(); TraceRecorder.instance.init();
SteamEdit.init(); new WorldEditRendererCUIEditor();
SteamEdit.ADJACENT_FILTERS = location -> {
Region region = Region.getRegion(location);
Region.Area area;
if (region.getBuildArea().inRegion(location, true)) {
area = region.getBuildArea();
} else if (region.getTestblockArea().inRegion(location, true)) {
area = region.getTestblockArea();
} else {
return SteamEdit.AdjacentFilter.ALWAYS;
}
return (x, y, z) -> area.inRegion(new Location(null, x, y, z), true);
};
Bukkit.getWorlds().get(0).setGameRule(GameRule.SEND_COMMAND_FEEDBACK, false); Bukkit.getWorlds().get(0).setGameRule(GameRule.SEND_COMMAND_FEEDBACK, false);
String identifier = BauServerInfo.getOwnerUser().getUUID().toString().replace("-", ""); String identifier;
if (BauServerInfo.getWorldId() != null) {
identifier = BauServerInfo.getWorldId().toString().replace("-", "");
} else {
identifier = BauServerInfo.getOwnerUser().getUUID().toString().replace("-", "");
}
WorldIdentifier.set("bau/" + Core.getVersion() + "/" + identifier); WorldIdentifier.set("bau/" + Core.getVersion() + "/" + identifier);
} }
@@ -60,10 +60,15 @@ public enum Permission {
} }
public boolean hasPermission(Player member) { public boolean hasPermission(Player member) {
if (SteamwarUser.get(member.getUniqueId()).getId() == BauServer.getInstance().getOwnerID()) { SteamwarUser steamwarUser = SteamwarUser.get(member.getUniqueId());
BauServer server = BauServer.getInstance();
if (!server.isTeamWorld() && steamwarUser.getId() == server.getOwnerID()) {
return this != SPECTATOR; return this != SPECTATOR;
} }
BauweltMember bauweltMember = BauweltMember.getBauMember(BauServer.getInstance().getOwner(), member.getUniqueId()); if (server.isTeamWorld() && steamwarUser.getTeam() == server.getTeamID()) {
return this != SPECTATOR;
}
BauweltMember bauweltMember = BauweltMember.getBauMember(server.getWorldID(), steamwarUser.getId());
if (bauweltMember == null) return this == SPECTATOR; if (bauweltMember == null) return this == SPECTATOR;
return permissionPredicate.test(bauweltMember); return permissionPredicate.test(bauweltMember);
} }
@@ -34,16 +34,41 @@ public class BauServer {
} }
private Integer owner; private Integer owner;
private UUID world;
private Integer team;
public UUID getOwner() { public UUID getOwner() {
return SteamwarUser.byId(getOwnerID()).getUUID(); Integer ownerId = getOwnerID();
return ownerId == null ? null : SteamwarUser.byId(ownerId).getUUID();
} }
public int getOwnerID() { public Integer getOwnerID() {
//Lazy loading to improve startup time of the server in 1.15 //Lazy loading to improve startup time of the server in 1.15
if (owner == null) { if (owner == null) {
owner = BauServerInfo.getOwnerId(); owner = BauServerInfo.getOwnerId();
} }
return owner; return owner;
} }
public boolean hasOwner() {
return getOwnerID() != null;
}
public UUID getWorldID() {
if (world == null) {
world = BauServerInfo.getWorldId();
}
return world;
}
public Integer getTeamID() {
if (team == null) {
team = BauServerInfo.getTeamId();
}
return team;
}
public boolean isTeamWorld() {
return getTeamID() != null;
}
} }
@@ -47,7 +47,7 @@ public class BauInfoBauGuiItem extends BauGuiItem {
@Override @Override
public ItemStack getItem(Player player) { public ItemStack getItem(Player player) {
SWItem itemStack; SWItem itemStack;
if (!player.getName().endsWith("⍇")) { if (!player.getName().endsWith("⍇") && !BauServer.getInstance().isTeamWorld()) {
itemStack = SWItem.getPlayerSkull(SteamwarUser.get(BauServer.getInstance().getOwner()).getUserName()); itemStack = SWItem.getPlayerSkull(SteamwarUser.get(BauServer.getInstance().getOwner()).getUserName());
} else { } else {
itemStack = new SWItem(Material.PLAYER_HEAD, ""); itemStack = new SWItem(Material.PLAYER_HEAD, "");
@@ -47,7 +47,9 @@ public class InfoCommand extends SWCommand {
@Register(description = "BAU_INFO_COMMAND_HELP") @Register(description = "BAU_INFO_COMMAND_HELP")
public void genericCommand(Player p) { public void genericCommand(Player p) {
BauSystem.MESSAGE.send("BAU_INFO_COMMAND_OWNER", p, SteamwarUser.byId(bauServer.getOwnerID()).getUserName()); if (!bauServer.isTeamWorld()) {
BauSystem.MESSAGE.send("BAU_INFO_COMMAND_OWNER", p, SteamwarUser.byId(bauServer.getOwnerID()).getUserName());
}
Region region = Region.getRegion(p.getLocation()); Region region = Region.getRegion(p.getLocation());
for (Flag flag : Flag.getFlags()) { for (Flag flag : Flag.getFlags()) {
if (!region.getRegionData().has(flag).isApplicable()) continue; if (!region.getRegionData().has(flag).isApplicable()) continue;
@@ -58,7 +60,7 @@ public class InfoCommand extends SWCommand {
} }
if (Permission.BUILD.hasPermission(p)) { if (Permission.BUILD.hasPermission(p)) {
List<BauweltMember> members = BauweltMember.getMembers(bauServer.getOwnerID()); List<BauweltMember> members = BauweltMember.getWorldMembers(bauServer.getWorldID());
Map<Permission, List<BauweltMember>> memberByPermission = new HashMap<>(); Map<Permission, List<BauweltMember>> memberByPermission = new HashMap<>();
members.forEach(member -> { members.forEach(member -> {
if (Permission.SUPERVISOR.hasPermission(member)) { if (Permission.SUPERVISOR.hasPermission(member)) {
@@ -1,31 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 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.bausystem.features.experimental;
import de.steamwar.command.SWCommand;
import de.steamwar.linkage.Linked;
@Linked
public class ExperimentalCommand extends SWCommand {
public ExperimentalCommand() {
super("experimental", "experiment");
}
}
@@ -1,135 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 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.bausystem.features.experimental.redstone_engine;
import de.steamwar.bausystem.features.experimental.ExperimentalCommand;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.utils.ScoreboardElement;
import de.steamwar.command.AbstractSWCommand;
import de.steamwar.command.SWCommand;
import de.steamwar.linkage.Linked;
import io.papermc.paper.configuration.WorldConfiguration;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.title.TitlePart;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import java.util.Collection;
import java.util.List;
@AbstractSWCommand.PartOf(ExperimentalCommand.class)
@Linked
public class RedstoneEngine extends SWCommand implements Listener, ScoreboardElement {
public RedstoneEngine() {
super("");
}
private WorldConfiguration.Misc getConfig() {
return ((CraftWorld) Bukkit.getWorlds().get(0)).getHandle().paperConfig().misc;
}
@Register("redstone")
@Register("redstoneengine")
public void setRedstoneEngine(Player player, @StaticValue("alternate_current") String __, WorldConfiguration.Misc.AlternateCurrentUpdateOrder updateOrder) {
WorldConfiguration.Misc misc = getConfig();
misc.redstoneImplementation = WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT;
misc.alternateCurrentUpdateOrder = updateOrder;
broadcastTitle(Bukkit.getOnlinePlayers());
}
@Register("redstone")
@Register("redstoneengine")
public void setRedstoneEngine(Player player, WorldConfiguration.Misc.RedstoneImplementation implementation) {
getConfig().redstoneImplementation = implementation;
broadcastTitle(Bukkit.getOnlinePlayers());
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
WorldConfiguration.Misc misc = getConfig();
if (misc.redstoneImplementation != WorldConfiguration.Misc.RedstoneImplementation.EIGENCRAFT) {
broadcastTitle(List.of(event.getPlayer()));
}
}
private void broadcastTitle(Collection<? extends Player> players) {
WorldConfiguration.Misc misc = getConfig();
Component title = switch (misc.redstoneImplementation) {
case VANILLA -> Component.text("⚠").color(NamedTextColor.RED).append(Component.text(" Redstone: Vanilla ").color(NamedTextColor.WHITE)).append(Component.text("⚠").color(NamedTextColor.RED));
case EIGENCRAFT -> Component.text("Redstone: Eigencraft");
case ALTERNATE_CURRENT -> Component.text("⚠").color(NamedTextColor.RED).append(Component.text(" Redstone: AC ").color(NamedTextColor.WHITE)).append(Component.text("⚠").color(NamedTextColor.RED));
};
Component subtitle;
if (misc.redstoneImplementation != WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) {
subtitle = Component.text().build();
} else {
subtitle = switch (misc.alternateCurrentUpdateOrder) {
case VERTICAL_FIRST_INWARD -> Component.text("Y first inwards"); // Y before XZ
case VERTICAL_FIRST_OUTWARD -> Component.text("Y first outwards"); // XZ before Y
case HORIZONTAL_FIRST_INWARD -> Component.text("XZ first inwards"); // Y before XZ
case HORIZONTAL_FIRST_OUTWARD -> Component.text("XZ first outwards"); // XZ before Y
};
}
players.forEach(player -> {
player.sendTitlePart(TitlePart.TITLE, title);
player.sendTitlePart(TitlePart.SUBTITLE, subtitle);
});
}
@Override
public ScoreboardGroup getGroup() {
return ScoreboardGroup.FOOTER;
}
@Override
public int order() {
return Integer.MAX_VALUE;
}
@Override
public String get(Region region, Player p) {
WorldConfiguration.Misc misc = getConfig();
switch (misc.redstoneImplementation) {
case ALTERNATE_CURRENT:
switch (misc.alternateCurrentUpdateOrder) {
case VERTICAL_FIRST_INWARD:
return "§eRedstone§8: §cAC §8(§7Y in§8)";
case VERTICAL_FIRST_OUTWARD:
return "§eRedstone§8: §cAC §8(§7Y out§8)";
case HORIZONTAL_FIRST_INWARD:
return "§eRedstone§8: §cAC §8(§7XZ in§8)";
case HORIZONTAL_FIRST_OUTWARD:
return "§eRedstone§8: §cAC §8(§7XZ out§8)";
}
return "§eRedstone§8: §cAC";
case EIGENCRAFT:
return null;
case VANILLA:
default:
return "§eRedstone§8: §cVanilla";
}
}
}
@@ -290,7 +290,7 @@ public class KillcheckerVisualizer {
} }
rEntities.get(point).die(); rEntities.get(point).die();
} }
RBlockDisplay entity = new RBlockDisplay(outlinePoints.contains(point) ? outline : inner, point.toLocation(WORLD, 0, 0, 0)); RBlockDisplay entity = new RBlockDisplay(outlinePoints.contains(point) ? outline : inner, point.toLocation(WORLD, 0.5, 0, 0.5));
entity.setBlock(MATERIALS[Math.min(count - 1, MATERIALS.length) - 1].createBlockData()); entity.setBlock(MATERIALS[Math.min(count - 1, MATERIALS.length) - 1].createBlockData());
rEntities.put(point, entity); rEntities.put(point, entity);
if (outlinePoints.contains(point)) outlinePointsCache.add(point); if (outlinePoints.contains(point)) outlinePointsCache.add(point);
@@ -46,7 +46,7 @@ public class ObserverTracerListener implements Listener {
public ObserverTracerListener() { public ObserverTracerListener() {
Bukkit.getScheduler().runTaskTimer(BauSystem.getInstance(), () -> { Bukkit.getScheduler().runTaskTimer(BauSystem.getInstance(), () -> {
SWPlayer.allWithSingleComponent(ObserverTracer.class).forEach(pair -> { SWPlayer.allWithSingleComponent(ObserverTracer.class).forEach(pair -> {
if (pair.getSwPlayer().getPlayer().getGameMode() != GameMode.SPECTATOR) return; if (pair.getPlayer().getGameMode() != GameMode.SPECTATOR) return;
pair.getComponent().show(); pair.getComponent().show();
}); });
}, 15L, 15L); }, 15L, 15L);
@@ -20,6 +20,7 @@
package de.steamwar.bausystem.features.region; package de.steamwar.bausystem.features.region;
import de.steamwar.bausystem.BauSystem; import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission;
import de.steamwar.bausystem.config.BauServer; import de.steamwar.bausystem.config.BauServer;
import de.steamwar.bausystem.region.Region; import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.region.RegionUtils; import de.steamwar.bausystem.region.RegionUtils;
@@ -83,6 +84,9 @@ public class ColorCommand extends SWCommand {
@ClassValidator(value = Player.class, local = true) @ClassValidator(value = Player.class, local = true)
public TypeValidator<Player> validator() { public TypeValidator<Player> validator() {
return (commandSender, player, messageSender) -> { return (commandSender, player, messageSender) -> {
if (bauServer.isTeamWorld()) {
return !messageSender.send(!Permission.SUPERVISOR.hasPermission(player), "NO_PERMISSION");
}
return !messageSender.send(!bauServer.getOwner().equals(player.getUniqueId()), "NO_PERMISSION"); return !messageSender.send(!bauServer.getOwner().equals(player.getUniqueId()), "NO_PERMISSION");
}; };
} }
@@ -68,7 +68,7 @@ public class ResetCommand extends SWCommand {
Region region = regionCheck(p); Region region = regionCheck(p);
if (region == null) return; if (region == null) return;
if (!p.getUniqueId().equals(bauServer.getOwner())) { if (bauServer.hasOwner() && !p.getUniqueId().equals(bauServer.getOwner())) {
if (Punishment.isPunished(SteamwarUser.get(bauServer.getOwner()), Punishment.PunishmentType.NoSchemReceiving, punishment -> BauSystem.MESSAGE.send("REGION_TB_NO_SCHEMRECEIVING", p, punishment.getEndTime()))) { if (Punishment.isPunished(SteamwarUser.get(bauServer.getOwner()), Punishment.PunishmentType.NoSchemReceiving, punishment -> BauSystem.MESSAGE.send("REGION_TB_NO_SCHEMRECEIVING", p, punishment.getEndTime()))) {
return; return;
} }
@@ -91,7 +91,7 @@ public class TestblockCommand extends SWCommand {
} }
} }
if (!p.getUniqueId().equals(bauServer.getOwner())) { if (bauServer.hasOwner() && !p.getUniqueId().equals(bauServer.getOwner())) {
if (Punishment.isPunished(SteamwarUser.get(bauServer.getOwner()), Punishment.PunishmentType.NoSchemReceiving, punishment -> BauSystem.MESSAGE.send("REGION_TB_NO_SCHEMRECEIVING", p, punishment.getEndTime()))) { if (Punishment.isPunished(SteamwarUser.get(bauServer.getOwner()), Punishment.PunishmentType.NoSchemReceiving, punishment -> BauSystem.MESSAGE.send("REGION_TB_NO_SCHEMRECEIVING", p, punishment.getEndTime()))) {
return; return;
} }
@@ -185,16 +185,17 @@ public class TestblockCommand extends SWCommand {
return new TypeMapper<SchematicNode>() { return new TypeMapper<SchematicNode>() {
@Override @Override
public List<String> tabCompletes(CommandSender commandSender, PreviousArguments previousArguments, String s) { public List<String> tabCompletes(CommandSender commandSender, PreviousArguments previousArguments, String s) {
List<String> stringList = new ArrayList<>(); List<String> stringList = new ArrayList<>(SchematicNode.getNodeTabcomplete(SteamwarUser.get(((Player) commandSender).getUniqueId()), s));
stringList.addAll(SchematicNode.getNodeTabcomplete(SteamwarUser.byId(0), s)); stringList.addAll(SchematicNode.getNodeTabcomplete(SteamwarUser.byId(0), s));
stringList.addAll(SchematicNode.getNodeTabcomplete(SteamwarUser.get(((Player) commandSender).getUniqueId()), s));
return stringList; return stringList;
} }
@Override @Override
public SchematicNode map(CommandSender commandSender, PreviousArguments previousArguments, String s) { public SchematicNode map(CommandSender commandSender, PreviousArguments previousArguments, String s) {
SchematicNode node = SchematicNode.getNodeFromPath(SteamwarUser.byId(0), s); SchematicNode node = SchematicNode.getNodeFromPath(SteamwarUser.get(((Player) commandSender).getUniqueId()), s);
if (node == null) node = SchematicNode.getNodeFromPath(SteamwarUser.get(((Player) commandSender).getUniqueId()), s); if (node == null) {
node = SchematicNode.getNodeFromPath(SteamwarUser.byId(0), s);
}
return node; return node;
} }
}; };
@@ -19,7 +19,7 @@
package de.steamwar.bausystem.features.simulator; package de.steamwar.bausystem.features.simulator;
import com.destroystokyo.paper.event.server.ServerTickEndEvent; import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.bausystem.BauSystem; import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission; import de.steamwar.bausystem.Permission;
import de.steamwar.bausystem.SWUtils; import de.steamwar.bausystem.SWUtils;
@@ -38,88 +38,77 @@ import de.steamwar.bausystem.features.simulator.gui.SimulatorGui;
import de.steamwar.bausystem.features.simulator.gui.base.SimulatorBaseGui; import de.steamwar.bausystem.features.simulator.gui.base.SimulatorBaseGui;
import de.steamwar.bausystem.utils.BauMemberUpdateEvent; import de.steamwar.bausystem.utils.BauMemberUpdateEvent;
import de.steamwar.bausystem.utils.ItemUtils; import de.steamwar.bausystem.utils.ItemUtils;
import de.steamwar.core.SWPlayer; import de.steamwar.bausystem.utils.RayTraceUtils;
import de.steamwar.cursor.Cursor;
import de.steamwar.entity.REntity; import de.steamwar.entity.REntity;
import de.steamwar.entity.REntityServer; import de.steamwar.entity.REntityServer;
import de.steamwar.entity.RFallingBlockEntity;
import de.steamwar.inventory.SWAnvilInv; import de.steamwar.inventory.SWAnvilInv;
import de.steamwar.linkage.Linked; import de.steamwar.linkage.Linked;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.block.Action; import org.bukkit.event.block.Action;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.event.player.*; import org.bukkit.event.player.*;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import java.util.*; import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Linked @Linked
public class SimulatorCursor implements Listener { public class SimulatorCursor implements Listener {
private static final Map<Player, CursorType> cursorType = Collections.synchronizedMap(new HashMap<>()); private static final World WORLD = Bukkit.getWorlds().get(0);
private static final Map<Player, REntityServer> emptyTargetServers = Collections.synchronizedMap(new HashMap<>());
private static Map<Player, CursorType> cursorType = Collections.synchronizedMap(new HashMap<>());
private static Map<Player, REntityServer> cursors = Collections.synchronizedMap(new HashMap<>());
private static final Set<Player> calculating = new HashSet<>();
public static boolean isSimulatorItem(ItemStack itemStack) { public static boolean isSimulatorItem(ItemStack itemStack) {
return ItemUtils.isItem(itemStack, "simulator"); return ItemUtils.isItem(itemStack, "simulator");
} }
private static boolean hasSimulatorItem(Player player) { public SimulatorCursor() {
return isSimulatorItem(player.getInventory().getItemInMainHand()) || isSimulatorItem(player.getInventory().getItemInOffHand()); BiFunction<Player, ServerboundMovePlayerPacket, Object> function = (player, object) -> {
} calcCursor(player);
return object;
private static Set<Player> scheduledUpdates = new HashSet<>(); };
TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.Pos.class, function);
private static void scheduleCursorUpdate(Player player) { TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.Rot.class, function);
scheduledUpdates.add(player); TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.PosRot.class, function);
}
@EventHandler
public void onServerTickEnd(ServerTickEndEvent event) {
scheduledUpdates.forEach(SimulatorCursor::calcCursor);
scheduledUpdates.clear();
} }
@EventHandler @EventHandler
public void onPlayerJoin(PlayerJoinEvent event) { public void onPlayerJoin(PlayerJoinEvent event) {
if (!Permission.BUILD.hasPermission(event.getPlayer())) return; if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
scheduleCursorUpdate(event.getPlayer()); Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
calcCursor(event.getPlayer());
}, 0);
} }
@EventHandler @EventHandler
public void onPlayerDropItem(PlayerDropItemEvent event) { public void onPlayerDropItem(PlayerDropItemEvent event) {
if (!Permission.BUILD.hasPermission(event.getPlayer())) return; if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
scheduleCursorUpdate(event.getPlayer()); calcCursor(event.getPlayer());
} }
@EventHandler @EventHandler
public void onPlayerItemHeld(PlayerItemHeldEvent event) { public void onPlayerItemHeld(PlayerItemHeldEvent event) {
if (!Permission.BUILD.hasPermission(event.getPlayer())) return; if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
scheduleCursorUpdate(event.getPlayer()); Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
} calcCursor(event.getPlayer());
}, 1);
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
if (!(event.getWhoClicked() instanceof Player player)) return;
if (!Permission.BUILD.hasPermission(player)) return;
scheduleCursorUpdate(player);
}
@EventHandler
public void onInventoryDrag(InventoryDragEvent event) {
if (!(event.getWhoClicked() instanceof Player player)) return;
if (!Permission.BUILD.hasPermission(player)) return;
scheduleCursorUpdate(player);
} }
@EventHandler @EventHandler
@@ -130,8 +119,10 @@ public class SimulatorCursor implements Listener {
@EventHandler @EventHandler
public void onPlayerQuit(PlayerQuitEvent event) { public void onPlayerQuit(PlayerQuitEvent event) {
cursorType.remove(event.getPlayer()); cursorType.remove(event.getPlayer());
removeCursor(event.getPlayer()); cursors.remove(event.getPlayer());
scheduledUpdates.remove(event.getPlayer()); synchronized (calculating) {
calculating.remove(event.getPlayer());
}
} }
private static final Map<Player, Long> LAST_SNEAKS = new HashMap<>(); private static final Map<Player, Long> LAST_SNEAKS = new HashMap<>();
@@ -147,7 +138,7 @@ public class SimulatorCursor implements Listener {
public void onPlayerToggleSneak(PlayerToggleSneakEvent event) { public void onPlayerToggleSneak(PlayerToggleSneakEvent event) {
if (!event.isSneaking()) return; if (!event.isSneaking()) return;
Player player = event.getPlayer(); Player player = event.getPlayer();
if (!hasSimulatorItem(player)) { if (!isSimulatorItem(player.getInventory().getItemInMainHand()) && !isSimulatorItem(player.getInventory().getItemInOffHand())) {
return; return;
} }
if (LAST_SNEAKS.containsKey(player)) { if (LAST_SNEAKS.containsKey(player)) {
@@ -173,10 +164,17 @@ public class SimulatorCursor implements Listener {
} }
public static void calcCursor(Player player) { public static void calcCursor(Player player) {
if (!Permission.BUILD.hasPermission(player) || !hasSimulatorItem(player)) { synchronized (calculating) {
if (removeCursor(player) | SimulatorWatcher.show(null, player)) { if (calculating.contains(player)) return;
calculating.add(player);
}
if (!Permission.BUILD.hasPermission(player) || (!isSimulatorItem(player.getInventory().getItemInMainHand()) && !isSimulatorItem(player.getInventory().getItemInOffHand()))) {
if (removeCursor(player) || SimulatorWatcher.show(null, player)) {
SWUtils.sendToActionbar(player, ""); SWUtils.sendToActionbar(player, "");
} }
synchronized (calculating) {
calculating.remove(player);
}
return; return;
} }
@@ -185,98 +183,203 @@ public class SimulatorCursor implements Listener {
removeCursor(player); removeCursor(player);
SimulatorWatcher.show(null, player); SimulatorWatcher.show(null, player);
SWUtils.sendToActionbar(player, "§cGenerating Stab"); SWUtils.sendToActionbar(player, "§cGenerating Stab");
synchronized (calculating) {
calculating.remove(player);
}
return; return;
} }
SimulatorWatcher.show(simulator, player); SimulatorWatcher.show(simulator, player);
Cursor cursor = getOrCreateCursor(player, simulator, cursorType.getOrDefault(player, CursorType.TNT)); List<REntity> entities = SimulatorWatcher.getEntitiesOfSimulator(simulator);
cursor.renderDeduplicated(); RayTraceUtils.RRayTraceResult rayTraceResult = RayTraceUtils.traceREntity(player, player.getLocation(), entities);
} if (rayTraceResult == null) {
removeCursor(player);
private static Cursor getOrCreateCursor(Player player, Simulator simulator, CursorType type) { if (simulator == null) {
REntityServer targetServer = simulator == null ? emptyTargetServers.computeIfAbsent(player, __ -> new REntityServer()) : SimulatorWatcher.getEntityServerOfSimulator(simulator); SWUtils.sendToActionbar(player, "§eSelect Simulator");
SWPlayer swPlayer = SWPlayer.of(player); } else {
Optional<Cursor> activeCursor = swPlayer.getComponent(Cursor.class); SWUtils.sendToActionbar(player, "§eOpen Simulator");
}
Cursor cursor = activeCursor.orElse(null); synchronized (calculating) {
if (cursor == null || cursor.getTargetServer() != targetServer) { calculating.remove(player);
swPlayer.removeComponent(Cursor.class); }
cursor = new Cursor( return;
targetServer,
player,
Material.GLASS,
type.material,
type.cursorModes,
(location, hitEntity, action) -> handlePlayerClick(player, location, hitEntity, action),
(location, hitEntity) -> sendCursorActionbar(player, SimulatorStorage.getSimulator(player), location != null, hitEntity.isPresent())
);
} else {
cursor.setCursorMaterial(type.material);
cursor.setAllowedCursorModes(type.cursorModes);
} }
return cursor; showCursor(player, rayTraceResult, simulator != null);
synchronized (calculating) {
calculating.remove(player);
}
} }
private static synchronized boolean removeCursor(Player player) { private static synchronized boolean removeCursor(Player player) {
Optional<Cursor> cursor = SWPlayer.of(player).getComponent(Cursor.class); REntityServer entityServer = cursors.get(player);
cursor.ifPresent(__ -> SWPlayer.of(player).removeComponent(Cursor.class)); boolean hadCursor = entityServer != null && !entityServer.getEntities().isEmpty();
REntityServer emptyTargetServer = emptyTargetServers.remove(player); if (entityServer != null) {
if (emptyTargetServer != null) { entityServer.getEntities().forEach(REntity::die);
emptyTargetServer.close();
} }
return cursor.isPresent(); return hadCursor;
} }
private static void sendCursorActionbar(Player player, Simulator simulator, boolean hasCursorLocation, boolean hasHitEntity) { private static synchronized void showCursor(Player player, RayTraceUtils.RRayTraceResult rayTraceResult, boolean hasSimulatorSelected) {
if (!hasCursorLocation) { REntityServer entityServer = cursors.computeIfAbsent(player, __ -> {
SWUtils.sendToActionbar(player, simulator == null ? "§eSelect Simulator" : "§eOpen Simulator"); REntityServer rEntityServer = new REntityServer();
} else if (simulator == null) { rEntityServer.addPlayer(player);
SWUtils.sendToActionbar(player, "§eCreate new Simulator"); return rEntityServer;
} else if (hasHitEntity) { });
SWUtils.sendToActionbar(player, "§eEdit Position");
} else { CursorType type = cursorType.getOrDefault(player, CursorType.TNT);
SWUtils.sendToActionbar(player, "§eAdd new " + cursorType.getOrDefault(player, CursorType.TNT).name); REntity hitEntity = rayTraceResult.getHitEntity();
Location location = hitEntity != null ? new Vector(hitEntity.getX(), hitEntity.getY(), hitEntity.getZ()).toLocation(WORLD) :
type.position.apply(player, rayTraceResult).toLocation(WORLD);
Material material = hitEntity != null ? Material.GLASS : type.getMaterial();
List<RFallingBlockEntity> entities = entityServer.getEntitiesByType(RFallingBlockEntity.class);
entities.removeIf(rFallingBlockEntity -> {
if (rFallingBlockEntity.getMaterial() != material) {
rFallingBlockEntity.die();
return true;
}
rFallingBlockEntity.move(location);
return false;
});
if (entities.isEmpty()) {
RFallingBlockEntity rFallingBlockEntity = new RFallingBlockEntity(entityServer, location, material);
rFallingBlockEntity.setNoGravity(true);
if (material == Material.GLASS) {
rFallingBlockEntity.setGlowing(true);
}
} }
if (hasSimulatorSelected) {
if (hitEntity != null) {
SWUtils.sendToActionbar(player, "§eEdit Position");
} else {
SWUtils.sendToActionbar(player, "§eAdd new " + type.name);
}
} else {
SWUtils.sendToActionbar(player, "§eCreate new Simulator");
}
}
public static Vector getPosFree(Player player, RayTraceUtils.RRayTraceResult result) {
Vector pos = result.getHitPosition();
BlockFace face = result.getHitBlockFace();
if (face != null) {
switch (face) {
case DOWN:
pos.setY(pos.getY() - 0.98);
break;
case EAST:
pos.setX(pos.getX() + 0.49);
break;
case WEST:
pos.setX(pos.getX() - 0.49);
break;
case NORTH:
pos.setZ(pos.getZ() - 0.49);
break;
case SOUTH:
pos.setZ(pos.getZ() + 0.49);
break;
default:
break;
}
if (face.getModY() == 0 && player.isSneaking()) {
pos.setY(pos.getY() - 0.49);
}
}
if (!player.isSneaking()) {
pos.setX(pos.getBlockX() + 0.5);
if (face == null || face.getModY() == 0) {
pos.setY(pos.getBlockY() + 0.0);
}
pos.setZ(pos.getBlockZ() + 0.5);
}
return pos;
}
private static Vector getPosBlockAligned(Player player, RayTraceUtils.RRayTraceResult result) {
Vector pos = result.getHitPosition();
BlockFace face = result.getHitBlockFace();
if (face != null) {
switch (face) {
case DOWN:
pos.setY(pos.getY() - 0.98);
break;
case EAST:
pos.setX(pos.getX() + 0.49);
break;
case WEST:
pos.setX(pos.getX() - 0.49);
break;
case NORTH:
pos.setZ(pos.getZ() - 0.49);
break;
case SOUTH:
pos.setZ(pos.getZ() + 0.49);
break;
default:
break;
}
}
pos.setX(pos.getBlockX() + 0.5);
if (pos.getY() - pos.getBlockY() != 0 && face == BlockFace.UP) {
pos.setY(pos.getBlockY() + 1.0);
} else {
pos.setY(pos.getBlockY());
}
pos.setZ(pos.getBlockZ() + 0.5);
return pos;
} }
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public enum CursorType { public enum CursorType {
TNT(Material.TNT, Material.GUNPOWDER, List.of(Cursor.CursorMode.FREE, Cursor.CursorMode.SURFACE_ALIGNED), "TNT", vector -> new TNTElement(vector).add(new TNTPhase())), TNT(Material.TNT, Material.GUNPOWDER, SimulatorCursor::getPosFree, "TNT", vector -> new TNTElement(vector).add(new TNTPhase())),
REDSTONE_BLOCK(Material.REDSTONE_BLOCK, Material.REDSTONE, List.of(Cursor.CursorMode.BLOCK_ALIGNED), "Redstone Block", vector -> new RedstoneElement(vector).add(new RedstonePhase())), REDSTONE_BLOCK(Material.REDSTONE_BLOCK, Material.REDSTONE, SimulatorCursor::getPosBlockAligned, "Redstone Block", vector -> new RedstoneElement(vector).add(new RedstonePhase())),
OBSERVER(Material.OBSERVER, Material.QUARTZ, List.of(Cursor.CursorMode.BLOCK_ALIGNED), "Observer", vector -> new ObserverElement(vector).add(new ObserverPhase())), OBSERVER(Material.OBSERVER, Material.QUARTZ, SimulatorCursor::getPosBlockAligned, "Observer", vector -> new ObserverElement(vector).add(new ObserverPhase())),
; ;
public final Material material; public final Material material;
public final Material nonSelectedMaterial; public final Material nonSelectedMaterial;
public final List<Cursor.CursorMode> cursorModes; public final BiFunction<Player, RayTraceUtils.RRayTraceResult, Vector> position;
public final String name; public final String name;
public final Function<Vector, SimulatorElement<?>> elementFunction; public final Function<Vector, SimulatorElement<?>> elementFunction;
} }
private static void handlePlayerClick(Player player, Location cursorLocation, Optional<REntity> hitEntity, Action action) { @EventHandler
if (!Permission.BUILD.hasPermission(player)) return; public void onPlayerInteract(PlayerInteractEvent event) {
if (!hasSimulatorItem(player)) { if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
if (!ItemUtils.isItem(event.getItem(), "simulator")) {
return; return;
} }
event.setCancelled(true);
Player player = event.getPlayer();
Simulator simulator = SimulatorStorage.getSimulator(player); Simulator simulator = SimulatorStorage.getSimulator(player);
if (action == Action.LEFT_CLICK_BLOCK || action == Action.LEFT_CLICK_AIR) { if (event.getAction() == Action.LEFT_CLICK_BLOCK || event.getAction() == Action.LEFT_CLICK_AIR) {
if (simulator == null) { if (simulator == null) {
return; return;
} }
SimulatorExecutor.run(player, simulator, null); SimulatorExecutor.run(event.getPlayer(), simulator, null);
return; return;
} }
if (action != Action.RIGHT_CLICK_BLOCK && action != Action.RIGHT_CLICK_AIR) { if (event.getAction() != Action.RIGHT_CLICK_BLOCK && event.getAction() != Action.RIGHT_CLICK_AIR) {
return; return;
} }
RayTraceUtils.RRayTraceResult rayTraceResult = RayTraceUtils.traceREntity(player, player.getLocation(), SimulatorWatcher.getEntitiesOfSimulator(simulator));
if (simulator == null) { if (simulator == null) {
if (cursorLocation == null) { if (rayTraceResult == null) {
SimulatorStorage.openSimulatorSelector(player); SimulatorStorage.openSimulatorSelector(player);
} else { } else {
SWAnvilInv anvilInv = new SWAnvilInv(player, "Name"); SWAnvilInv anvilInv = new SWAnvilInv(player, "Name");
@@ -292,7 +395,7 @@ public class SimulatorCursor implements Listener {
} }
sim = new Simulator(s); sim = new Simulator(s);
SimulatorStorage.addSimulator(s, sim); SimulatorStorage.addSimulator(s, sim);
createElement(player, cursorLocation, sim); createElement(player, rayTraceResult, sim);
SimulatorStorage.setSimulator(player, sim); SimulatorStorage.setSimulator(player, sim);
}); });
anvilInv.open(); anvilInv.open();
@@ -300,56 +403,57 @@ public class SimulatorCursor implements Listener {
return; return;
} }
if (cursorLocation == null) { if (rayTraceResult == null) {
new SimulatorGui(player, simulator).open(); new SimulatorGui(player, simulator).open();
return; return;
} }
if (hitEntity.isPresent()) { if (rayTraceResult.getHitEntity() != null) {
openElement(player, simulator, hitEntity.get()); REntity hitEntity = rayTraceResult.getHitEntity();
Vector vector = new Vector(hitEntity.getX(), hitEntity.getY(), hitEntity.getZ());
List<SimulatorElement<?>> elements = simulator.getGroups().stream().map(SimulatorGroup::getElements).flatMap(List::stream).filter(element -> {
return element.getWorldPos().distanceSquared(vector) < (1 / 16.0) * (1 / 16.0);
}).collect(Collectors.toList());
switch (elements.size()) {
case 0:
return;
case 1:
// Open single element present in Simulator
SimulatorElement<?> element = elements.get(0);
SimulatorGroup group1 = element.getGroup(simulator);
SimulatorBaseGui back = new SimulatorGui(player, simulator);
if (group1.getElements().size() > 1) {
back = new SimulatorGroupGui(player, simulator, group1, back);
}
element.open(player, simulator, group1, back);
break;
default:
List<SimulatorGroup> parents = elements.stream().map(e -> e.getGroup(simulator)).distinct().collect(Collectors.toList());
if (parents.size() == 1) {
// Open multi element present in Simulator in existing group
SimulatorGui simulatorGui = new SimulatorGui(player, simulator);
new SimulatorGroupGui(player, simulator, parents.get(0), simulatorGui).open();
} else {
// Open multi element present in Simulator in implicit group
SimulatorGroup group2 = new SimulatorGroup();
group2.setMaterial(null);
group2.getElements().addAll(elements);
SimulatorGui simulatorGui = new SimulatorGui(player, simulator);
new SimulatorGroupGui(player, simulator, group2, simulatorGui).open();
}
break;
}
return; return;
} }
createElement(player, cursorLocation, simulator); // Add new Element to current simulator
createElement(player, rayTraceResult, simulator);
} }
private static void openElement(Player player, Simulator simulator, REntity hitEntity) { private void createElement(Player player, RayTraceUtils.RRayTraceResult rayTraceResult, Simulator simulator) {
Vector vector = new Vector(hitEntity.getX(), hitEntity.getY(), hitEntity.getZ());
List<SimulatorElement<?>> elements = simulator.getGroups().stream().map(SimulatorGroup::getElements).flatMap(List::stream).filter(element -> {
return element.getWorldPos().distanceSquared(vector) < (1 / 16.0) * (1 / 16.0);
}).collect(Collectors.toList());
switch (elements.size()) {
case 0:
return;
case 1:
SimulatorElement<?> element = elements.get(0);
SimulatorGroup group1 = element.getGroup(simulator);
SimulatorBaseGui back = new SimulatorGui(player, simulator);
if (group1.getElements().size() > 1) {
back = new SimulatorGroupGui(player, simulator, group1, back);
}
element.open(player, simulator, group1, back);
break;
default:
List<SimulatorGroup> parents = elements.stream().map(e -> e.getGroup(simulator)).distinct().collect(Collectors.toList());
if (parents.size() == 1) {
SimulatorGui simulatorGui = new SimulatorGui(player, simulator);
new SimulatorGroupGui(player, simulator, parents.get(0), simulatorGui).open();
} else {
SimulatorGroup group2 = new SimulatorGroup();
group2.setMaterial(null);
group2.getElements().addAll(elements);
SimulatorGui simulatorGui = new SimulatorGui(player, simulator);
new SimulatorGroupGui(player, simulator, group2, simulatorGui).open();
}
break;
}
}
private static void createElement(Player player, Location cursorLocation, Simulator simulator) {
CursorType type = cursorType.getOrDefault(player, CursorType.TNT); CursorType type = cursorType.getOrDefault(player, CursorType.TNT);
Vector vector = cursorLocation.toVector(); Vector vector = type.position.apply(player, rayTraceResult);
if (type == CursorType.REDSTONE_BLOCK) { if (type == CursorType.REDSTONE_BLOCK) {
vector.subtract(new Vector(0.5, 0, 0.5)); vector.subtract(new Vector(0.5, 0, 0.5));
} }
@@ -124,11 +124,4 @@ public class SimulatorWatcher {
} }
return entityServer.getEntities(); return entityServer.getEntities();
} }
synchronized REntityServer getEntityServerOfSimulator(Simulator simulator) {
if (simulator == null) {
return null;
}
return entityServers.computeIfAbsent(simulator, __ -> createSim(new REntityServer(), simulator));
}
} }
@@ -1,72 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 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.bausystem.features.slaves.blastresistance;
import com.google.common.util.concurrent.AtomicDouble;
import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.bukkit.BukkitAdapter;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.world.block.BlockState;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.command.SWCommand;
import de.steamwar.linkage.Linked;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import java.util.concurrent.atomic.AtomicInteger;
@Linked
public class BlastResistanceCommand extends SWCommand {
public BlastResistanceCommand() {
super("blastresistance");
}
@Register(description = "BLASTRESISTANCE_HELP")
public void command(@Validator Player player) {
LocalSession localSession = WorldEdit.getInstance().getSessionManager().get(BukkitAdapter.adapt(player));
Clipboard clipboard;
try {
clipboard = localSession.getClipboard().getClipboards().getFirst();
} catch (WorldEditException e) {
BauSystem.MESSAGE.send("BLASTRESISTANCE_NO_CLIPBOARD", player);
return;
}
AtomicDouble min = new AtomicDouble(0);
AtomicDouble max = new AtomicDouble(0);
AtomicDouble sum = new AtomicDouble(0);
AtomicInteger count = new AtomicInteger(0);
clipboard.forEach(blockVector3 -> {
BlockState blockState = clipboard.getBlock(blockVector3);
Material material = BukkitAdapter.adapt(blockState).getMaterial();
if (material == Material.WATER || material == Material.LAVA) return;
double blastResistance = BukkitAdapter.adapt(blockState).getMaterial().getBlastResistance();
min.set(Math.min(min.get(), blastResistance));
max.set(Math.max(max.get(), blastResistance));
sum.addAndGet(blastResistance);
count.getAndIncrement();
});
BauSystem.MESSAGE.send("BLASTRESISTANCE_RESULT", player, min.get(), max.get(), sum.get() / count.get());
}
}
@@ -31,7 +31,6 @@ import de.steamwar.command.TypeMapper;
import de.steamwar.core.CraftbukkitWrapper; import de.steamwar.core.CraftbukkitWrapper;
import de.steamwar.linkage.Linked; import de.steamwar.linkage.Linked;
import de.steamwar.linkage.LinkedInstance; import de.steamwar.linkage.LinkedInstance;
import de.steamwar.techhider.legacy.TechHider;
import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.ChatMessageType;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@@ -39,6 +38,7 @@ import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerQuitEvent;
import de.steamwar.techhider.legacy.TechHider;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -165,7 +165,6 @@ public class TPSSystem implements Listener {
} }
@Register(value = "default", description = "TPSLIMIT_DEFAULT_HELP") @Register(value = "default", description = "TPSLIMIT_DEFAULT_HELP")
@Register(value = "rate")
public void reset(@Validator Player player) { public void reset(@Validator Player player) {
TickManager.impl.setTickRate(20.0F); TickManager.impl.setTickRate(20.0F);
sendTickRateChange(); sendTickRateChange();
@@ -188,18 +187,15 @@ public class TPSSystem implements Listener {
} }
@Register(value = {"rate", "0"}, description = "TICK_FREEZE_HELP") @Register(value = {"rate", "0"}, description = "TICK_FREEZE_HELP")
public void rate(@Validator Player player) { @Register(value = "freeze", description = "TICK_FREEZE_HELP_2")
public void freeze(@Validator Player player) {
TickManager.impl.setFreeze(true); TickManager.impl.setFreeze(true);
sendTickRateChange(); sendTickRateChange();
} }
@Register(value = "freeze", description = "TICK_FREEZE_HELP_2") @Register(value = "unfreeze", description = "TICK_UNFREEZE_HELP")
public void freezeToggle(@Validator Player player) { public void unfreeze(@Validator Player player) {
if (TickManager.impl.isFrozen()) { TickManager.impl.setTickRate(20.0F);
TickManager.impl.setTickRate(20.0F);
} else {
TickManager.impl.setFreeze(true);
}
sendTickRateChange(); sendTickRateChange();
} }
} }
@@ -25,6 +25,7 @@ import de.steamwar.bausystem.features.tracer.rendering.TraceEntity;
import de.steamwar.bausystem.features.tracer.rendering.ViewFlag; import de.steamwar.bausystem.features.tracer.rendering.ViewFlag;
import de.steamwar.bausystem.region.Region; import de.steamwar.bausystem.region.Region;
import de.steamwar.entity.REntity; import de.steamwar.entity.REntity;
import de.steamwar.entity.REntityAction;
import de.steamwar.entity.REntityServer; import de.steamwar.entity.REntityServer;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@@ -153,11 +153,7 @@ public class TraceCommand extends SWCommand {
@Register(value = "isolate", description = "TRACE_COMMAND_HELP_ISOLATE") @Register(value = "isolate", description = "TRACE_COMMAND_HELP_ISOLATE")
public void isolate(@Validator Player player, Trace trace, @ErrorMessage("TRACE_RECORD_ID_INVALID") TNTPoint... records) { public void isolate(@Validator Player player, Trace trace, @ErrorMessage("TRACE_RECORD_ID_INVALID") TNTPoint... records) {
if (records.length == 0) { TraceManager.instance.isolate(player, records);
TraceManager.instance.isolate(player, trace, trace.getRecords().toArray(TNTPoint[]::new));
} else {
TraceManager.instance.isolate(player, trace, records);
}
BauSystem.MESSAGE.send("TRACE_MESSAGE_ISOLATE", player); BauSystem.MESSAGE.send("TRACE_MESSAGE_ISOLATE", player);
} }
@@ -347,10 +347,9 @@ public class TraceManager implements Listener {
* Toggles the isolated render for the given records and player * Toggles the isolated render for the given records and player
* *
* @param player the player the trace is shown to * @param player the player the trace is shown to
* @param ptrace the trace for whitch isolation is toggled * @param records the record for which isolation is toggled
* @param records the records of the trace for which isolation is toggled
*/ */
public void isolate(Player player, Trace ptrace, TNTPoint... records) { public void isolate(Player player, TNTPoint... records) {
unfollow(player); unfollow(player);
Region region = Region.getRegion(player.getLocation()); Region region = Region.getRegion(player.getLocation());
@@ -375,20 +374,12 @@ public class TraceManager implements Listener {
isolateFlag.toggleId(record.getTntId()); isolateFlag.toggleId(record.getTntId());
} }
if (isolateFlag.isEmpty() && playerTraceShowData.hasViewFlagOnly(IsolateFlag.class) && records.length != 0) {
playerTraceShowData.removeViewFlag(IsolateFlag.class);
}
PlayerTraceShowData finalPlayerTraceShowData = playerTraceShowData; PlayerTraceShowData finalPlayerTraceShowData = playerTraceShowData;
tracesByRegion.getOrDefault(region, Collections.emptyMap()).forEach((integer, trace) -> { tracesByRegion.getOrDefault(region, Collections.emptyMap()).forEach((integer, trace) -> {
if (trace.getUuid() == ptrace.getUuid() || finalPlayerTraceShowData.hasNoViewFlags()) { trace.render(player, finalPlayerTraceShowData);
trace.render(player, finalPlayerTraceShowData); followerMap.getOrDefault(player, Collections.emptySet()).forEach(follower -> {
followerMap.getOrDefault(player, Collections.emptySet()).forEach(follower -> { trace.render(follower, finalPlayerTraceShowData);
trace.render(follower, finalPlayerTraceShowData); });
});
} else {
trace.hide(player);
}
}); });
} }
@@ -135,13 +135,6 @@ public class TraceRecorder implements Listener {
Iterator<TNTPrimed> iter = trackedTNT.getOrDefault(region, Collections.emptyList()).iterator(); Iterator<TNTPrimed> iter = trackedTNT.getOrDefault(region, Collections.emptyList()).iterator();
while (iter.hasNext()) { while (iter.hasNext()) {
TNTPrimed tnt = iter.next(); TNTPrimed tnt = iter.next();
if (tnt.isDead()) {
iter.remove();
tntSpawnRegion.remove(tnt);
historyMap.remove(tnt);
tntSpawnRegion.remove(tnt);
continue;
}
if (tnt.getFuseTicks() == 80) continue; if (tnt.getFuseTicks() == 80) continue;
TNTPoint record = record(tnt, wrappedTrace, Collections.emptyList()); TNTPoint record = record(tnt, wrappedTrace, Collections.emptyList());
if (record == null) { if (record == null) {
@@ -95,8 +95,4 @@ public class PlayerTraceShowData {
public void addViewFlag(ViewFlag viewFlag) { public void addViewFlag(ViewFlag viewFlag) {
viewFlags.put(viewFlag.getClass(), viewFlag); viewFlags.put(viewFlag.getClass(), viewFlag);
} }
public <T extends ViewFlag> void removeViewFlag(Class<T> clazz) {
viewFlags.remove(clazz);
}
} }
@@ -48,9 +48,9 @@ import static de.steamwar.bausystem.features.util.TNTClickListener.TNT_CLICK_DET
*/ */
public class TraceEntity extends RBlockDisplay { public class TraceEntity extends RBlockDisplay {
public static final float TNT_VISUAL_SCALE = 0.98F; private static final float TNT_VISUAL_SCALE = 0.98F;
public static final float TNT_VISUAL_OFFSET = -TNT_VISUAL_SCALE / 2.0F; private static final float TNT_VISUAL_OFFSET = -TNT_VISUAL_SCALE / 2.0F;
public static final Transformation TNT_VISUAL_TRANSFORM = new Transformation( private static final Transformation TNT_VISUAL_TRANSFORM = new Transformation(
new Vector3f(TNT_VISUAL_OFFSET, 0.0F, TNT_VISUAL_OFFSET), new Vector3f(TNT_VISUAL_OFFSET, 0.0F, TNT_VISUAL_OFFSET),
new Quaternionf(0, 0, 0, 1), new Quaternionf(0, 0, 0, 1),
new Vector3f(TNT_VISUAL_SCALE, TNT_VISUAL_SCALE, TNT_VISUAL_SCALE), new Vector3f(TNT_VISUAL_SCALE, TNT_VISUAL_SCALE, TNT_VISUAL_SCALE),
@@ -131,7 +131,6 @@ public abstract class ViewFlag {
if (yLocation.distanceSquared(representative.getLocation()) >= 1.0 / 256.0 && yLocation.distanceSquared(previous.getLocation()) >= 1.0 / 256.0) { if (yLocation.distanceSquared(representative.getLocation()) >= 1.0 / 256.0 && yLocation.distanceSquared(previous.getLocation()) >= 1.0 / 256.0) {
RBlockDisplay y = new RBlockDisplay(server, yLocation); RBlockDisplay y = new RBlockDisplay(server, yLocation);
y.setBlock(Material.WHITE_STAINED_GLASS.createBlockData()); y.setBlock(Material.WHITE_STAINED_GLASS.createBlockData());
y.setTransform(TraceEntity.TNT_VISUAL_TRANSFORM);
} }
Location secoundLocation; Location secoundLocation;
@@ -144,7 +143,6 @@ public abstract class ViewFlag {
if (secoundLocation.distanceSquared(representative.getLocation()) >= 1.0 / 256.0 && secoundLocation.distanceSquared(previous.getLocation()) >= 1.0 / 256.0) { if (secoundLocation.distanceSquared(representative.getLocation()) >= 1.0 / 256.0 && secoundLocation.distanceSquared(previous.getLocation()) >= 1.0 / 256.0) {
RBlockDisplay second = new RBlockDisplay(server, secoundLocation); RBlockDisplay second = new RBlockDisplay(server, secoundLocation);
second.setBlock(Material.WHITE_STAINED_GLASS.createBlockData()); second.setBlock(Material.WHITE_STAINED_GLASS.createBlockData());
second.setTransform(TraceEntity.TNT_VISUAL_TRANSFORM);
} }
} }
} }
@@ -51,10 +51,6 @@ public class IsolateFlag extends ViewFlag {
} }
} }
public boolean isEmpty() {
return tntToIsolate.isEmpty();
}
@Override @Override
public Stream<TNTPoint> filter(Stream<TNTPoint> records) { public Stream<TNTPoint> filter(Stream<TNTPoint> records) {
if (tntToIsolate.isEmpty()) return records; if (tntToIsolate.isEmpty()) return records;
@@ -38,7 +38,6 @@ import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.command.CommandMap; import org.bukkit.command.CommandMap;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
@@ -48,6 +47,7 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataType; import org.bukkit.persistence.PersistentDataType;
import java.lang.reflect.Field;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@@ -73,7 +73,19 @@ public class BindCommand extends SWCommand implements Listener {
} }
} }
private static final CommandMap commandMap = ((CraftServer) Bukkit.getServer()).getCommandMap(); private static final CommandMap commandMap;
static {
Field knownCommandsField;
try {
knownCommandsField = Bukkit.getServer().getClass().getDeclaredField("commandMap");
knownCommandsField.setAccessible(true);
commandMap = (CommandMap) knownCommandsField.get(Bukkit.getServer());
} catch (IllegalAccessException | NoSuchFieldException var2) {
Bukkit.shutdown();
throw new SecurityException("Oh shit. Commands cannot be registered.", var2);
}
}
private static final NamespacedKey KEY = SWUtils.getNamespaceKey("command"); private static final NamespacedKey KEY = SWUtils.getNamespaceKey("command");
@@ -21,6 +21,7 @@ package de.steamwar.bausystem.features.util;
import com.comphenix.tinyprotocol.TinyProtocol; import com.comphenix.tinyprotocol.TinyProtocol;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import de.steamwar.Reflection;
import de.steamwar.bausystem.BauSystem; import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.features.tpslimit.TPSUtils; import de.steamwar.bausystem.features.tpslimit.TPSUtils;
import de.steamwar.bausystem.utils.BauMemberUpdateEvent; import de.steamwar.bausystem.utils.BauMemberUpdateEvent;
@@ -29,6 +30,7 @@ import de.steamwar.core.ProtocolWrapper;
import de.steamwar.core.SWPlayer; import de.steamwar.core.SWPlayer;
import de.steamwar.linkage.Linked; import de.steamwar.linkage.Linked;
import net.minecraft.network.protocol.game.*; import net.minecraft.network.protocol.game.*;
import net.minecraft.server.level.ServerPlayerGameMode;
import net.minecraft.world.entity.player.Abilities; import net.minecraft.world.entity.player.Abilities;
import net.minecraft.world.level.GameType; import net.minecraft.world.level.GameType;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@@ -53,7 +55,7 @@ public class NoClipCommand extends SWCommand implements Listener {
@Override @Override
public void onUnmount(SWPlayer player) { public void onUnmount(SWPlayer player) {
player.getPlayer().setGameMode(GameMode.CREATIVE); player.setGameMode(GameMode.CREATIVE);
} }
} }
@@ -101,8 +103,10 @@ public class NoClipCommand extends SWCommand implements Listener {
TinyProtocol.instance.addFilter(ServerboundSetCreativeModeSlotPacket.class, third); TinyProtocol.instance.addFilter(ServerboundSetCreativeModeSlotPacket.class, third);
} }
private static final Reflection.Field<GameType> playerGameMode = Reflection.getField(ServerPlayerGameMode.class, GameType.class, 0);
private void setInternalGameMode(Player player, GameMode gameMode) { private void setInternalGameMode(Player player, GameMode gameMode) {
((CraftPlayer) player).getHandle().gameMode.gameModeForPlayer = GameType.byId(gameMode.getValue()); playerGameMode.set(((CraftPlayer) player).getHandle().gameMode, GameType.byId(gameMode.getValue()));
} }
@Register(help = true) @Register(help = true)
@@ -139,7 +143,6 @@ public class NoClipCommand extends SWCommand implements Listener {
@EventHandler(ignoreCancelled = true) @EventHandler(ignoreCancelled = true)
public void onBlock(BlockCanBuildEvent event) { public void onBlock(BlockCanBuildEvent event) {
if (event.getPlayer() == null) return;
if (SWPlayer.of(event.getPlayer()).hasComponent(NoClipData.class)) { if (SWPlayer.of(event.getPlayer()).hasComponent(NoClipData.class)) {
event.setBuildable(true); event.setBuildable(true);
} }
@@ -20,6 +20,7 @@
package de.steamwar.bausystem.features.world; package de.steamwar.bausystem.features.world;
import de.steamwar.bausystem.BauSystem; import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission;
import de.steamwar.bausystem.config.BauServer; import de.steamwar.bausystem.config.BauServer;
import de.steamwar.core.CRIUWakeupEvent; import de.steamwar.core.CRIUWakeupEvent;
import de.steamwar.linkage.Linked; import de.steamwar.linkage.Linked;
@@ -36,12 +37,13 @@ public class AntiBauAddMemberFix implements Listener {
@EventHandler(priority = EventPriority.LOW) @EventHandler(priority = EventPriority.LOW)
public void onPlayerJoin(PlayerJoinEvent event) { public void onPlayerJoin(PlayerJoinEvent event) {
if (BauSystem.DEV_SERVER) return; if (BauSystem.DEV_SERVER) return;
if (event.getPlayer().getUniqueId().equals(BauServer.getInstance().getOwner())) { if (Permission.SUPERVISOR.hasPermission(event.getPlayer())) {
return; return;
} }
if (BauweltMember.getBauMember(BauServer.getInstance().getOwner(), event.getPlayer().getUniqueId()) == null) { if (BauweltMember.getBauMember(BauServer.getInstance().getWorldID(), event.getPlayer().getUniqueId()) == null) {
event.getPlayer().kickPlayer(""); event.getPlayer().kickPlayer("");
throw new SecurityException("The player " + event.getPlayer().getName() + " joined on the server of " + SteamwarUser.byId(BauServer.getInstance().getOwnerID()).getUserName() + " without being added!"); String owner = BauServer.getInstance().isTeamWorld() ? "team " + BauServer.getInstance().getTeamID() : SteamwarUser.byId(BauServer.getInstance().getOwnerID()).getUserName();
throw new SecurityException("The player " + event.getPlayer().getName() + " joined on the server of " + owner + " without being added!");
} }
} }
@@ -1,41 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 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.bausystem.features.world;
import de.steamwar.linkage.Linked;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.scoreboard.Scoreboard;
import org.bukkit.scoreboard.Team;
@Linked
public class AntiPlayerCollision implements Listener {
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerJoin(PlayerJoinEvent event) {
Scoreboard board = event.getPlayer().getScoreboard();
Team team = board.getTeam("players");
if (team == null) team = board.registerNewTeam("players");
team.setOption(Team.Option.COLLISION_RULE, Team.OptionStatus.NEVER);
team.addPlayer(event.getPlayer());
}
}
@@ -33,7 +33,7 @@ public class AxiomPermissionCheck implements Listener {
@EventHandler @EventHandler
public void onAxiomHandshake(AxiomHandshakeEvent event) { public void onAxiomHandshake(AxiomHandshakeEvent event) {
if (Permission.SUPERVISOR.hasPermission(event.getPlayer()) || BauServer.getInstance().getOwner().equals(event.getPlayer().getUniqueId())) { if (Permission.SUPERVISOR.hasPermission(event.getPlayer()) || (BauServer.getInstance().hasOwner() && BauServer.getInstance().getOwner().equals(event.getPlayer().getUniqueId()))) {
event.setMaxBufferSize(Short.MAX_VALUE); event.setMaxBufferSize(Short.MAX_VALUE);
return; return;
} }
@@ -28,7 +28,7 @@ import de.steamwar.data.BauLockState;
import de.steamwar.linkage.Linked; import de.steamwar.linkage.Linked;
import de.steamwar.network.packets.PacketHandler; import de.steamwar.network.packets.PacketHandler;
import de.steamwar.network.packets.server.BaulockUpdatePacket; import de.steamwar.network.packets.server.BaulockUpdatePacket;
import de.steamwar.sql.UserConfig; import de.steamwar.sql.SteamwarWorld;
import lombok.Getter; import lombok.Getter;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@@ -37,13 +37,13 @@ import org.bukkit.event.Listener;
@Linked @Linked
public class BauLockStateScoreboard extends PacketHandler implements ScoreboardElement, Listener { public class BauLockStateScoreboard extends PacketHandler implements ScoreboardElement, Listener {
private static final String BAU_LOCK_CONFIG_NAME = "baulockstate";
@Getter @Getter
private BauLockState lockState = loadLockState(); private BauLockState lockState = loadLockState();
private BauLockState loadLockState() { private BauLockState loadLockState() {
String state = UserConfig.getConfig(BauServer.getInstance().getOwner(), BAU_LOCK_CONFIG_NAME); if (BauServer.getInstance().isTeamWorld()) return BauLockState.OPEN;
SteamwarWorld world = SteamwarWorld.getWorld(BauServer.getInstance().getWorldID());
String state = world == null ? null : world.getLockState();
return state == null ? BauLockState.OPEN : BauLockState.valueOf(state); return state == null ? BauLockState.OPEN : BauLockState.valueOf(state);
} }
@@ -69,6 +69,7 @@ public class BauLockStateScoreboard extends PacketHandler implements ScoreboardE
@Override @Override
public String get(Region region, Player p) { public String get(Region region, Player p) {
if (BauServer.getInstance().isTeamWorld()) return null;
if (!BauServer.getInstance().getOwner().equals(p.getUniqueId())) { if (!BauServer.getInstance().getOwner().equals(p.getUniqueId())) {
return null; return null;
} }
@@ -36,6 +36,8 @@ import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.player.PlayerItemConsumeEvent; import org.bukkit.event.player.PlayerItemConsumeEvent;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerQuitEvent;
@@ -42,7 +42,11 @@ public class KickallCommand extends SWCommand {
if (!Permission.OWNER.hasPermission(player)) return; if (!Permission.OWNER.hasPermission(player)) return;
Bukkit.getOnlinePlayers().forEach(p -> { Bukkit.getOnlinePlayers().forEach(p -> {
if (!bauServer.getOwner().equals(p.getUniqueId())) p.kickPlayer(""); if (bauServer.isTeamWorld()) {
if (!Permission.SUPERVISOR.hasPermission(p)) p.kickPlayer("");
} else if (!bauServer.getOwner().equals(p.getUniqueId())) {
p.kickPlayer("");
}
}); });
} }
} }
@@ -25,7 +25,6 @@ import de.steamwar.bausystem.config.BauServer;
import de.steamwar.bausystem.utils.BauMemberUpdateEvent; import de.steamwar.bausystem.utils.BauMemberUpdateEvent;
import de.steamwar.linkage.Linked; import de.steamwar.linkage.Linked;
import de.steamwar.sql.BauweltMember; import de.steamwar.sql.BauweltMember;
import de.steamwar.techhider.legacy.TechHider;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
@@ -40,6 +39,7 @@ import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityPickupItemEvent; import org.bukkit.event.entity.EntityPickupItemEvent;
import org.bukkit.event.player.*; import org.bukkit.event.player.*;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import de.steamwar.techhider.legacy.TechHider;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@@ -143,17 +143,14 @@ public class SpectatorListener implements Listener {
public void onPlayerJoin(PlayerJoinEvent event) { public void onPlayerJoin(PlayerJoinEvent event) {
enableOrDisableTechhider(); enableOrDisableTechhider();
if (BauSystem.DEV_SERVER) return; if (BauSystem.DEV_SERVER) return;
if (event.getPlayer().getUniqueId().equals(BauServer.getInstance().getOwner())) { if (Permission.SUPERVISOR.hasPermission(event.getPlayer())) {
return; return;
} }
BauweltMember bauweltMember = BauweltMember.getBauMember(BauServer.getInstance().getOwner(), event.getPlayer().getUniqueId()); BauweltMember bauweltMember = BauweltMember.getBauMember(BauServer.getInstance().getWorldID(), event.getPlayer().getUniqueId());
if (bauweltMember == null) { if (bauweltMember == null) {
event.getPlayer().kickPlayer(""); event.getPlayer().kickPlayer("");
return; return;
} }
if (Permission.SUPERVISOR.hasPermission(event.getPlayer())) {
return;
}
if (!anySupervisorOnline(null)) { if (!anySupervisorOnline(null)) {
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> { Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
event.getPlayer().kickPlayer(""); event.getPlayer().kickPlayer("");
@@ -57,7 +57,7 @@ public class WorldEditListener implements Listener {
private static final Set<String> commands = new HashSet<>(); private static final Set<String> commands = new HashSet<>();
private static final Set<String> commandExclusions = new HashSet<>(); private static final Set<String> commandExclusions = new HashSet<>();
private static final String[] shortcutCommands = {"//1", "//2", "//90", "//-90", "//180", "//p", "//c", "//flopy", "//floppy", "//flopyp", "//floppyp", "//u", "//r", "//download", "/download"}; private static final String[] shortcutCommands = {"//1", "//2", "//90", "//-90", "//180", "//p", "//c", "//flopy", "//floppy", "//flopyp", "//floppyp", "//u", "//r"};
public static boolean isWorldEditCommand(String command) { public static boolean isWorldEditCommand(String command) {
for (String shortcut : shortcutCommands) { for (String shortcut : shortcutCommands) {
@@ -28,7 +28,6 @@ import de.steamwar.command.SWCommand;
import de.steamwar.core.CraftbukkitWrapper; import de.steamwar.core.CraftbukkitWrapper;
import de.steamwar.linkage.Linked; import de.steamwar.linkage.Linked;
import de.steamwar.linkage.LinkedInstance; import de.steamwar.linkage.LinkedInstance;
import de.steamwar.techhider.legacy.TechHider;
import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.ChatMessageType;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket; import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
@@ -41,6 +40,7 @@ import org.bukkit.event.Listener;
import org.bukkit.event.block.Action; import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerQuitEvent;
import de.steamwar.techhider.legacy.TechHider;
import java.util.*; import java.util.*;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@@ -19,6 +19,7 @@
package de.steamwar.bausystem.utils; package de.steamwar.bausystem.utils;
import de.steamwar.Reflection;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
@@ -85,6 +86,9 @@ public class PlaceItemUtils {
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} }
private static final Reflection.Field<?> positionAccessor = Reflection.getField(CraftBlockState.class, BlockPos.class, 0);
private static final Reflection.Field<?> worldAccessor = Reflection.getField(CraftBlockState.class, CraftWorld.class, 0);
/** /**
* Attempt to place an {@link ItemStack} the {@link Player} is holding against a {@link Block} inside the World. * Attempt to place an {@link ItemStack} the {@link Player} is holding against a {@link Block} inside the World.
* This can be easily used inside the {@link org.bukkit.event.player.PlayerInteractEvent} to mimik placing a * This can be easily used inside the {@link org.bukkit.event.player.PlayerInteractEvent} to mimik placing a
@@ -284,9 +288,8 @@ public class PlaceItemUtils {
} else { } else {
// If a BlockState is present set the Position and World to the Block you want to place // If a BlockState is present set the Position and World to the Block you want to place
Location blockLocation = block.getLocation(); Location blockLocation = block.getLocation();
CraftBlockState craftBlockState = (CraftBlockState) blockState; positionAccessor.set(blockState, new BlockPos(blockLocation.getBlockX(), blockLocation.getBlockY(), blockLocation.getBlockZ()));
craftBlockState.position = new BlockPos(blockLocation.getBlockX(), blockLocation.getBlockY(), blockLocation.getBlockZ()); worldAccessor.set(blockState, blockLocation.getWorld());
craftBlockState.world = (CraftWorld) blockLocation.getWorld();
} }
if (blockData.getMaterial().isSolid()) { if (blockData.getMaterial().isSolid()) {
@@ -1,7 +1,7 @@
/* /*
* This file is a part of the SteamWar software. * This file is a part of the SteamWar software.
* *
* Copyright (C) 2026 SteamWar.de-Serverteam * Copyright (C) 2025 SteamWar.de-Serverteam
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU Affero General Public License as published by
@@ -17,7 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package de.steamwar.cursor; package de.steamwar.bausystem.utils;
import de.steamwar.entity.REntity; import de.steamwar.entity.REntity;
import lombok.Data; import lombok.Data;
@@ -20,6 +20,7 @@
package de.steamwar.bausystem.utils; package de.steamwar.bausystem.utils;
import com.comphenix.tinyprotocol.TinyProtocol; import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.Reflection;
import de.steamwar.bausystem.BauSystem; import de.steamwar.bausystem.BauSystem;
import net.minecraft.network.protocol.game.ClientboundTickingStatePacket; import net.minecraft.network.protocol.game.ClientboundTickingStatePacket;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
@@ -32,6 +33,7 @@ public class TickManager implements Listener {
public static final TickManager impl = new TickManager(); public static final TickManager impl = new TickManager();
private static final ServerTickRateManager manager = MinecraftServer.getServer().tickRateManager(); private static final ServerTickRateManager manager = MinecraftServer.getServer().tickRateManager();
private static final Reflection.Field<Long> remainingSprintTicks = Reflection.getField(ServerTickRateManager.class, long.class, 0);
private boolean blockTpsPacket = true; private boolean blockTpsPacket = true;
private int totalSteps; private int totalSteps;
@@ -119,7 +121,7 @@ public class TickManager implements Listener {
public long getRemainingTicks() { public long getRemainingTicks() {
if (isSprinting()) { if (isSprinting()) {
return manager.remainingSprintTicks; return remainingSprintTicks.get(manager);
} else { } else {
return manager.frozenTicksToRun(); return manager.frozenTicksToRun();
} }
@@ -19,7 +19,6 @@
package de.steamwar.bausystem.utils; package de.steamwar.bausystem.utils;
import com.fastasyncworldedit.core.regions.selector.PolyhedralRegionSelector;
import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.IncompleteRegionException; import com.sk89q.worldedit.IncompleteRegionException;
import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.LocalSession;
@@ -33,19 +32,16 @@ import com.sk89q.worldedit.internal.registry.InputParser;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.regions.RegionSelector; import com.sk89q.worldedit.regions.RegionSelector;
import com.sk89q.worldedit.regions.selector.*;
import com.sk89q.worldedit.regions.selector.limit.SelectorLimits; import com.sk89q.worldedit.regions.selector.limit.SelectorLimits;
import com.sk89q.worldedit.world.World; import de.steamwar.Reflection;
import de.steamwar.bausystem.shared.Pair; import de.steamwar.bausystem.shared.Pair;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@UtilityClass @UtilityClass
@@ -95,36 +91,17 @@ public class WorldEditUtils {
.getRegionSelector(BukkitAdapter.adapt(player.getWorld())); .getRegionSelector(BukkitAdapter.adapt(player.getWorld()));
return new Pair<>(regionSelector.getClass(), regionSelector.getVertices() return new Pair<>(regionSelector.getClass(), regionSelector.getVertices()
.stream() .stream()
.map(blockVector3 -> { .map(blockVector3 -> blockVector3 == null ? null : adapt(player.getWorld(), blockVector3))
if (blockVector3 == null) {
return null;
} else {
return BukkitAdapter.adapt(player.getWorld(), blockVector3);
}
})
.collect(Collectors.toList())); .collect(Collectors.toList()));
} }
private static final Map<Class<? extends RegionSelector>, Function<World, RegionSelector>> constructors = new HashMap<>();
static {
constructors.put(CuboidRegionSelector.class, CuboidRegionSelector::new);
constructors.put(ExtendingCuboidRegionSelector.class, ExtendingCuboidRegionSelector::new);
constructors.put(Polygonal2DRegionSelector.class, Polygonal2DRegionSelector::new);
constructors.put(EllipsoidRegionSelector.class, EllipsoidRegionSelector::new);
constructors.put(SphereRegionSelector.class, SphereRegionSelector::new);
constructors.put(CylinderRegionSelector.class, CylinderRegionSelector::new);
constructors.put(ConvexPolyhedralRegionSelector.class, ConvexPolyhedralRegionSelector::new);
constructors.put(PolyhedralRegionSelector.class, PolyhedralRegionSelector::new);
}
public void setVertices(Player player, Class<? extends RegionSelector> clazz, List<Location> vertices) { public void setVertices(Player player, Class<? extends RegionSelector> clazz, List<Location> vertices) {
LocalSession localSession = WorldEdit.getInstance() LocalSession localSession = WorldEdit.getInstance()
.getSessionManager() .getSessionManager()
.get(BukkitAdapter.adapt(player)); .get(BukkitAdapter.adapt(player));
Function<World, RegionSelector> constructor = constructors.get(clazz); Reflection.Constructor constructorInvoker = Reflection.getConstructor(clazz, com.sk89q.worldedit.world.World.class);
if (constructor == null) return; RegionSelector regionSelector = (RegionSelector) constructorInvoker.invoke(BukkitAdapter.adapt(player.getWorld()));
RegionSelector regionSelector = constructor.apply(BukkitAdapter.adapt(player.getWorld()));
localSession.setRegionSelector(BukkitAdapter.adapt(player.getWorld()), regionSelector); localSession.setRegionSelector(BukkitAdapter.adapt(player.getWorld()), regionSelector);
if (vertices.isEmpty()) return; if (vertices.isEmpty()) return;
@@ -150,9 +127,13 @@ public class WorldEditUtils {
try { try {
BlockVector3 min = regionSelector.getRegion().getMinimumPoint(); BlockVector3 min = regionSelector.getRegion().getMinimumPoint();
BlockVector3 max = regionSelector.getRegion().getMaximumPoint(); BlockVector3 max = regionSelector.getRegion().getMaximumPoint();
return new Pair<>(BukkitAdapter.adapt(player.getWorld(), min), BukkitAdapter.adapt(player.getWorld(), max)); return new Pair<>(adapt(player.getWorld(), min), adapt(player.getWorld(), max));
} catch (IncompleteRegionException e) { } catch (IncompleteRegionException e) {
return null; return null;
} }
} }
private Location adapt(World world, BlockVector3 blockVector3) {
return new Location(world, blockVector3.getBlockX(), blockVector3.getBlockY(), blockVector3.getBlockZ());
}
} }
+1 -1
View File
@@ -4,7 +4,7 @@ version: "2.0"
depend: [ WorldEdit, SpigotCore ] depend: [ WorldEdit, SpigotCore ]
load: POSTWORLD load: POSTWORLD
main: de.steamwar.bausystem.BauSystem main: de.steamwar.bausystem.BauSystem
api-version: "1.21" api-version: "1.13"
website: "https://steamwar.de" website: "https://steamwar.de"
description: "So unseriös wie wir sind: BauSystem nur besser." description: "So unseriös wie wir sind: BauSystem nur besser."
-1
View File
@@ -37,6 +37,5 @@ tasks.register<DevServer>("DevBau21") {
dependsOn(":SpigotCore:shadowJar") dependsOn(":SpigotCore:shadowJar")
dependsOn(":BauSystem:shadowJar") dependsOn(":BauSystem:shadowJar")
dependsOn(":SchematicSystem:shadowJar") dependsOn(":SchematicSystem:shadowJar")
dependsOn(":KotlinCore:shadowJar")
template = "Bau21" template = "Bau21"
} }
-1
View File
@@ -18,7 +18,6 @@ dependencies {
implementation("com.github.ajalt.clikt:clikt:5.0.3") implementation("com.github.ajalt.clikt:clikt:5.0.3")
implementation("com.github.ajalt.mordant:mordant:3.0.2") implementation("com.github.ajalt.mordant:mordant:3.0.2")
implementation(libs.logback) implementation(libs.logback)
implementation("org.yaml:snakeyaml:2.2")
implementation("org.mariadb.jdbc:mariadb-java-client:3.3.1") implementation("org.mariadb.jdbc:mariadb-java-client:3.3.1")
implementation(libs.exposedCore) implementation(libs.exposedCore)
+10
View File
@@ -11,12 +11,22 @@ import de.steamwar.commands.profiler.ProfilerCommand
import de.steamwar.commands.user.UserCommand import de.steamwar.commands.user.UserCommand
import de.steamwar.commands.user.UserInfoCommand import de.steamwar.commands.user.UserInfoCommand
import de.steamwar.commands.user.UserSearchCommand import de.steamwar.commands.user.UserSearchCommand
import de.steamwar.commands.world.MigrationCommand
import de.steamwar.commands.world.SaveToStorageCommand
import de.steamwar.commands.world.TemplateCommand
import de.steamwar.commands.world.TemplateCreateCommand
import de.steamwar.commands.world.WorldCommand
fun main(args: Array<String>) = fun main(args: Array<String>) =
SteamWar() SteamWar()
.subcommands( .subcommands(
DatabaseCommand().subcommands(InfoCommand(), ResetCommand()), DatabaseCommand().subcommands(InfoCommand(), ResetCommand()),
UserCommand().subcommands(UserInfoCommand(), UserSearchCommand()), UserCommand().subcommands(UserInfoCommand(), UserSearchCommand()),
WorldCommand().subcommands(
MigrationCommand(),
SaveToStorageCommand(),
TemplateCommand().subcommands(TemplateCreateCommand())
),
DevCommand(), DevCommand(),
ProfilerCommand() ProfilerCommand()
) )
+4 -17
View File
@@ -7,7 +7,6 @@ import com.github.ajalt.mordant.rendering.TextColors
import com.github.ajalt.mordant.rendering.TextStyles import com.github.ajalt.mordant.rendering.TextStyles
import de.steamwar.db.Database import de.steamwar.db.Database
import de.steamwar.db.execute import de.steamwar.db.execute
import de.steamwar.db.executeScript
import de.steamwar.db.useDb import de.steamwar.db.useDb
import java.io.File import java.io.File
@@ -23,24 +22,12 @@ class ResetCommand : CliktCommand() {
val schema = schemaFile.readText() val schema = schemaFile.readText()
execute("SET FOREIGN_KEY_CHECKS=0;") { } val tables = execute("SHOW TABLES;") { it.getString(1) }
for (table in tables) {
val databaseObjects = execute("SHOW FULL TABLES;") { it.getString(1) to it.getString(2) } execute("DROP TABLE IF EXISTS $table;") { }
for (view in databaseObjects.filter { it.second == "VIEW" }.map { it.first }) {
execute("DROP VIEW IF EXISTS `${view.replace("`", "``")}`;") { }
}
for (table in databaseObjects.filter { it.second == "BASE TABLE" }.map { it.first }) {
execute("DROP TABLE IF EXISTS `${table.replace("`", "``")}`;") { }
} }
executeScript(schema) execute(schema) { }
val seed = javaClass.getResource("/db/reset-seed.sql")
?: throw CliktError("Reset seed file not found!")
executeScript(seed.readText())
execute("SET FOREIGN_KEY_CHECKS=1;") { }
echo(TextColors.brightGreen(TextStyles.bold("Database reset!"))) echo(TextColors.brightGreen(TextStyles.bold("Database reset!")))
} }
+15 -90
View File
@@ -14,11 +14,9 @@ import com.github.ajalt.clikt.parameters.types.file
import com.github.ajalt.clikt.parameters.types.long import com.github.ajalt.clikt.parameters.types.long
import com.github.ajalt.clikt.parameters.types.path import com.github.ajalt.clikt.parameters.types.path
import com.sun.security.auth.module.UnixSystem import com.sun.security.auth.module.UnixSystem
import org.yaml.snakeyaml.Yaml
import java.io.File import java.io.File
import kotlin.io.path.absolute import kotlin.io.path.absolute
import kotlin.io.path.absolutePathString import kotlin.io.path.absolutePathString
import kotlin.random.Random
const val LOG4J_CONFIG = """<?xml version="1.0" encoding="UTF-8"?> const val LOG4J_CONFIG = """<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" packages="com.mojang.util"> <Configuration status="WARN" packages="com.mojang.util">
@@ -71,47 +69,35 @@ class DevCommand : CliktCommand("dev") {
override fun run() { override fun run() {
val args = mutableListOf<String>() val args = mutableListOf<String>()
var serverDir = resolveServerDirectory(server) val serverDirectory = File(workingDir, server)
val serverDir =
if (serverDirectory.exists() && serverDirectory.isDirectory) serverDirectory else File(workingDir, server)
if (isVelocity(server)) { if (isVelocity(server)) {
runServer( runServer(
args, jvmArgs, listOf( args, jvmArgs, listOf(
jar?.absolutePath jar?.absolutePath
?: File("/jars/Velocity.jar").absolutePath ?: File("/jar/Velocity.jar").absolutePath
), serverDir ), serverDir
) )
} else { } else {
setLogConfig(args) setLogConfig(args)
val version = findVersion(server) val version = findVersion(server)
?: throw CliktError("Unknown Server Version") ?: throw CliktError("Unknown Server Version")
val gameModeTemplate = if (serverDir.isDirectory) null else loadGameModeTemplate(server)
if (gameModeTemplate != null) {
serverDir = gameModeTemplate.serverDir
args += "-Dconfig=$server.yml"
}
val worldFile = world?.absolute()?.toFile() val worldFile = world?.absolute()?.toFile()
?: File(workingDir, "devtempworld") ?: File(serverDir, "devtempworld")
var jarFile = jar?.absolutePath val jarFile = jar?.absolutePath
?: additionalVersions[server]?.let { supportedVersionJars[it] } ?: additionalVersions[server]?.let { supportedVersionJars[it] }
?: supportedVersionJars[version] ?: supportedVersionJars[version]
?: throw CliktError("Unknown Server Version") ?: throw CliktError("Unknown Server Version")
if (gameModeTemplate != null) {
jarFile = if (gameModeTemplate.spigot) {
jarFile.replace("paper", "spigot")
} else {
jarFile.replace("spigot", "paper")
}
}
if (!worldFile.exists()) { if (!worldFile.exists()) {
val templateFile = gameModeTemplate?.worldTemplate ?: File(serverDir, "Bauwelt") val templateFile = File(serverDir, "Bauwelt")
if (!templateFile.exists()) { if (!templateFile.exists()) {
throw CliktError("Could not find world template: ${templateFile.absolutePath}") throw CliktError("World Template not found!")
} }
worldFile.parentFile?.mkdirs()
templateFile.copyRecursively(worldFile) templateFile.copyRecursively(worldFile)
} }
val worldDir = worldFile.parentFile ?: workingDir
val devFile = File("/configs/DevServer/${System.getProperty("user.name")}.$port.$version") val devFile = File("/configs/DevServer/${System.getProperty("user.name")}.$port.$version")
if (System.getProperty("user.name") != "minecraft") { if (System.getProperty("user.name") != "minecraft") {
@@ -122,11 +108,10 @@ class DevCommand : CliktCommand("dev") {
args, jvmArgs, listOf( args, jvmArgs, listOf(
jarFile, jarFile,
*(if (forceUpgrade) arrayOf("-forceUpgrade") else arrayOf()), *(if (forceUpgrade) arrayOf("-forceUpgrade") else arrayOf()),
"--log-strip-color",
"--port", port.toString(), "--port", port.toString(),
"--level-name", worldFile.name, "--level-name", worldFile.name,
"--world-dir", worldDir.absolutePath, "--world-dir", workingDir.absolutePath,
"nogui", "--nogui",
*(if (plugins != null) arrayOf("--plugins", plugins!!.absolutePathString()) else arrayOf()) *(if (plugins != null) arrayOf("--plugins", plugins!!.absolutePathString()) else arrayOf())
), serverDir ), serverDir
) )
@@ -138,12 +123,6 @@ class DevCommand : CliktCommand("dev") {
} }
} }
data class GameModeTemplate(
val serverDir: File,
val worldTemplate: File,
val spigot: Boolean
)
val jvmDefaultParams = arrayOf( val jvmDefaultParams = arrayOf(
"-Xmx1G", "-Xmx1G",
"-Xgc:excessiveGCratio=80", "-Xgc:excessiveGCratio=80",
@@ -159,16 +138,6 @@ class DevCommand : CliktCommand("dev") {
val jvmArgOverrides = arrayOf("--add-opens", "java.base/jdk.internal.misc=ALL-UNNAMED") val jvmArgOverrides = arrayOf("--add-opens", "java.base/jdk.internal.misc=ALL-UNNAMED")
val jvmNonJava8Params = arrayOf(
*jvmArgOverrides,
"-XX:-CRIUSecProvider"
)
val extendedStartupParams = arrayOf(
"-Dpaper.disablePluginRemapping=true",
"-javaagent:/jars/AccessWidener.jar=start"
)
val supportedVersionJars = mapOf( val supportedVersionJars = mapOf(
8 to "/jars/paper-1.8.8.jar", 8 to "/jars/paper-1.8.8.jar",
9 to "/jars/spigot-1.9.4.jar", 9 to "/jars/spigot-1.9.4.jar",
@@ -183,7 +152,8 @@ class DevCommand : CliktCommand("dev") {
) )
val additionalVersions = mapOf( val additionalVersions = mapOf(
"Lobby" to 21 "Tutorial" to 15,
"Lobby" to 20
) )
fun findVersion(server: String): Int? = fun findVersion(server: String): Int? =
@@ -196,41 +166,6 @@ class DevCommand : CliktCommand("dev") {
fun isVelocity(server: String): Boolean = fun isVelocity(server: String): Boolean =
server.endsWith("Velocity") server.endsWith("Velocity")
fun resolveServerDirectory(server: String): File {
val localServer = File(workingDir, server)
if (localServer.isDirectory) {
return localServer
}
return File("/servers", server)
}
fun loadGameModeTemplate(server: String): GameModeTemplate? {
val configFile = File("/configs/GameModes/$server.yml")
if (!configFile.exists()) {
throw CliktError("Server/GameMode not found")
}
val document = configFile.reader().use { reader ->
Yaml().load<Map<String, Any?>>(reader)
} ?: throw CliktError("GameMode config is empty: ${configFile.absolutePath}")
val serverConfig = document["Server"] as? Map<*, *>
?: throw CliktError("GameMode config is missing Server section: ${configFile.absolutePath}")
val folder = serverConfig["Folder"] as? String
?: throw CliktError("GameMode config is missing Server.Folder: ${configFile.absolutePath}")
val maps = (serverConfig["Maps"] as? List<*>)
?.filterIsInstance<String>()
?.takeIf { it.isNotEmpty() }
?: throw CliktError("GameMode config is missing Server.Maps: ${configFile.absolutePath}")
val serverDir = File("/servers", folder)
val worldTemplate = File(File(serverDir, "arenas"), maps[Random.nextInt(maps.size)])
return GameModeTemplate(
serverDir = serverDir,
worldTemplate = worldTemplate,
spigot = serverConfig["Spigot"] == true
)
}
fun setLogConfig(args: MutableList<String>) { fun setLogConfig(args: MutableList<String>) {
args += "-DlogPath=${workingDir.absolutePath}/logs" args += "-DlogPath=${workingDir.absolutePath}/logs"
args += "-Dlog4j.configurationFile=${log4jConfig.absolutePath}" args += "-Dlog4j.configurationFile=${log4jConfig.absolutePath}"
@@ -241,23 +176,13 @@ class DevCommand : CliktCommand("dev") {
} }
fun runServer(args: List<String>, jvmArgs: List<String>, cmd: List<String>, serverDir: File) { fun runServer(args: List<String>, jvmArgs: List<String>, cmd: List<String>, serverDir: File) {
val effectiveJvmArgs = mutableListOf<String>()
effectiveJvmArgs += jvmArgs
if (!isVelocity(server)) {
extendedStartupParams.forEach { arg ->
if (effectiveJvmArgs.none { it == arg }) {
effectiveJvmArgs += arg
}
}
}
val process = ProcessBuilder( val process = ProcessBuilder(
jvm?.absolutePath jvm?.absolutePath
?: if (isJava8(server)) "/usr/lib/jvm/openj9-8/bin/java" else "/usr/lib/jvm/openj9-21/bin/java", ?: if (isJava8(server)) "/usr/lib/jvm/openj9-8/bin/java" else "java",
*effectiveJvmArgs.toTypedArray(), *jvmArgs.toTypedArray(),
*args.toTypedArray(), *args.toTypedArray(),
*jvmDefaultParams, *jvmDefaultParams,
*(if (isJava8(server)) arrayOf() else jvmNonJava8Params), *(if (isJava8(server)) arrayOf() else jvmArgOverrides),
*(if (profile) arrayOf("-javaagent:/jars/LixfelsProfiler.jar=start") else arrayOf()), *(if (profile) arrayOf("-javaagent:/jars/LixfelsProfiler.jar=start") else arrayOf()),
"-Xshareclasses:nonfatal,name=$server", "-Xshareclasses:nonfatal,name=$server",
"-jar", "-jar",
+243
View File
@@ -0,0 +1,243 @@
package de.steamwar.commands.world
import com.github.ajalt.clikt.core.CliktCommand
import de.steamwar.db.Database
import de.steamwar.sql.SteamwarUser
import de.steamwar.sql.SteamwarWorld
import de.steamwar.sql.UserConfig
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
import java.io.File
import java.sql.SQLException
import java.util.UUID
class MigrationCommand : CliktCommand(name = "migrate") {
private val userWorldPattern = Regex("""userworlds(\d+)""")
private val builderWorldPattern = Regex("""builder(\d+)""")
private val trailingVersionPattern = Regex("""(\d+)$""")
override fun run() {
Database.ensureConnected()
val bau = migrateBauWorlds()
val builder = migrateBuilderWorlds()
val arena = migrateArenaWorlds()
migrateBauLockConfig()
migrateBauweltMembers()
echo("Imported ${bau.imported} build worlds, skipped ${bau.skipped}, missing users ${bau.missingOwner}.")
echo("Imported ${builder.imported} builder worlds, skipped ${builder.skipped}.")
echo("Imported ${arena.imported} arena worlds, skipped ${arena.skipped}.")
echo("World migration completed.")
}
private fun migrateBauWorlds(): MigrationStats {
val stats = MigrationStats()
val worldsRoot = File("/worlds")
worldsRoot.listFiles { file -> file.isDirectory && userWorldPattern.matches(file.name) }
?.forEach { versionFolder ->
val version = userWorldPattern.matchEntire(versionFolder.name)!!.groupValues[1].toInt()
versionFolder.listFiles { file -> file.isDirectory && isWorldDirectory(file) }
?.sortedBy { it.name }
?.forEach { legacyWorld ->
val owner = legacyWorld.ownerUser()
if (owner == null) {
stats.missingOwner++
echo("Skipped build world ${legacyWorld.path}: no matching user.")
return@forEach
}
val lockState = UserConfig.getConfig(owner.id.value, BAU_LOCK_CONFIG_NAME)
val world = SteamwarWorld.getOrCreateBauWorld(owner, owner.userName, version, legacyWorld)
if (world.lockState == null && lockState != null) {
world.changeLockState(lockState)
}
stats.imported++
}
} ?: run {
stats.skipped++
echo("Skipped build worlds: /worlds does not exist.")
}
return stats
}
private fun migrateBuilderWorlds(): MigrationStats {
val stats = MigrationStats()
val worldsRoot = File("/worlds")
worldsRoot.listFiles { file -> file.isDirectory && builderWorldPattern.matches(file.name) }
?.forEach { versionFolder ->
val version = builderWorldPattern.matchEntire(versionFolder.name)!!.groupValues[1].toInt()
versionFolder.listFiles { file -> file.isDirectory && isWorldDirectory(file) }
?.sortedBy { it.name }
?.forEach { legacyWorld ->
SteamwarWorld.getOrCreateBuilderWorld(legacyWorld.name, version, legacyWorld)
stats.imported++
}
} ?: run {
stats.skipped++
echo("Skipped builder worlds: /worlds does not exist.")
}
return stats
}
private fun migrateArenaWorlds(): MigrationStats {
val stats = MigrationStats()
val configs = parseGameModeConfigs(File("/configs/GameModes"))
if (configs.isEmpty()) {
stats.skipped++
echo("Skipped arena worlds: no game mode configs found in /configs/GameModes.")
return stats
}
for (config in configs) {
val version = trailingVersionPattern.find(config.serverFolder)?.value?.toIntOrNull()
if (version == null) {
stats.skipped++
echo("Skipped arena mode ${config.modeName}: server folder '${config.serverFolder}' has no version suffix.")
continue
}
val arenaRoot = File("/servers/${config.serverFolder}/arenas")
for (map in config.maps) {
val legacyWorld = File(arenaRoot, map)
if (!isWorldDirectory(legacyWorld)) {
stats.skipped++
continue
}
SteamwarWorld.getOrCreateArenaWorld(config.modeName, map, version, legacyWorld)
stats.imported++
}
}
return stats
}
private fun migrateBauLockConfig() {
transaction(Database.db) {
exec(
"""
UPDATE `world` w
JOIN `UserConfig` uc ON uc.`User` = w.`Owner` AND uc.`Config` = 'baulockstate'
SET w.`LockState` = uc.`Value`
WHERE w.`Owner` IS NOT NULL AND w.`LockState` IS NULL
""".trimIndent()
)
}
}
private fun migrateBauweltMembers() {
if (!columnExists("BauweltMember", "WorldID") || !columnExists("BauweltMember", "BauweltID")) {
echo("Skipped BauweltMember data migration: expected WorldID and BauweltID columns are not both present.")
return
}
execIgnore(
"""
INSERT IGNORE INTO `BauweltMember` (`WorldID`, `MemberID`, `Build`, `WorldEdit`, `World`)
SELECT w.`id`, bm.`MemberID`, bm.`Build`, bm.`WorldEdit`, bm.`World`
FROM `BauweltMember` bm
JOIN `world` w ON w.`Owner` = bm.`BauweltID` AND w.`Deleted` = 0
""".trimIndent(),
"Skipped BauweltMember data migration"
)
}
private fun parseGameModeConfigs(configRoot: File): List<ArenaMigrationConfig> {
if (!configRoot.isDirectory) return emptyList()
return configRoot.listFiles { file -> file.isFile && file.extension == "yml" && !file.name.endsWith(".kits.yml") }
?.mapNotNull { file ->
val server = parseServerBlock(file)
val folder = server.folder ?: return@mapNotNull null
ArenaMigrationConfig(file.nameWithoutExtension, folder, server.maps)
}
?: emptyList()
}
private fun parseServerBlock(file: File): ParsedServerBlock {
var inServer = false
var inMaps = false
var folder: String? = null
val maps = mutableListOf<String>()
file.readLines().forEach { line ->
val trimmed = line.trim()
if (trimmed.isEmpty() || trimmed.startsWith("#")) return@forEach
val indent = line.indexOfFirst { !it.isWhitespace() }.let { if (it == -1) 0 else it }
if (indent == 0) {
inServer = trimmed == "Server:"
inMaps = false
return@forEach
}
if (!inServer) return@forEach
when {
indent <= 2 && trimmed.startsWith("Folder:") -> folder = trimmed.substringAfter(":").cleanYamlValue()
indent <= 2 && trimmed.startsWith("Maps:") -> {
val value = trimmed.substringAfter(":").trim()
inMaps = true
if (value.startsWith("[") && value.endsWith("]")) {
maps += value.removePrefix("[").removeSuffix("]")
.split(",")
.map { it.cleanYamlValue() }
.filter { it.isNotEmpty() }
inMaps = false
}
}
inMaps && trimmed.startsWith("-") -> maps += trimmed.removePrefix("-").cleanYamlValue()
inMaps && indent <= 2 -> inMaps = false
}
}
return ParsedServerBlock(folder, maps)
}
private fun File.ownerUser(): SteamwarUser? {
name.toIntOrNull()?.let { return SteamwarUser.byId(it) }
return runCatching { SteamwarUser.get(UUID.fromString(name)) }.getOrNull()
}
private fun isWorldDirectory(file: File): Boolean =
file.isDirectory && (File(file, "level.dat").isFile || File(file, "region").isDirectory)
private fun String.cleanYamlValue(): String =
trim().trim('"').trim('\'')
private fun columnExists(table: String, column: String): Boolean =
transaction(Database.db) {
exec("SHOW COLUMNS FROM `$table` LIKE '$column'") { result -> result.next() } ?: false
}
private fun execIgnore(sql: String, prefix: String) {
try {
transaction(Database.db) {
exec(sql)
}
} catch (e: SQLException) {
echo("$prefix: ${e.message}")
}
}
private data class MigrationStats(
var imported: Int = 0,
var skipped: Int = 0,
var missingOwner: Int = 0,
)
private data class ArenaMigrationConfig(
val modeName: String,
val serverFolder: String,
val maps: List<String>,
)
private data class ParsedServerBlock(
val folder: String?,
val maps: List<String>,
)
private companion object {
private const val BAU_LOCK_CONFIG_NAME = "baulockstate"
}
}
@@ -0,0 +1,30 @@
package de.steamwar.commands.world
import com.github.ajalt.clikt.core.CliktCommand
import de.steamwar.db.Database
import de.steamwar.sql.SteamwarWorld
import java.time.LocalTime
class SaveToStorageCommand : CliktCommand(name = "saveToStorage") {
override fun run() {
Database.ensureConnected()
var archived = 0
for (world in SteamwarWorld.getWorldsToArchive()) {
if (!beforeCutoff()) {
echo("Stopping before 04:00. Archived $archived worlds.")
return
}
if (world.archiveIfNeeded()) {
archived++
echo("Archived ${world.uuid} (${world.type}/${world.name}).")
}
}
echo("Storage save completed. Archived $archived worlds.")
}
private fun beforeCutoff(): Boolean =
LocalTime.now().isBefore(LocalTime.of(4, 0))
}
+24
View File
@@ -0,0 +1,24 @@
package de.steamwar.commands.world
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.types.int
import com.github.ajalt.clikt.parameters.types.path
import de.steamwar.db.Database
import de.steamwar.sql.SteamwarWorld
class TemplateCommand : CliktCommand(name = "template") {
override fun run() = Unit
}
class TemplateCreateCommand : CliktCommand(name = "create") {
private val name by argument()
private val version by argument().int()
private val source by argument().path(canBeFile = false, mustExist = true)
override fun run() {
Database.ensureConnected()
SteamwarWorld.getOrCreateTemplateWorld(name, version, source.toFile())
echo("Template world '$name' created.")
}
}
+7
View File
@@ -0,0 +1,7 @@
package de.steamwar.commands.world
import com.github.ajalt.clikt.core.CliktCommand
class WorldCommand : CliktCommand(name = "world") {
override fun run() = Unit
}
+9 -120
View File
@@ -16,14 +16,15 @@ import java.sql.ResultSet
import java.util.* import java.util.*
object Database { object Database {
val host: String lateinit var host: String
val port: String lateinit var port: String
val database: String lateinit var database: String
val username: String
val password: String
lateinit var db: Database lateinit var db: Database
init { fun ensureConnected() {
if (::db.isInitialized) {
return
}
val config = File(System.getProperty("user.home"), "mysql.properties") val config = File(System.getProperty("user.home"), "mysql.properties")
if (!config.exists()) { if (!config.exists()) {
@@ -37,14 +38,9 @@ object Database {
host = props.getProperty("host") host = props.getProperty("host")
port = props.getProperty("port") port = props.getProperty("port")
database = props.getProperty("database") database = props.getProperty("database")
username = props.getProperty("user")
password = props.getProperty("password")
}
fun ensureConnected() { val username = props.getProperty("user")
if (::db.isInitialized) { val password = props.getProperty("password")
return
}
val url = "jdbc:mariadb://$host:$port/$database" val url = "jdbc:mariadb://$host:$port/$database"
@@ -83,113 +79,6 @@ fun <T> JdbcTransaction.executeSingle(sql: String, transform: (ResultSet) -> T):
}.single() }.single()
} }
fun JdbcTransaction.executeScript(sql: String) {
for (statement in splitSqlScript(sql)) {
exec(statement) { }
}
}
private fun splitSqlScript(sql: String): List<String> {
val statements = mutableListOf<String>()
val current = StringBuilder()
var quote: Char? = null
var inLineComment = false
var inBlockComment = false
var index = 0
fun addStatement() {
val statement = current.toString().trim()
if (statement.isNotEmpty()) {
statements += statement
}
current.clear()
}
while (index < sql.length) {
val char = sql[index]
val next = sql.getOrNull(index + 1)
if (inLineComment) {
current.append(char)
if (char == '\n') {
inLineComment = false
}
index++
continue
}
if (inBlockComment) {
current.append(char)
if (char == '*' && next == '/') {
current.append(next)
inBlockComment = false
index += 2
} else {
index++
}
continue
}
if (quote != null) {
current.append(char)
if (char == '\\' && quote != '`' && next != null) {
current.append(next)
index += 2
continue
}
if (char == quote) {
if (next == quote) {
current.append(next)
index += 2
continue
}
quote = null
}
index++
continue
}
when {
char == '-' && next == '-' -> {
current.append(char).append(next)
inLineComment = true
index += 2
}
char == '#' -> {
current.append(char)
inLineComment = true
index++
}
char == '/' && next == '*' -> {
current.append(char).append(next)
inBlockComment = true
index += 2
}
char == '\'' || char == '"' || char == '`' -> {
current.append(char)
quote = char
index++
}
char == ';' -> {
addStatement()
index++
}
else -> {
current.append(char)
index++
}
}
}
addStatement()
return statements
}
fun useDb(statement: JdbcTransaction.() -> Unit) { fun useDb(statement: JdbcTransaction.() -> Unit) {
de.steamwar.db.Database.ensureConnected() de.steamwar.db.Database.ensureConnected()
transaction(de.steamwar.db.Database.db, statement = statement) transaction(de.steamwar.db.Database.db, statement = statement)
File diff suppressed because one or more lines are too long
-5
View File
@@ -19,7 +19,6 @@
plugins { plugins {
steamwar.kotlin steamwar.kotlin
kotlin("plugin.lombok")
} }
kotlin { kotlin {
@@ -41,8 +40,4 @@ dependencies {
compileOnlyApi(libs.exposedDao) compileOnlyApi(libs.exposedDao)
compileOnlyApi(libs.exposedJdbc) compileOnlyApi(libs.exposedJdbc)
compileOnlyApi(libs.exposedTime) compileOnlyApi(libs.exposedTime)
testImplementation(kotlin("test"))
}
repositories {
mavenCentral()
} }
@@ -19,7 +19,6 @@
package de.steamwar.sql package de.steamwar.sql
import de.steamwar.sql.BauweltMemberTable.bauweltId
import de.steamwar.sql.internal.useDb import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.core.and import org.jetbrains.exposed.v1.core.and
import org.jetbrains.exposed.v1.core.dao.id.CompositeID import org.jetbrains.exposed.v1.core.dao.id.CompositeID
@@ -32,45 +31,37 @@ import org.jetbrains.exposed.v1.jdbc.insertIgnore
import java.util.* import java.util.*
object BauweltMemberTable : CompositeIdTable("BauweltMember") { object BauweltMemberTable : CompositeIdTable("BauweltMember") {
val bauweltId = reference("BauweltID", SteamwarUserTable).index() val worldId = reference("WorldID", WorldTable).index()
val memberId = reference("MemberID", SteamwarUserTable).index() val memberId = reference("MemberID", SteamwarUserTable).index()
val build = bool("Build") val build = bool("Build")
val worldEdit = bool("WorldEdit") val worldEdit = bool("WorldEdit")
val world = bool("World") val world = bool("World")
override val primaryKey = PrimaryKey(bauweltId, memberId) override val primaryKey = PrimaryKey(worldId, memberId)
init { init {
addIdColumn(bauweltId) addIdColumn(worldId)
addIdColumn(memberId) addIdColumn(memberId)
} }
} }
class BauweltMember(id: EntityID<CompositeID>) : CompositeEntity(id) { class BauweltMember(id: EntityID<CompositeID>) : CompositeEntity(id) {
companion object : CompositeEntityClass<BauweltMember>(BauweltMemberTable) { companion object : CompositeEntityClass<BauweltMember>(BauweltMemberTable) {
private val cache = mutableMapOf<Int, BauweltMember>() private val cache = mutableMapOf<Pair<UUID, Int>, BauweltMember>()
private fun cache(member: BauweltMember) = private fun cache(member: BauweltMember) =
cache.put(member.memberID, member) cache.put(member.worldID to member.memberID, member)
@JvmStatic @JvmStatic
fun clear() = fun clear() =
cache.clear() cache.clear()
@JvmStatic @JvmStatic
@Deprecated("Use addMember(ownerId: Int, newMemberId: Int)") fun addMember(worldId: UUID, newMemberId: Int) =
fun addMember(ownerId: UUID, newMemberId: UUID) =
addMember(SteamwarUser.get(ownerId)!!.id, SteamwarUser.get(newMemberId)!!.id)
@JvmStatic
fun addMember(ownerId: Int, newMemberId: Int) =
addMember(EntityID(ownerId, SteamwarUserTable), EntityID(newMemberId, SteamwarUserTable))
fun addMember(ownerId: EntityID<Int>, newMemberId: EntityID<Int>) =
useDb { useDb {
BauweltMemberTable.insertIgnore { BauweltMemberTable.insertIgnore {
it[bauweltId] = ownerId it[BauweltMemberTable.worldId] = EntityID(worldId, WorldTable)
it[memberId] = newMemberId it[memberId] = EntityID(newMemberId, SteamwarUserTable)
it[build] = false it[build] = false
it[worldEdit] = false it[worldEdit] = false
it[world] = false it[world] = false
@@ -78,31 +69,32 @@ class BauweltMember(id: EntityID<CompositeID>) : CompositeEntity(id) {
} }
@JvmStatic @JvmStatic
@Deprecated("Use getBauMember(bauwelt: Int, member: Int)") fun addMember(worldId: UUID, newMemberId: UUID) =
fun getBauMember(bauwelt: UUID, member: UUID) = addMember(worldId, SteamwarUser.get(newMemberId)!!.id.value)
@JvmStatic
fun getBauMember(world: UUID, member: Int) =
cache[world to member]
?: useDb {
find { (BauweltMemberTable.worldId eq world) and (BauweltMemberTable.memberId eq member) }.firstOrNull()?.also { cache(it) }
}
@JvmStatic
fun getBauMember(world: UUID, member: UUID) =
getBauMember(world, SteamwarUser.get(member)!!.id.value)
@JvmStatic
fun getWorldMembers(world: UUID) =
useDb { useDb {
find { (bauweltId eq SteamwarUser.get(bauwelt)!!.id) and (BauweltMemberTable.memberId eq SteamwarUser.get(member)!!.id) }.firstOrNull()?.also { cache(it) } find { BauweltMemberTable.worldId eq world }.toList().also { it.forEach { cache(it) } }
} }
@JvmStatic @JvmStatic
fun getBauMember(bauwelt: Int, member: Int) = fun getMembers(world: UUID) =
useDb { getWorldMembers(world)
find { (bauweltId eq bauwelt) and (BauweltMemberTable.memberId eq member) }.firstOrNull()?.also { cache(it) }
}
@JvmStatic
@Deprecated("Use getMembers(bauwelt: Int)")
fun getMembers(bauwelt: UUID) =
getMembers(SteamwarUser.get(bauwelt)!!.id.value)
@JvmStatic
fun getMembers(bauwelt: Int) =
useDb {
find { bauweltId eq bauwelt }.toList().also { it.forEach { cache(it) } }
}
} }
val bauweltID by BauweltMemberTable.bauweltId.transform({ EntityID(it, SteamwarUserTable) }, { it.value }) val worldID by BauweltMemberTable.worldId.transform({ EntityID(it, WorldTable) }, { it.value })
val memberID by BauweltMemberTable.memberId.transform({ EntityID(it, SteamwarUserTable) }, { it.value }) val memberID by BauweltMemberTable.memberId.transform({ EntityID(it, SteamwarUserTable) }, { it.value })
private var worldEditInternal by BauweltMemberTable.worldEdit private var worldEditInternal by BauweltMemberTable.worldEdit
var worldEdit: Boolean var worldEdit: Boolean
@@ -86,24 +86,6 @@ class CheckedSchematic(id: EntityID<CompositeID>) : CompositeEntity(id) {
useDb { useDb {
find { (CheckedSchematicTable.nodeOwner eq owner.id) and (CheckedSchematicTable.seen eq false) }.orderBy(CheckedSchematicTable.endTime to SortOrder.DESC).toList() find { (CheckedSchematicTable.nodeOwner eq owner.id) and (CheckedSchematicTable.seen eq false) }.orderBy(CheckedSchematicTable.endTime to SortOrder.DESC).toList()
} }
@JvmStatic
fun countAccepted(owner: SteamwarUser) =
useDb {
find { (CheckedSchematicTable.nodeOwner eq owner.id) and (CheckedSchematicTable.declineReason eq "freigegeben") }.count()
}
@JvmStatic
fun countAccepted(owner: SteamwarUser, type: String) =
useDb {
find { (CheckedSchematicTable.nodeOwner eq owner.id) and (CheckedSchematicTable.declineReason eq "freigegeben") and (CheckedSchematicTable.nodeType like "$type%") }.count()
}
@JvmStatic
fun countChecked(validator: SteamwarUser) =
useDb {
find { CheckedSchematicTable.validator eq validator.id }.count()
}
} }
val node by CheckedSchematicTable.nodeId.transform({ it?.let { EntityID(it, SchematicNodeTable) } }, { it?.value }) val node by CheckedSchematicTable.nodeId.transform({ it?.let { EntityID(it, SchematicNodeTable) } }, { it?.value })
@@ -130,43 +130,6 @@ class EventFight(id: EntityID<Int>) : IntEntity(id), Comparable<EventFight> {
} }
) )
} }
@JvmStatic
fun countEventFights(fighter: SteamwarUser) =
useDb {
exec(
"SELECT COUNT(DISTINCT F.FightID) AS FightCount FROM FightPlayer INNER JOIN Fight F on FightPlayer.FightID = F.FightID INNER JOIN EventFight EF on F.FightID = EF.Fight WHERE UserID = ?",
args = listOf(IntegerColumnType() to fighter.id.value)
) {
if (it.next()) {
it.getLong("FightCount")
} else {
0
}
}
?: 0
}
@JvmStatic
fun countPlacement(fighter: SteamwarUser, placement: Int) =
useDb {
exec(
"""
SELECT COUNT(DISTINCT EventFight.EventID) AS PlacementCount FROM TeamTeilnahme
INNER JOIN EventFight ON EventFight.EventID = TeamTeilnahme.EventID
INNER JOIN FightPlayer ON FightPlayer.FightID = EventFight.Fight
WHERE (IF(FightPlayer.Team = 1, EventFight.TeamBlue, EventFight.TeamRed)) = TeamTeilnahme.TeamID AND UserID = ? AND Placement = ?
""".trimIndent(),
args = listOf(IntegerColumnType() to fighter.id.value, IntegerColumnType() to placement)
) {
if (it.next()) {
it.getInt("PlacementCount")
} else {
0
}
}
?: 0
}
} }
val fightID by EventFightTable.id.transform({ EntityID(it, EventFightTable) }, { it.value }) val fightID by EventFightTable.id.transform({ EntityID(it, EventFightTable) }, { it.value })
@@ -237,7 +200,6 @@ class EventFight(id: EntityID<Int>) : IntEntity(id), Comparable<EventFight> {
override fun delete() = override fun delete() =
useDb { useDb {
EventRelation.deleteRelations(this@EventFight)
super.delete() super.delete()
} }
} }
@@ -159,10 +159,7 @@ class EventGroup(id: EntityID<Int>) : IntEntity(id) {
} }
override fun delete() = override fun delete() =
useDb { useDb { super.delete() }
EventRelation.deleteRelations(this@EventGroup)
super.delete()
}
enum class EventGroupType { enum class EventGroupType {
GROUP_STAGE, GROUP_STAGE,
@@ -24,10 +24,8 @@ import org.jetbrains.exposed.v1.core.and
import org.jetbrains.exposed.v1.core.dao.id.EntityID import org.jetbrains.exposed.v1.core.dao.id.EntityID
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
import org.jetbrains.exposed.v1.core.eq import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.core.or
import org.jetbrains.exposed.v1.dao.IntEntity import org.jetbrains.exposed.v1.dao.IntEntity
import org.jetbrains.exposed.v1.dao.IntEntityClass import org.jetbrains.exposed.v1.dao.IntEntityClass
import org.jetbrains.exposed.v1.jdbc.deleteWhere
import org.jetbrains.exposed.v1.jdbc.select import org.jetbrains.exposed.v1.jdbc.select
object EventRelationTable : IntIdTable("EventRelation") { object EventRelationTable : IntIdTable("EventRelation") {
@@ -61,23 +59,6 @@ class EventRelation(id: EntityID<Int>) : IntEntity(id) {
fun getGroupRelations(group: EventGroup) = fun getGroupRelations(group: EventGroup) =
useDb { find { (EventRelationTable.fromId eq group.id.value) and (EventRelationTable.fromType eq FromType.GROUP) }.toList() } useDb { find { (EventRelationTable.fromId eq group.id.value) and (EventRelationTable.fromType eq FromType.GROUP) }.toList() }
@JvmStatic
fun deleteRelations(fight: EventFight) =
useDb {
EventRelationTable.deleteWhere {
(EventRelationTable.fightId eq fight.id) or
((EventRelationTable.fromId eq fight.id.value) and (EventRelationTable.fromType eq FromType.FIGHT))
}
}
@JvmStatic
fun deleteRelations(group: EventGroup) =
useDb {
EventRelationTable.deleteWhere {
(EventRelationTable.fromId eq group.id.value) and (EventRelationTable.fromType eq FromType.GROUP)
}
}
@JvmStatic @JvmStatic
fun create(fight: EventFight, fightTeam: FightTeam, fromType: FromType, fromId: Int, fromPlace: Int) = fun create(fight: EventFight, fightTeam: FightTeam, fromType: FromType, fromId: Int, fromPlace: Int) =
useDb { useDb {
@@ -20,12 +20,9 @@
package de.steamwar.sql package de.steamwar.sql
import de.steamwar.sql.internal.useDb import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.core.IntegerColumnType
import org.jetbrains.exposed.v1.core.VarCharColumnType
import org.jetbrains.exposed.v1.core.dao.id.CompositeID import org.jetbrains.exposed.v1.core.dao.id.CompositeID
import org.jetbrains.exposed.v1.core.dao.id.CompositeIdTable import org.jetbrains.exposed.v1.core.dao.id.CompositeIdTable
import org.jetbrains.exposed.v1.core.dao.id.EntityID import org.jetbrains.exposed.v1.core.dao.id.EntityID
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.core.inList import org.jetbrains.exposed.v1.core.inList
import org.jetbrains.exposed.v1.dao.CompositeEntity import org.jetbrains.exposed.v1.dao.CompositeEntity
import org.jetbrains.exposed.v1.dao.CompositeEntityClass import org.jetbrains.exposed.v1.dao.CompositeEntityClass
@@ -72,28 +69,6 @@ class FightPlayer(id: EntityID<CompositeID>) : CompositeEntity(id) {
useDb { useDb {
find { FightPlayerTable.fightId inList fightIds.toList() }.toList() find { FightPlayerTable.fightId inList fightIds.toList() }.toList()
} }
@JvmStatic
fun countFights(userId: Int) =
useDb {
find { FightPlayerTable.userId eq userId }.count()
}
@JvmStatic
fun countFights(userId: Int, type: String) =
useDb {
exec(
"SELECT COUNT(*) AS FightCount FROM FightPlayer INNER JOIN Fight F on FightPlayer.FightID = F.FightID WHERE UserID = ? AND GameMode LIKE ?",
args = listOf(IntegerColumnType() to userId, VarCharColumnType() to "$type%")
) {
if (it.next()) {
it.getInt("FightCount")
} else {
0
}
}
?: 0
}
} }
val fightID by FightPlayerTable.fightId.transform({ EntityID(it, FightTable) }, { it.value }) val fightID by FightPlayerTable.fightId.transform({ EntityID(it, FightTable) }, { it.value })
@@ -146,12 +146,12 @@ public final class GameModeConfig<M, W> {
public final List<String> CheckQuestions; public final List<String> CheckQuestions;
/** /**
* The allowed checkers to check this schematic type denoted by a list of SteamwarUser ids. * The allowed checkers to check this schematic type denoted by a list of SteamWar ids.
* The people need the {@link UserPerm#CHECK} to be able to check though. * The people need the {@link UserPerm#CHECK} to be able to check though.
* *
* @implSpec {@code []} by default -> denoting every person with {@link UserPerm#CHECK} can check it * @implSpec {@code []} by default -> denoting every person with {@link UserPerm#CHECK} can check it
*/ */
public final Set<Integer> Checkers; public final List<Integer> Checkers;
/** /**
* Bundle for countdowns during the fight * Bundle for countdowns during the fight
@@ -246,7 +246,7 @@ public final class GameModeConfig<M, W> {
} }
CheckQuestions = loader.getStringList("CheckQuestions"); CheckQuestions = loader.getStringList("CheckQuestions");
Checkers = loader.getIntSet("Checkers"); Checkers = loader.getIntList("Checkers");
Times = new TimesConfig(loader.with("Times")); Times = new TimesConfig(loader.with("Times"));
// Arena would be here to be in config order but needs Schematic.Size and EnterStages loaded afterwards // Arena would be here to be in config order but needs Schematic.Size and EnterStages loaded afterwards
Schematic = new SchematicConfig<>(loader.with("Schematic")); Schematic = new SchematicConfig<>(loader.with("Schematic"));
@@ -488,27 +488,6 @@ public final class GameModeConfig<M, W> {
*/ */
public final boolean NoFloor; public final boolean NoFloor;
/**
* Allows Wind Charges to cross the Middle.
*
* @implSpec {@code false} by default
*/
public final boolean WindchargesCanCrossMiddle;
/**
* Allows Wind Charge interaction with Blocks.
*
* @implSpec {@code true} by default
*/
public final boolean WindchargesInteractWithBlocks;
/**
* Allows Wind Charge to destroy Water.
*
* @implSpec {@code false} by default
*/
public final boolean WindchargesDestroyWater;
private ArenaConfig(YMLWrapper<M, W> loader, SchematicConfig.SizeConfig Size, List<Integer> EnterStages) { private ArenaConfig(YMLWrapper<M, W> loader, SchematicConfig.SizeConfig Size, List<Integer> EnterStages) {
loaded = loader.canLoad(); loaded = loader.canLoad();
WaterDepth = loader.getInt("WaterDepth", 0); WaterDepth = loader.getInt("WaterDepth", 0);
@@ -526,9 +505,6 @@ public final class GameModeConfig<M, W> {
Leaveable = loader.getBoolean("Leaveable", false); Leaveable = loader.getBoolean("Leaveable", false);
AllowMissiles = loader.getBoolean("AllowMissiles", !EnterStages.isEmpty()); AllowMissiles = loader.getBoolean("AllowMissiles", !EnterStages.isEmpty());
NoFloor = loader.getBoolean("NoFloor", false); NoFloor = loader.getBoolean("NoFloor", false);
WindchargesCanCrossMiddle = loader.getBoolean("WindchargesCanCrossMiddle", false);
WindchargesInteractWithBlocks = loader.getBoolean("WindchargesInteractWithBlocks", true);
WindchargesDestroyWater = loader.getBoolean("WindchargesDestroyWater", false);
} }
@ToString @ToString
@@ -688,6 +664,13 @@ public final class GameModeConfig<M, W> {
*/ */
public final int MaxBlocks; public final int MaxBlocks;
/**
* Maximal amount of items per dispenser
*
* @implSpec {@code 128} by default
*/
public final int MaxDispenserItems;
/** /**
* Maximal blast resistance for the blocks * Maximal blast resistance for the blocks
* *
@@ -703,12 +686,10 @@ public final class GameModeConfig<M, W> {
public final double MaxDesignBlastResistance; public final double MaxDesignBlastResistance;
/** /**
* List of limited block materials * List of limited material (combinations)<br/>
* * List contains tags Amount (integer) and Materials (List of material names in Spigot 1.12 AND Spigot 1.15 format)
* @implSpec {@link SQLWrapper#getInventoryMaterials)} are all disallowed by default to have any content but are unlimited in number.
* @implSpec Any Block that has more Blast Resistance than {@link #MaxBlastResistance} will be defaulted to 0.
*/ */
public final List<BlockRule<M>> Limited; public final Map<Set<M>, Integer> Limited;
private SchematicConfig(YMLWrapper<M, ?> loader) { private SchematicConfig(YMLWrapper<M, ?> loader) {
loaded = loader.canLoad(); loaded = loader.canLoad();
@@ -726,140 +707,32 @@ public final class GameModeConfig<M, W> {
IgnorePublicOnly = loader.getBoolean("IgnorePublicOnly", false); IgnorePublicOnly = loader.getBoolean("IgnorePublicOnly", false);
UnlimitedPrepare = loader.getBoolean("UnlimitedPrepare", false); UnlimitedPrepare = loader.getBoolean("UnlimitedPrepare", false);
MaxBlocks = loader.getInt("MaxBlocks", 0); MaxBlocks = loader.getInt("MaxBlocks", 0);
MaxDispenserItems = loader.getInt("MaxDispenserItems", 128);
MaxBlastResistance = loader.getDouble("MaxBlastResistance", Double.MAX_VALUE); MaxBlastResistance = loader.getDouble("MaxBlastResistance", Double.MAX_VALUE);
MaxDesignBlastResistance = loader.getDouble("MaxDesignBlastResistance", MaxBlastResistance); MaxDesignBlastResistance = loader.getDouble("MaxDesignBlastResistance", MaxBlastResistance);
List<BlockRule<M>> limited = new ArrayList<>(); Map<Set<M>, Integer> Limited = new HashMap<>();
Set<M> blocks = new HashSet<>(); for (Map<?, ?> entry : loader.getMapList("Limited")) {
for (YMLWrapper<M, ?> limitedLoader : loader.withAsList("Limited")) { int amount = (Integer) entry.get("Amount");
BlockRule<M> blockRule = new BlockRule<>(limitedLoader); Set<String> materials = new HashSet<>((List<String>) entry.get("Materials"));
blocks.addAll(blockRule.Materials); if (amount == 0) {
limited.add(blockRule); materials.forEach(material -> {
Limited.put(Collections.singleton(loader.materialMapper.apply(material.toUpperCase())), 0);
});
} else {
Limited.put(Collections.unmodifiableSet(materials.stream().map(String::toUpperCase).map(loader.materialMapper).collect(Collectors.toSet())), amount);
}
} }
for (M material : (List<M>) SQLWrapper.impl.getMaterialWithGreaterBlastResistance(MaxBlastResistance)) { SQLWrapper.impl.getMaterialWithGreaterBlastResistance(MaxBlastResistance).forEach(material -> {
if (material == null || !blocks.add(material)) continue; if (Limited.entrySet().stream().anyMatch(entry -> entry.getKey().contains(material))) return;
limited.add(new BlockRule<>(material, 0)); Limited.put(Collections.singleton((M) material), 0);
} });
for (M material : (Set<M>) SQLWrapper.impl.getInventoryMaterials()) { this.Limited = Collections.unmodifiableMap(Limited);
if (material == null || !blocks.add(material)) continue;
limited.add(new BlockRule<>(material, Integer.MAX_VALUE));
}
this.Limited = Collections.unmodifiableList(limited);
this.ReplacementsWithoutBlockUpdates = loader.getMap("ReplacementsWithoutBlockUpdates", loader.materialMapper, loader.materialMapper); this.ReplacementsWithoutBlockUpdates = loader.getMap("ReplacementsWithoutBlockUpdates", loader.materialMapper, loader.materialMapper);
this.ReplacementsWithBlockUpdates = loader.getMap("ReplacementsWithBlockUpdates", loader.materialMapper, loader.materialMapper); this.ReplacementsWithBlockUpdates = loader.getMap("ReplacementsWithBlockUpdates", loader.materialMapper, loader.materialMapper);
} }
public int getMaxCount(M material) {
return Limited.stream()
.filter(rule -> rule.Materials.contains(material))
.mapToInt(rule -> rule.Amount)
.max()
.orElse(Integer.MAX_VALUE);
}
public boolean isInventory(M material) {
return Limited.stream()
.filter(rule -> rule.Materials.contains(material))
.anyMatch(rule -> rule.Content.loaded);
}
public Integer getItemAmount(M material, M item) {
return Limited.stream()
.filter(rule -> rule.Materials.contains(material))
.filter(rule -> rule.Content.Items.contains(item))
.mapToInt(rule -> rule.Content.Amount)
.max()
.orElse(0);
}
@ToString
public static final class BlockRule<M> {
/**
* The block materials this rule applies to
*/
public final Set<M> Materials;
/**
* Maximal amount of Blocks allowed in the schematic
*
* @implSpec {@code 0} by default
*/
public final int Amount;
/**
* The item content filter for Blocks
*
* @implSpec no Items with {@code Amount 0} (denies any item) by default
*/
public final Content<M> Content;
/**
* Resolves one {@code Materials} entry to the concrete materials it denotes: {@code storage}
* for {@link SQLWrapper#getStorageMaterials()}, {@code inventory} for
* {@link SQLWrapper#getInventoryMaterials()}, or the material itself otherwise.
*/
private Set<M> expandInventoryGroup(YMLWrapper<M, ?> loader, String name) {
switch (name.toUpperCase()) {
case "STORAGE":
return (Set<M>) SQLWrapper.impl.getStorageMaterials();
case "INVENTORY":
return (Set<M>) SQLWrapper.impl.getInventoryMaterials();
default:
M material = loader.materialMapper.apply(name.toUpperCase());
return material != null ? Collections.singleton(material) : Collections.emptySet();
}
}
private BlockRule(YMLWrapper<M, ?> loader) {
Set<M> blocks = loader.getStringList("Materials")
.stream()
.flatMap(value -> expandInventoryGroup(loader, value).stream())
.collect(Collectors.toSet());
Materials = Collections.unmodifiableSet(blocks);
Amount = loader.getInt("Amount", 0);
Content = new Content<>(loader.with("Content"));
}
private BlockRule(M block, int amount) {
Materials = Collections.singleton(block);
Amount = amount;
Content = new Content<>();
}
@ToString
public static final class Content<M> {
public final boolean loaded;
/**
* Items allowed by this rule
*/
public final Set<M> Items;
/**
* Maximal amount of an allowed item per single block instance
*
* @implSpec {@code 0} by default
*/
public final int Amount;
private Content(YMLWrapper<M, ?> loader) {
loaded = loader.canLoad();
List<M> items = loader.getMaterialList("Items");
Items = Collections.unmodifiableSet(new HashSet<>(items));
Amount = loader.getInt("Amount", 0);
}
private Content() {
loaded = false;
Items = new HashSet<>();
Amount = 0;
}
}
}
@ToString @ToString
public static final class SizeConfig { public static final class SizeConfig {
@@ -1056,13 +929,6 @@ public final class GameModeConfig<M, W> {
*/ */
public final boolean PersonalKits; public final boolean PersonalKits;
/**
* Maximal blast resistance for the blocks in the kit
*
* @implSpec {@code 9.0} by default
*/
public final double MaxBlastResistance;
/** /**
* Items (Valid spigot material values) that are not allowed in the personal kit * Items (Valid spigot material values) that are not allowed in the personal kit
*/ */
@@ -1074,10 +940,7 @@ public final class GameModeConfig<M, W> {
MemberDefault = loader.getString("MemberDefault", "default"); MemberDefault = loader.getString("MemberDefault", "default");
LeaderDefault = loader.getString("LeaderDefault", "default"); LeaderDefault = loader.getString("LeaderDefault", "default");
PersonalKits = loader.getBoolean("PersonalKits", false); PersonalKits = loader.getBoolean("PersonalKits", false);
MaxBlastResistance = loader.getDouble("MaxBlastResistance", 9.0); ForbiddenItems = loader.getMaterialList("ForbiddenItems");
List forbiddenItems = new ArrayList<>(loader.getMaterialList("ForbiddenItems"));
forbiddenItems.addAll(SQLWrapper.impl.getMaterialWithGreaterBlastResistance(MaxBlastResistance));
ForbiddenItems = Collections.unmodifiableList(forbiddenItems);
} }
} }
@@ -183,6 +183,14 @@ class Punishment(id: EntityID<Int>) : IntEntity(id) {
"UNNOFIGHTSERVER", "UNNOFIGHTSERVER",
UserPerm.MODERATION UserPerm.MODERATION
), ),
NoTeamServer(
"NOTEAMSERVER_TEAM",
"NOTEAMSERVER_PERMA",
"NOTEAMSERVER_UNTIL",
"UNNOTEAMSERVER_ERROR",
"UNNOTEAMSERVER",
UserPerm.MODERATION
),
Note("NOTE_TEAM", null, null, null, null, UserPerm.PUNISHMENTS, true); Note("NOTE_TEAM", null, null, null, null, UserPerm.PUNISHMENTS, true);
fun isMulti() = fun isMulti() =
@@ -24,7 +24,6 @@ import de.steamwar.ImplementationProvider;
import java.io.File; import java.io.File;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set;
public interface SQLWrapper<M> { public interface SQLWrapper<M> {
SQLWrapper<?> impl = ImplementationProvider.getImpl("de.steamwar.sql.SQLWrapperImpl"); SQLWrapper<?> impl = ImplementationProvider.getImpl("de.steamwar.sql.SQLWrapperImpl");
@@ -37,21 +36,5 @@ public interface SQLWrapper<M> {
return Collections.emptyList(); return Collections.emptyList();
} }
/**
* Storage-type inventory materials (chests, barrels, shulker boxes of any color) - the
* {@code storage} group usable in {@code GameModeConfig}'s {@code Limited}.
*/
default Set<M> getStorageMaterials() {
return Collections.emptySet();
}
/**
* Every inventory material - the {@code all} group usable in {@code GameModeConfig}'s
* {@code Limited}. Expected to be a superset of {@link #getStorageMaterials()}.
*/
default Set<M> getInventoryMaterials() {
return Collections.emptySet();
}
void additionalExceptionMetadata(StringBuilder builder); void additionalExceptionMetadata(StringBuilder builder);
} }
@@ -50,6 +50,8 @@ enum class UserPerm {
PUNISHMENTS, PUNISHMENTS,
TICKET_LOG, TICKET_LOG,
BUILD, BUILD,
BUILD_EXTRA_WORLDS,
BUILD_UNLIMITED_WORLDS,
CHECK, CHECK,
MODERATION, MODERATION,
ADMINISTRATION; ADMINISTRATION;
+345
View File
@@ -0,0 +1,345 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 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.sql
import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.core.and
import org.jetbrains.exposed.v1.core.dao.id.EntityID
import org.jetbrains.exposed.v1.core.dao.id.java.UUIDTable
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.core.not
import org.jetbrains.exposed.v1.core.or
import org.jetbrains.exposed.v1.core.SortOrder
import org.jetbrains.exposed.v1.dao.Entity
import org.jetbrains.exposed.v1.dao.EntityClass
import org.jetbrains.exposed.v1.javatime.CurrentTimestamp
import org.jetbrains.exposed.v1.javatime.timestamp
import java.io.File
import java.time.Instant
import java.time.temporal.ChronoUnit
import java.util.UUID
object WorldTable : UUIDTable("world") {
val name = varchar("Name", 512)
val version = integer("Version")
val type = enumeration<WorldType>("Type")
val owner = reference("Owner", SteamwarUserTable).nullable()
val team = reference("Team", TeamTable).nullable()
val lockState = varchar("LockState", 32).nullable()
val deleted = bool("Deleted").default(false)
val created = timestamp("Created").defaultExpression(CurrentTimestamp)
val lastUsed = timestamp("LastUsed").defaultExpression(CurrentTimestamp)
}
enum class WorldType {
BAU,
BUILDER,
TEAM,
ARENA,
TEMPLATE,
}
class SteamwarWorld(id: EntityID<UUID>) : Entity<UUID>(id) {
var name by WorldTable.name
private set
var version by WorldTable.version
private set
var type by WorldTable.type
private set
var owner by WorldTable.owner
private set
var team by WorldTable.team
private set
var lockState by WorldTable.lockState
private set
var deleted by WorldTable.deleted
private set
val created by WorldTable.created
var lastUsed by WorldTable.lastUsed
private set
val archived: Boolean
get() = !storageDirectory.exists() && archiveFile.exists()
val shouldArchive: Boolean
get() = !archived && (deleted || lastUsed.plus(7, ChronoUnit.DAYS).isBefore(Instant.now()))
val uuid: UUID
get() = id.value
val storageDirectory: File
get() = File(WORLD_STORAGE, id.value.toString())
private val archiveFile: File
get() = File(ARCHIVE_WORLD_STORAGE, "${id.value}.zip")
@JvmOverloads
fun setupAndGetStoragePath(prototype: File? = null): String {
if (archived) {
loadFromArchive()
}
val needsInitialization = !storageDirectory.exists() || storageDirectory.list()?.isEmpty() == true
if (needsInitialization) {
if (prototype != null && prototype.exists()) {
prototype.copyRecursively(storageDirectory, overwrite = true)
} else {
storageDirectory.mkdirs()
}
}
useDb {
lastUsed = Instant.now()
}
return storageDirectory.path
}
fun markDeleted() = useDb {
deleted = true
}
fun rename(newName: String) = useDb {
name = newName
}
fun archiveIfNeeded(): Boolean {
if (!shouldArchive) return false
File(ARCHIVE_WORLD_STORAGE).mkdirs()
return archiveWorld() == 0
}
fun changeVersion(newVersion: Int) = useDb {
version = newVersion
}
fun changeLockState(newLockState: String?) = useDb {
lockState = newLockState
}
private fun archiveWorld() = ProcessBuilder("zip", "-u9oymrqq", "$ARCHIVE_WORLD_STORAGE/${id.value}.zip", id.value.toString())
.directory(File(WORLD_STORAGE))
.inheritIO()
.start()
.waitFor()
private fun loadFromArchive() = ProcessBuilder("unzip", "-qq", "-o", "$ARCHIVE_WORLD_STORAGE/${id.value}.zip", "-d", WORLD_STORAGE)
.inheritIO()
.start()
.waitFor()
companion object : EntityClass<UUID, SteamwarWorld>(WorldTable) {
const val WORLD_STORAGE = "/worlds/storage"
const val ARCHIVE_WORLD_STORAGE = "/mnt/storage/worlds/storage"
const val DEFAULT_BAU_WORLD_LIMIT = 3
const val EXTRA_BAU_WORLD_LIMIT = 10
const val TEAM_WORLD_LIMIT = 2
@JvmStatic
fun getWorld(uuid: UUID) = useDb {
findById(uuid)
}
@JvmStatic
fun getBauWorld(user: SteamwarUser, version: Int) = useDb {
find {
(WorldTable.owner eq user.id) and
(WorldTable.version eq version) and
(WorldTable.type eq WorldType.BAU) and
not(WorldTable.deleted)
}.firstOrNull()
}
@JvmStatic
fun getBauWorld(user: SteamwarUser, name: String, version: Int) = useDb {
find {
(WorldTable.owner eq user.id) and
(WorldTable.name eq name) and
(WorldTable.version eq version) and
(WorldTable.type eq WorldType.BAU) and
not(WorldTable.deleted)
}.firstOrNull()
}
@JvmStatic
fun getBauWorlds(user: SteamwarUser) = useDb {
find {
(WorldTable.owner eq user.id) and
(WorldTable.type eq WorldType.BAU) and
not(WorldTable.deleted)
}.orderBy(WorldTable.created to SortOrder.ASC).toList()
}
@JvmStatic
fun countBauWorlds(user: SteamwarUser) = useDb {
find {
(WorldTable.owner eq user.id) and
(WorldTable.type eq WorldType.BAU) and
not(WorldTable.deleted)
}.count()
}
@JvmStatic
@JvmOverloads
fun getOrCreateBauWorld(user: SteamwarUser, version: Int, prototype: File? = null): SteamwarWorld =
getBauWorld(user, version) ?: createWorld(user, user.userName, version, WorldType.BAU, prototype)
@JvmStatic
@JvmOverloads
fun getOrCreateBauWorld(user: SteamwarUser, name: String, version: Int, prototype: File? = null): SteamwarWorld =
getBauWorld(user, name, version) ?: createWorld(user, name, version, WorldType.BAU, prototype)
@JvmStatic
fun getBuilderWorld(name: String, version: Int) = useDb {
find {
(WorldTable.name eq name) and
(WorldTable.version eq version) and
(WorldTable.type eq WorldType.BUILDER) and
not(WorldTable.deleted)
}.firstOrNull()
}
@JvmStatic
fun getBuilderWorlds(version: Int) = useDb {
find {
(WorldTable.version eq version) and
(WorldTable.type eq WorldType.BUILDER) and
not(WorldTable.deleted)
}.toList()
}
@JvmStatic
@JvmOverloads
fun getOrCreateBuilderWorld(name: String, version: Int, prototype: File? = null): SteamwarWorld =
getBuilderWorld(name, version) ?: createWorld(null, name, version, WorldType.BUILDER, prototype)
@JvmStatic
fun getTeamWorld(team: Team, name: String, version: Int) = useDb {
find {
(WorldTable.team eq team.id) and
(WorldTable.name eq name) and
(WorldTable.version eq version) and
(WorldTable.type eq WorldType.TEAM) and
not(WorldTable.deleted)
}.firstOrNull()
}
@JvmStatic
fun getTeamWorlds(team: Team) = useDb {
find {
(WorldTable.team eq team.id) and
(WorldTable.type eq WorldType.TEAM) and
not(WorldTable.deleted)
}.orderBy(WorldTable.created to SortOrder.ASC).toList()
}
@JvmStatic
@JvmOverloads
fun getOrCreateTeamWorld(team: Team, name: String, version: Int, prototype: File? = null): SteamwarWorld =
getTeamWorld(team, name, version) ?: createWorld(null, name, version, WorldType.TEAM, prototype, team)
@JvmStatic
fun countTeamWorlds(team: Team) = useDb {
find {
(WorldTable.team eq team.id) and
(WorldTable.type eq WorldType.TEAM) and
not(WorldTable.deleted)
}.count()
}
@JvmStatic
fun getArenaWorld(mode: String, map: String, version: Int) = useDb {
find {
(WorldTable.name eq arenaWorldName(mode, map)) and
(WorldTable.version eq version) and
(WorldTable.type eq WorldType.ARENA) and
not(WorldTable.deleted)
}.firstOrNull()
}
@JvmStatic
@JvmOverloads
fun getOrCreateArenaWorld(mode: String, map: String, version: Int, prototype: File? = null): SteamwarWorld =
getArenaWorld(mode, map, version) ?: createWorld(null, arenaWorldName(mode, map), version, WorldType.ARENA, prototype)
@JvmStatic
fun getTemplateWorld(name: String, version: Int) = useDb {
find {
(WorldTable.name eq name) and
(WorldTable.version eq version) and
(WorldTable.type eq WorldType.TEMPLATE) and
not(WorldTable.deleted)
}.firstOrNull()
}
@JvmStatic
fun getTemplateWorlds() = useDb {
find {
(WorldTable.type eq WorldType.TEMPLATE) and
not(WorldTable.deleted)
}.orderBy(WorldTable.name to SortOrder.ASC).toList()
}
@JvmStatic
fun getTemplateWorlds(version: Int) = useDb {
find {
(WorldTable.version eq version) and
(WorldTable.type eq WorldType.TEMPLATE) and
not(WorldTable.deleted)
}.orderBy(WorldTable.name to SortOrder.ASC).toList()
}
@JvmStatic
@JvmOverloads
fun getOrCreateTemplateWorld(name: String, version: Int, prototype: File? = null): SteamwarWorld =
getTemplateWorld(name, version) ?: createWorld(null, name, version, WorldType.TEMPLATE, prototype)
@JvmStatic
fun getAllActiveWorlds() = useDb {
find { not(WorldTable.deleted) }.toList()
}
@JvmStatic
fun getWorldsToArchive() = useDb {
find {
not(WorldTable.deleted) or (WorldTable.deleted eq true)
}.toList().filter { it.shouldArchive }
}
private fun arenaWorldName(mode: String, map: String) = "$mode/$map"
@JvmStatic
@JvmOverloads
fun createWorld(user: SteamwarUser?, name: String, version: Int, type: WorldType, prototype: File? = null, team: Team? = null): SteamwarWorld {
val world = useDb { new {
this.name = name
this.version = version
this.type = type
this.owner = user?.id
this.team = team?.id
} }
world.setupAndGetStoragePath(prototype)
return world
}
}
}
@@ -76,25 +76,6 @@ final class YMLWrapper<M, W> {
return new YMLWrapper<>(false, Collections.emptyMap(), materialMapper, winconditionMapper); return new YMLWrapper<>(false, Collections.emptyMap(), materialMapper, winconditionMapper);
} }
public List<YMLWrapper<M, W>> withAsList(String path) {
if (document.containsKey(path)) {
Object value = document.get(path);
if (value instanceof List) {
List<?> list = (List<?>) value;
List<Map> maps = list.stream()
.filter(Map.class::isInstance)
.map(Map.class::cast)
.collect(Collectors.toList());
if (maps.size() == list.size()) {
return (List) maps.stream()
.map(map -> new YMLWrapper(true, map, materialMapper, winconditionMapper))
.collect(Collectors.toList());
}
}
}
return Collections.emptyList();
}
public <T> T get(String path, T defaultValue, Function<Object, T> mapper) { public <T> T get(String path, T defaultValue, Function<Object, T> mapper) {
Object value = this.document.get(path); Object value = this.document.get(path);
if (value == null) return defaultValue; if (value == null) return defaultValue;
@@ -158,12 +139,6 @@ final class YMLWrapper<M, W> {
return get(path, o -> (List<Integer>) o); return get(path, o -> (List<Integer>) o);
} }
public Set<Integer> getIntSet(String path) {
List<Integer> list = get(path, o -> (List<Integer>) o);
if (list.isEmpty()) return Collections.emptySet();
return Collections.unmodifiableSet(new HashSet<>(list));
}
public List<SchematicType> getSchematicTypeList(String path) { public List<SchematicType> getSchematicTypeList(String path) {
List<String> list = getStringList(path); List<String> list = getStringList(path);
if (list.isEmpty()) { if (list.isEmpty()) {
+6 -18
View File
@@ -64,10 +64,6 @@ Arena:
AllowMissiles: false # defaults to true if EnterStages are present otherwise 'false' AllowMissiles: false # defaults to true if EnterStages are present otherwise 'false'
# Denotes that there is no floor for this GameMode # Denotes that there is no floor for this GameMode
NoFloor: false # defaults to false if missing NoFloor: false # defaults to false if missing
# Allows Wind Charges to cross the Middle.
WindchargesCanCrossMiddle: false # defaults to false if missing
# Allows Wind Charge interaction with Blocks.
WindchargesInteractWithBlocks: false # defaults to false if missing
Schematic: Schematic:
# The size of the schematics # The size of the schematics
@@ -107,23 +103,15 @@ Schematic:
UnlimitedPrepare: false # defaults to false if missing UnlimitedPrepare: false # defaults to false if missing
# Maximal amount of blocks allowed in the schematic # Maximal amount of blocks allowed in the schematic
MaxBlocks: 0 # defaults to 0 (ignored) if missing MaxBlocks: 0 # defaults to 0 (ignored) if missing
# Maximal amount of items per dispenser
MaxDispenserItems: 128 # defaults to 128 if missing
# Maximal blast resistance for the design blocks # Maximal blast resistance for the design blocks
MaxDesignBlastResistance: 100000000 # defaults to Double.MAX_VALUE if missing MaxDesignBlastResistance: 100000000 # defaults to Double.MAX_VALUE if missing
# List of limited block materials # List of limited material (combinations)
# InventoryMaterials are all disallowed by default to have any content but are unlimited in number # List contains tags Amount (integer) and Materials (List of material names in Spigot 1.12 AND Spigot 1.15 format)
# Any Block that has more Blast Resistance than MaxBlastResistance will be defaulted to 0.
Limited: Limited:
- Blocks: [ DISPENSER ] - Materials: [ ]
MaxCount: 64 Amount: 0
- Blocks: [ DISPENSER ]
MaxCount: 16
Content:
Items: [ ARROW, FIRE_CHARGE ]
Amount: 128
- Blocks: [ storage ]
Content:
Items: [ TNT ]
Amount: 1728
# The name of the game mode presented to the players # The name of the game mode presented to the players
GameName: WarGear # defaults to WarGear if missing GameName: WarGear # defaults to WarGear if missing
@@ -39,7 +39,6 @@ public enum ArenaMode {
public static final Set<ArenaMode> Test = Collections.unmodifiableSet(EnumSet.of(TEST, CHECK)); public static final Set<ArenaMode> Test = Collections.unmodifiableSet(EnumSet.of(TEST, CHECK));
public static final Set<ArenaMode> Prepare = Collections.unmodifiableSet(EnumSet.of(PREPARE)); public static final Set<ArenaMode> Prepare = Collections.unmodifiableSet(EnumSet.of(PREPARE));
public static final Set<ArenaMode> Replay = Collections.unmodifiableSet(EnumSet.of(REPLAY, TEST)); public static final Set<ArenaMode> Replay = Collections.unmodifiableSet(EnumSet.of(REPLAY, TEST));
public static final Set<ArenaMode> TestOrReferee = Collections.unmodifiableSet(EnumSet.of(TEST, CHECK, EVENT));
public static final Set<ArenaMode> AntiReplay = Collections.unmodifiableSet(EnumSet.complementOf(EnumSet.of(REPLAY))); public static final Set<ArenaMode> AntiReplay = Collections.unmodifiableSet(EnumSet.complementOf(EnumSet.of(REPLAY)));
public static final Set<ArenaMode> AntiTest = Collections.unmodifiableSet(EnumSet.complementOf(EnumSet.of(TEST, CHECK))); public static final Set<ArenaMode> AntiTest = Collections.unmodifiableSet(EnumSet.complementOf(EnumSet.of(TEST, CHECK)));
@@ -21,8 +21,8 @@ package de.steamwar.fightsystem;
import com.comphenix.tinyprotocol.TinyProtocol; import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.core.Core; import de.steamwar.core.Core;
import de.steamwar.core.worldedit.SteamEdit; import de.steamwar.core.WorldEditRendererCUIEditor;
import de.steamwar.core.worldedit.WorldEditRendererCUIEditor; import de.steamwar.fightsystem.commands.TechareaCommand;
import de.steamwar.fightsystem.fight.Fight; import de.steamwar.fightsystem.fight.Fight;
import de.steamwar.fightsystem.fight.FightTeam; import de.steamwar.fightsystem.fight.FightTeam;
import de.steamwar.fightsystem.listener.ClickAnalyzer; import de.steamwar.fightsystem.listener.ClickAnalyzer;
@@ -32,10 +32,7 @@ import de.steamwar.fightsystem.record.GlobalRecorder;
import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.OneShotStateDependent; import de.steamwar.fightsystem.states.OneShotStateDependent;
import de.steamwar.fightsystem.states.StateDependentListener; import de.steamwar.fightsystem.states.StateDependentListener;
import de.steamwar.fightsystem.utils.FightUI; import de.steamwar.fightsystem.utils.*;
import de.steamwar.fightsystem.utils.HullHider;
import de.steamwar.fightsystem.utils.SWSound;
import de.steamwar.fightsystem.utils.TechHiderWrapper;
import de.steamwar.linkage.AbstractLinker; import de.steamwar.linkage.AbstractLinker;
import de.steamwar.linkage.SpigotLinker; import de.steamwar.linkage.SpigotLinker;
import de.steamwar.message.Message; import de.steamwar.message.Message;
@@ -108,7 +105,7 @@ public class FightSystem extends JavaPlugin {
} }
}); });
new OneShotStateDependent(ArenaMode.All, FightState.PreSchemSetup, () -> Fight.playSound(SWSound.BLOCK_NOTE_PLING.getSound(), 100.0f, 2.0f)); new OneShotStateDependent(ArenaMode.All, FightState.PreSchemSetup, () -> Fight.playSound(SWSound.BLOCK_NOTE_PLING.getSound(), 100.0f, 2.0f));
new OneShotStateDependent(ArenaMode.Test, FightState.All, SteamEdit::init); new OneShotStateDependent(ArenaMode.Test, FightState.All, WorldEditRendererCUIEditor::new);
Config.world.setGameRule(GameRule.REDUCED_DEBUG_INFO, ArenaMode.AntiTest.contains(Config.mode)); Config.world.setGameRule(GameRule.REDUCED_DEBUG_INFO, ArenaMode.AntiTest.contains(Config.mode));
hullHider = new HullHider(); hullHider = new HullHider();
@@ -127,6 +124,8 @@ public class FightSystem extends JavaPlugin {
if (checkSchematicNode.isPrepared()) { if (checkSchematicNode.isPrepared()) {
Fight.getRedTeam().setSchem(checkSchematicNode, NodeData.getRevisions(checkSchematicNode) - 1); Fight.getRedTeam().setSchem(checkSchematicNode, NodeData.getRevisions(checkSchematicNode) - 1);
} }
new TechareaCommand();
} else if (Config.mode == ArenaMode.PREPARE) { } else if (Config.mode == ArenaMode.PREPARE) {
Fight.getUnrotated().setSchem(SchematicNode.getSchematicNode(Config.PrepareSchemID)); Fight.getUnrotated().setSchem(SchematicNode.getSchematicNode(Config.PrepareSchemID));
} }
@@ -24,25 +24,35 @@ import de.steamwar.fightsystem.ArenaMode;
import de.steamwar.fightsystem.FightSystem; import de.steamwar.fightsystem.FightSystem;
import de.steamwar.fightsystem.fight.Kit; import de.steamwar.fightsystem.fight.Kit;
import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentCommand;
import de.steamwar.linkage.Linked; import de.steamwar.linkage.Linked;
import de.steamwar.sql.SteamwarUser; import de.steamwar.sql.SteamwarUser;
import de.steamwar.sql.UserPerm; import de.steamwar.sql.UserPerm;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@Linked @Linked
public class AkCommand extends FightSWCommand { public class AkCommand implements CommandExecutor {
public AkCommand() { public AkCommand() {
super(ArenaMode.Test, FightState.All, "ak"); new StateDependentCommand(ArenaMode.Test, FightState.All, "ak", this);
} }
@Override @Override
protected boolean hasPerm(Player player, SteamwarUser user) { public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
return player.isOp() && user.hasPerm(UserPerm.ADMINISTRATION) && Core.getInstance() != FightSystem.getPlugin(); if (!(sender instanceof Player)) {
} return false;
}
Player player = (Player) sender;
if (!player.isOp()) return false;
@Register if (!SteamwarUser.get(player.getUniqueId()).hasPerm(UserPerm.ADMINISTRATION) && Core.getInstance() != FightSystem.getPlugin()) {
public void run(@Validator Player player, String name) { return false;
Kit.createKit(name, player); }
Kit.createKit(args[0], player);
return false;
} }
} }
@@ -1,95 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 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.fightsystem.commands;
import de.steamwar.command.SWCommand;
import de.steamwar.command.TypeValidator;
import de.steamwar.fightsystem.ArenaMode;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependent;
import de.steamwar.sql.SteamwarUser;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerCommandSendEvent;
import java.util.Set;
public abstract class FightSWCommand extends SWCommand implements Listener {
private final String command;
private final String[] aliases;
protected FightSWCommand(Set<ArenaMode> mode, Set<FightState> states, String command) {
super(command);
this.command = command;
this.aliases = new String[0];
stateDependant(mode, states);
}
protected FightSWCommand(Set<ArenaMode> mode, Set<FightState> states, String command, String... aliases) {
super(command, aliases);
this.command = command;
this.aliases = aliases;
stateDependant(mode, states);
}
private void stateDependant(Set<ArenaMode> mode, Set<FightState> states) {
FightSWCommand.this.unregister();
new StateDependent(mode, states) {
@Override
public void enable() {
System.out.println("--- Enable: " + command);
FightSWCommand.this.register();
}
@Override
public void disable() {
System.out.println("--- Disable: " + command);
FightSWCommand.this.unregister();
}
}.register();
}
public static void postFightStateChange() {
for (Player player : Bukkit.getOnlinePlayers()) {
player.updateCommands();
}
}
protected boolean hasPerm(Player player, SteamwarUser user) {
return true;
}
@EventHandler(ignoreCancelled = true)
public final void onPlayerCommandSend(PlayerCommandSendEvent event) {
if (hasPerm(event.getPlayer(), SteamwarUser.get(event.getPlayer().getUniqueId()))) return;
event.getCommands().remove(command);
for (String alias : aliases) {
event.getCommands().remove(alias);
}
}
@ClassValidator(value = Player.class, local = true)
public final TypeValidator<Player> permissionCheck() {
return (sender, value, messageSender) -> hasPerm(value, SteamwarUser.get(value.getUniqueId()));
}
}
@@ -19,28 +19,92 @@
package de.steamwar.fightsystem.commands; package de.steamwar.fightsystem.commands;
import de.steamwar.fightsystem.ArenaMode; import com.google.common.collect.ImmutableList;
import de.steamwar.fightsystem.Config; import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.FightSystem;
import de.steamwar.linkage.Linked; import de.steamwar.linkage.Linked;
import de.steamwar.sql.SteamwarUser; import net.md_5.bungee.api.ChatMessageType;
import org.bukkit.Bukkit;
import org.bukkit.GameMode; import org.bukkit.GameMode;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.defaults.BukkitCommand;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.util.StringUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Linked @Linked
public class GamemodeCommand extends FightSWCommand { public class GamemodeCommand extends BukkitCommand {
private static final List<String> GAMEMODE_NAMES = ImmutableList.of("adventure", "creative", "survival",
"spectator");
public GamemodeCommand() { public GamemodeCommand() {
super(ArenaMode.TestOrReferee, FightState.All, "gamemode", "gm"); super("gamemode");
List<String> aliases = new ArrayList<>();
aliases.add("gm");
this.setAliases(aliases);
Map<String, Command> knownCommands = ((CraftServer) Bukkit.getServer()).getCommandMap().getKnownCommands();
knownCommands.remove("gamemode");
Commands.injectCommand(this);
} }
@Override @Override
protected boolean hasPerm(Player player, SteamwarUser user) { public boolean execute(CommandSender sender, String currentAlias, String[] args) {
return Config.test() || Config.isReferee(player); if (!(sender instanceof Player)) {
return false;
} else if (args.length == 0) {
FightSystem.getMessage().sendPrefixless("GAMEMODE_HELP", sender);
return false;
}
Player p = (Player) sender;
if (!(Config.test() || Config.isReferee(p))) {
FightSystem.getMessage().sendPrefixless("GAMEMODE_NOT_ALLOWED", p, ChatMessageType.ACTION_BAR);
return false;
}
GameMode mode = createMode(args[0]);
if (mode == null) {
FightSystem.getMessage().sendPrefixless("GAMEMODE_UNKNOWN", p, ChatMessageType.ACTION_BAR, args[0]);
return false;
}
p.setGameMode(mode);
return true;
} }
@Register(description = "GAMEMODE_HELP") @SuppressWarnings("deprecation")
public void execute(@Validator Player player, GameMode gameMode) { private GameMode createMode(String modeArg) {
player.setGameMode(gameMode); try {
return GameMode.getByValue(Integer.parseInt(modeArg));
} catch (NumberFormatException ignored) {
if ((modeArg.equalsIgnoreCase("creative")) || (modeArg.equalsIgnoreCase("c"))) {
return GameMode.CREATIVE;
} else if ((modeArg.equalsIgnoreCase("adventure")) || (modeArg.equalsIgnoreCase("a"))) {
return GameMode.ADVENTURE;
} else if ((modeArg.equalsIgnoreCase("spectator")) || (modeArg.equalsIgnoreCase("sp"))) {
return GameMode.SPECTATOR;
} else if ((modeArg.equalsIgnoreCase("survival")) || (modeArg.equalsIgnoreCase("s"))) {
return GameMode.SURVIVAL;
}
}
return null;
} }
@Override
public List<String> tabComplete(CommandSender sender, String alias, String[] args) {
if (args.length == 1) {
return StringUtil.copyPartialMatches(args[0], GAMEMODE_NAMES, new ArrayList<>(GAMEMODE_NAMES.size()));
}
return ImmutableList.of();
}
} }
@@ -20,31 +20,34 @@
package de.steamwar.fightsystem.commands; package de.steamwar.fightsystem.commands;
import de.steamwar.fightsystem.ArenaMode; import de.steamwar.fightsystem.ArenaMode;
import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.FightSystem; import de.steamwar.fightsystem.FightSystem;
import de.steamwar.fightsystem.fight.Fight; import de.steamwar.fightsystem.fight.Fight;
import de.steamwar.fightsystem.fight.FightTeam; import de.steamwar.fightsystem.fight.FightTeam;
import de.steamwar.fightsystem.listener.Check;
import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentCommand;
import de.steamwar.linkage.Linked; import de.steamwar.linkage.Linked;
import de.steamwar.sql.SchematicNode; import de.steamwar.sql.SchematicNode;
import de.steamwar.sql.SteamwarUser; import de.steamwar.sql.SteamwarUser;
import de.steamwar.sql.UserPerm;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@Linked @Linked
public class InfoCommand extends FightSWCommand { public class InfoCommand implements CommandExecutor {
public InfoCommand() { public InfoCommand() {
super(ArenaMode.All, FightState.All, "fightinfo"); new StateDependentCommand(ArenaMode.All, FightState.All, "fightinfo", this);
} }
@Override @Override
protected boolean hasPerm(Player player, SteamwarUser user) { public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
return Check.checkPermission(user, Config.GameModeConfig); if (!(sender instanceof Player)) return false;
}
Player player = (Player) sender;
if (!SteamwarUser.get(player.getUniqueId()).hasPerm(UserPerm.CHECK)) return false;
@Register
public void run(@Validator Player player) {
for (FightTeam team : Fight.teams()) { for (FightTeam team : Fight.teams()) {
if (!team.isLeaderless()) { if (!team.isLeaderless()) {
FightSystem.getMessage().send("INFO_LEADER", player, team.getColoredName(), team.getLeader().getEntity().getName()); FightSystem.getMessage().send("INFO_LEADER", player, team.getColoredName(), team.getLeader().getEntity().getName());
@@ -55,5 +58,6 @@ public class InfoCommand extends FightSWCommand {
FightSystem.getMessage().send("INFO_SCHEMATIC", player, team.getColoredName(), schematic.getName(), SteamwarUser.byId(schematic.getOwner()).getUserName(), schematic.getRank()); FightSystem.getMessage().send("INFO_SCHEMATIC", player, team.getColoredName(), schematic.getName(), SteamwarUser.byId(schematic.getOwner()).getUserName(), schematic.getRank());
} }
} }
return false;
} }
} }
@@ -21,23 +21,32 @@ package de.steamwar.fightsystem.commands;
import de.steamwar.fightsystem.ArenaMode; import de.steamwar.fightsystem.ArenaMode;
import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentCommand;
import de.steamwar.linkage.Linked; import de.steamwar.linkage.Linked;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@Linked @Linked
public class KitCommand extends FightSWCommand { public class KitCommand implements CommandExecutor {
public KitCommand() { public KitCommand() {
super(ArenaMode.AntiReplay, FightState.Setup, "kit"); new StateDependentCommand(ArenaMode.AntiReplay, FightState.Setup, "kit", this);
} }
@Register @Override
public void execute(Player player) { public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
GUI.kitSelection(player, ""); if (!(sender instanceof Player)) {
} return false;
}
Player player = (Player) sender;
@Register if (args.length != 1) {
public void execute(Player player, String query) { // TODO: TabCompletion of Kit Names! GUI.kitSelection(player, "");
GUI.kitSelection(player, query); } else {
Commands.kit(player, args[0]);
}
return false;
} }
} }
@@ -21,18 +21,28 @@ package de.steamwar.fightsystem.commands;
import de.steamwar.fightsystem.ArenaMode; import de.steamwar.fightsystem.ArenaMode;
import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentCommand;
import de.steamwar.linkage.Linked; import de.steamwar.linkage.Linked;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@Linked @Linked
public class LeaveCommand extends FightSWCommand { public class LeaveCommand implements CommandExecutor {
public LeaveCommand() { public LeaveCommand() {
super(ArenaMode.AntiReplay, FightState.Setup, "leave"); new StateDependentCommand(ArenaMode.AntiReplay, FightState.Setup, "leave", this);
} }
@Register @Override
public void execute(Player player) { public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player)) {
return false;
}
Player player = (Player) sender;
Commands.leaveTeam(player); Commands.leaveTeam(player);
return false;
} }
} }
@@ -20,42 +20,52 @@
package de.steamwar.fightsystem.commands; package de.steamwar.fightsystem.commands;
import de.steamwar.fightsystem.ArenaMode; import de.steamwar.fightsystem.ArenaMode;
import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.FightSystem; import de.steamwar.fightsystem.FightSystem;
import de.steamwar.fightsystem.fight.Fight; import de.steamwar.fightsystem.fight.Fight;
import de.steamwar.fightsystem.fight.FightTeam; import de.steamwar.fightsystem.fight.FightTeam;
import de.steamwar.fightsystem.listener.Check;
import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentCommand;
import de.steamwar.linkage.Linked; import de.steamwar.linkage.Linked;
import de.steamwar.sql.SchematicNode; import de.steamwar.sql.SchematicNode;
import de.steamwar.sql.SchematicType; import de.steamwar.sql.SchematicType;
import de.steamwar.sql.SteamwarUser; import de.steamwar.sql.SteamwarUser;
import de.steamwar.sql.UserPerm;
import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.ChatMessageType;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@Linked @Linked
public class LockschemCommand extends FightSWCommand { public class LockschemCommand implements CommandExecutor {
public LockschemCommand() { public LockschemCommand() {
super(ArenaMode.All, FightState.Schem, "lockschem"); new StateDependentCommand(ArenaMode.All, FightState.Schem, "lockschem", this);
} }
@Override @Override
protected boolean hasPerm(Player player, SteamwarUser user) { public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
return Check.checkPermission(user, Config.GameModeConfig); if (!(sender instanceof Player)) return false;
} Player player = (Player) sender;
@Register(description = "LOCKSCHEM_HELP") if (!SteamwarUser.get(player.getUniqueId()).hasPerm(UserPerm.CHECK)) return false;
public void execute(@Validator Player player, String teamName) {
if (args.length != 1) {
FightSystem.getMessage().sendPrefixless("LOCKSCHEM_HELP", player);
return false;
}
String teamName = args[0];
FightTeam fightTeam = Fight.getTeamByName(teamName); FightTeam fightTeam = Fight.getTeamByName(teamName);
if (fightTeam == null) { if (fightTeam == null) {
FightSystem.getMessage().sendPrefixless("UNKNOWN_TEAM", player, ChatMessageType.ACTION_BAR); FightSystem.getMessage().sendPrefixless("UNKNOWN_TEAM", player, ChatMessageType.ACTION_BAR);
return; return false;
} }
SchematicNode.getSchematicNode(fightTeam.getSchematic()).setSchemtype(SchematicType.Normal); SchematicNode.getSchematicNode(fightTeam.getSchematic()).setSchemtype(SchematicType.Normal);
FightSystem.getMessage().sendPrefixless("LOCKSCHEM_LOCKED", player, ChatMessageType.ACTION_BAR); FightSystem.getMessage().sendPrefixless("LOCKSCHEM_LOCKED", player, ChatMessageType.ACTION_BAR);
fightTeam.broadcastSystem("LOCKSCHEM_LOCKED_BY", player.getName()); fightTeam.broadcastSystem("LOCKSCHEM_LOCKED_BY", player.getName());
return false;
} }
} }
@@ -21,18 +21,28 @@ package de.steamwar.fightsystem.commands;
import de.steamwar.fightsystem.ArenaMode; import de.steamwar.fightsystem.ArenaMode;
import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentCommand;
import de.steamwar.linkage.Linked; import de.steamwar.linkage.Linked;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@Linked @Linked
public class ReadyCommand extends FightSWCommand { public class ReadyCommand implements CommandExecutor {
public ReadyCommand() { public ReadyCommand() {
super(ArenaMode.AntiPrepare, FightState.PostSchemSetup, "ready"); new StateDependentCommand(ArenaMode.AntiPrepare, FightState.PostSchemSetup, "ready", this);
} }
@Register @Override
public void execute(Player player) { public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player)) {
return false;
}
Player player = (Player) sender;
Commands.toggleReady(player); Commands.toggleReady(player);
return false;
} }
} }
@@ -20,19 +20,35 @@
package de.steamwar.fightsystem.commands; package de.steamwar.fightsystem.commands;
import de.steamwar.fightsystem.ArenaMode; import de.steamwar.fightsystem.ArenaMode;
import de.steamwar.fightsystem.FightSystem;
import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentCommand;
import de.steamwar.linkage.Linked; import de.steamwar.linkage.Linked;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@Linked @Linked
public class RemoveCommand extends FightSWCommand { public class RemoveCommand implements CommandExecutor {
public RemoveCommand() { public RemoveCommand() {
super(ArenaMode.VariableTeams, FightState.Setup, "remove"); new StateDependentCommand(ArenaMode.VariableTeams, FightState.Setup, "remove", this);
} }
@Register(description = "REMOVE_HELP") @Override
public void execute(Player player, Player teamMember) { public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
Commands.kick(player, teamMember.getName()); if (!(sender instanceof Player)) {
return false;
}
Player player = (Player) sender;
if (args.length != 1) {
FightSystem.getMessage().sendPrefixless("REMOVE_HELP", player);
return false;
}
Commands.kick(player, args[0]);
return false;
} }
} }
@@ -26,28 +26,37 @@ import de.steamwar.fightsystem.fight.FightPlayer;
import de.steamwar.fightsystem.fight.FightTeam; import de.steamwar.fightsystem.fight.FightTeam;
import de.steamwar.fightsystem.fight.JoinRequest; import de.steamwar.fightsystem.fight.JoinRequest;
import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentCommand;
import de.steamwar.linkage.Linked; import de.steamwar.linkage.Linked;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
@Linked @Linked
public class RequestsCommand extends FightSWCommand { public class RequestsCommand implements CommandExecutor {
public RequestsCommand() { public RequestsCommand() {
super(ArenaMode.VariableTeams, FightState.AntiSpectate, "request", "requests"); new StateDependentCommand(ArenaMode.VariableTeams, FightState.AntiSpectate, "request", this);
new StateDependentCommand(ArenaMode.VariableTeams, FightState.AntiSpectate, "requests", this);
} }
@Register @Override
public void execute(Player player) { public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player)) return false;
Player player = (Player) sender;
FightPlayer fp = Fight.getFightPlayer(player); FightPlayer fp = Fight.getFightPlayer(player);
if (fp == null || !(fp.isLeader() || fp.isLiving())) { if (fp == null || !(fp.isLeader() || fp.isLiving())) {
GUI.joinRequest(player); GUI.joinRequest(player);
return; return false;
} }
if (Commands.checkGetLeader(player) == null) return; if (Commands.checkGetLeader(player) == null) return false;
GUI.chooseJoinRequests(player); GUI.chooseJoinRequests(player);
return false;
} }
public static void onJoinRequest(Player player, JoinRequest request, BiConsumer<JoinRequest, FightTeam> handleJoinRequest) { public static void onJoinRequest(Player player, JoinRequest request, BiConsumer<JoinRequest, FightTeam> handleJoinRequest) {
@@ -24,22 +24,32 @@ import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.fight.Fight; import de.steamwar.fightsystem.fight.Fight;
import de.steamwar.fightsystem.record.PacketProcessor; import de.steamwar.fightsystem.record.PacketProcessor;
import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentCommand;
import de.steamwar.linkage.Linked; import de.steamwar.linkage.Linked;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@Linked @Linked
public class SkipCommand extends FightSWCommand { public class SkipCommand implements CommandExecutor {
public SkipCommand() { public SkipCommand() {
super(ArenaMode.AntiPrepare, ArenaMode.Replay.contains(Config.mode) ? FightState.All : FightState.TeamFix, "skip"); new StateDependentCommand(ArenaMode.AntiPrepare, ArenaMode.Replay.contains(Config.mode) ? FightState.All : FightState.TeamFix, "skip", this);
} }
@Register @Override
public void execute(Player player) { public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player)) {
return false;
}
Player player = (Player) sender;
if (PacketProcessor.isReplaying() && player.getUniqueId().equals(Fight.getBlueTeam().getDesignatedLeader())) { if (PacketProcessor.isReplaying() && player.getUniqueId().equals(Fight.getBlueTeam().getDesignatedLeader())) {
PacketProcessor.currentReplay().skipToSubtitle(); PacketProcessor.currentReplay().skipToSubtitle();
} else { } else {
Commands.toggleSkip(player); Commands.toggleSkip(player);
} }
return false;
} }
} }
@@ -21,18 +21,27 @@ package de.steamwar.fightsystem.commands;
import de.steamwar.fightsystem.ArenaMode; import de.steamwar.fightsystem.ArenaMode;
import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentCommand;
import de.steamwar.linkage.Linked; import de.steamwar.linkage.Linked;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@Linked @Linked
public class StateCommand extends FightSWCommand { public class StateCommand implements CommandExecutor {
public StateCommand() { public StateCommand() {
super(ArenaMode.Test, FightState.All, "state"); new StateDependentCommand(ArenaMode.Test, FightState.All, "state", this);
} }
@Register @Override
public void execute(Player player) { public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
GUI.state(player); if (!(sender instanceof Player)) {
return false;
}
GUI.state((Player) sender);
return false;
} }
} }
@@ -22,18 +22,26 @@ package de.steamwar.fightsystem.commands;
import de.steamwar.fightsystem.ArenaMode; import de.steamwar.fightsystem.ArenaMode;
import de.steamwar.fightsystem.fight.Fight; import de.steamwar.fightsystem.fight.Fight;
import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentCommand;
import de.steamwar.linkage.Linked; import de.steamwar.linkage.Linked;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@Linked @Linked
public class TBCommand extends FightSWCommand { public class TBCommand implements CommandExecutor {
public TBCommand() { public TBCommand() {
super(ArenaMode.Check, FightState.All, "resettb"); new StateDependentCommand(ArenaMode.Check, FightState.All, "resettb", this);
} }
@Register @Override
public void execute(Player player) { public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player)) {
return false;
}
Fight.getRedTeam().pasteSchem(); Fight.getRedTeam().pasteSchem();
return false;
} }
} }
@@ -20,20 +20,34 @@ package de.steamwar.fightsystem.commands;
import de.steamwar.fightsystem.ArenaMode; import de.steamwar.fightsystem.ArenaMode;
import de.steamwar.fightsystem.FightSystem; import de.steamwar.fightsystem.FightSystem;
import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentCommand;
import de.steamwar.linkage.Linked; import de.steamwar.linkage.Linked;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import org.bukkit.entity.Player; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
@Linked @Linked
public class TPSWarpCommand extends FightSWCommand { public class TPSWarpCommand implements CommandExecutor {
public TPSWarpCommand() { public TPSWarpCommand() {
super(ArenaMode.Prepare, FightState.PostSchemSetup, "tpswarp", "tpslimit"); new StateDependentCommand(ArenaMode.Prepare, FightState.PostSchemSetup, "tpswarp", this);
new StateDependentCommand(ArenaMode.Prepare, FightState.PostSchemSetup, "tpslimit", this);
} }
@Register(description = "TPSWARP_HELP") @Override
public void execute(Player player, float tps) { public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
float tps;
try {
tps = Float.parseFloat(args[0]);
} catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
FightSystem.getMessage().send("TPSWARP_HELP", sender);
return false;
}
MinecraftServer.getServer().tickRateManager().setTickRate(tps); MinecraftServer.getServer().tickRateManager().setTickRate(tps);
FightSystem.getMessage().broadcastActionbar("TPSWARP_SET", tps); FightSystem.getMessage().broadcastActionbar("TPSWARP_SET", tps);
return false;
} }
} }
@@ -19,39 +19,42 @@
package de.steamwar.fightsystem.commands; package de.steamwar.fightsystem.commands;
import de.steamwar.command.SWCommand;
import de.steamwar.entity.CWireframe; import de.steamwar.entity.CWireframe;
import de.steamwar.entity.REntityServer; import de.steamwar.entity.REntityServer;
import de.steamwar.fightsystem.ArenaMode;
import de.steamwar.fightsystem.Config; import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.FightSystem;
import de.steamwar.fightsystem.states.OneShotStateDependent; import org.bukkit.Bukkit;
import de.steamwar.linkage.Linked;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@Linked import java.util.HashMap;
public class TechareaCommand extends FightSWCommand { import java.util.Map;
private REntityServer entityServer = new REntityServer(); import java.util.UUID;
public class TechareaCommand extends SWCommand {
private final Map<UUID, REntityServer> servers = new HashMap<>();
public TechareaCommand() { public TechareaCommand() {
super(ArenaMode.Check, FightState.All, "techarea"); super("techarea");
new OneShotStateDependent(ArenaMode.Check, FightState.All, () -> {
if (!entityServer.getEntities().isEmpty()) return; Bukkit.getScheduler().runTaskTimer(FightSystem.getPlugin(), () -> servers.forEach((uuid, rEntityServer) -> rEntityServer.tick()), 2, 2);
}
@Register
public void genericCommand(Player player) {
if (servers.containsKey(player.getUniqueId())) {
servers.get(player.getUniqueId()).close();
} else {
REntityServer server = new REntityServer(); REntityServer server = new REntityServer();
CWireframe wireframe = new CWireframe(server); CWireframe wireframe = new CWireframe(server);
wireframe.setPos1(Config.BlueInsetRegion.getMinLocation(Config.world)); wireframe.setPos1(Config.BlueInsetRegion.getMinLocation(Config.world));
wireframe.setPos2(Config.BlueInsetRegion.getMaxLocation(Config.world).subtract(1, 1, 1)); wireframe.setPos2(Config.BlueInsetRegion.getMaxLocation(Config.world).subtract(1, 1, 1));
wireframe.setBlock(Material.RED_CONCRETE.createBlockData()); wireframe.setBlock(Material.RED_CONCRETE.createBlockData());
});
}
@Register server.addPlayer(player);
public void genericCommand(Player player) { servers.put(player.getUniqueId(), server);
if (entityServer.getPlayers().contains(player)) {
entityServer.removePlayer(player);
} else {
entityServer.addPlayer(player);
} }
} }
} }
@@ -25,24 +25,27 @@ import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.FightSystem; import de.steamwar.fightsystem.FightSystem;
import de.steamwar.fightsystem.fight.Fight; import de.steamwar.fightsystem.fight.Fight;
import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentCommand;
import de.steamwar.linkage.Linked; import de.steamwar.linkage.Linked;
import de.steamwar.sql.SWException; import de.steamwar.sql.SWException;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.Arrays; import java.util.Arrays;
import java.util.logging.Level; import java.util.logging.Level;
@Linked @Linked
public class TechhiderbugCommand extends FightSWCommand { public class TechhiderbugCommand implements CommandExecutor {
public TechhiderbugCommand() { public TechhiderbugCommand() {
super(ArenaMode.All, FightState.All, "techhiderbug"); new StateDependentCommand(ArenaMode.All, FightState.All, "techhiderbug", this);
} }
@Register @Override
public void execute(Player player, String... args) { public boolean onCommand(CommandSender sender, Command cmd, String alias, String[] args) {
StringWriter writer = new StringWriter(); StringWriter writer = new StringWriter();
try { try {
@@ -73,6 +76,7 @@ public class TechhiderbugCommand extends FightSWCommand {
Bukkit.getLogger().log(Level.SEVERE, "Error while generating bug report", e); Bukkit.getLogger().log(Level.SEVERE, "Error while generating bug report", e);
} }
SWException.log("Techhider-Bug reported by " + player.getName() + ": " + Arrays.toString(args), writer.toString()); SWException.log("Techhider-Bug reported by " + sender.getName() + ": " + Arrays.toString(args), writer.toString());
return false;
} }
} }
@@ -23,20 +23,28 @@ import de.steamwar.fightsystem.ArenaMode;
import de.steamwar.fightsystem.fight.Fight; import de.steamwar.fightsystem.fight.Fight;
import de.steamwar.fightsystem.fight.FightWorld; import de.steamwar.fightsystem.fight.FightWorld;
import de.steamwar.fightsystem.states.FightState; import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentCommand;
import de.steamwar.linkage.Linked; import de.steamwar.linkage.Linked;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@Linked @Linked
public class WGCommand extends FightSWCommand { public class WGCommand implements CommandExecutor {
public WGCommand() { public WGCommand() {
super(ArenaMode.Check, FightState.All, "resetwg"); new StateDependentCommand(ArenaMode.Check, FightState.All, "resetwg", this);
} }
@Register @Override
public void execute(Player player) { public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player)) {
return false;
}
FightWorld.resetWorld(); FightWorld.resetWorld();
Fight.getBlueTeam().pasteSchem(); Fight.getBlueTeam().pasteSchem();
Fight.getRedTeam().pasteSchem(); Fight.getRedTeam().pasteSchem();
return false;
} }
} }

Some files were not shown because too many files have changed in this diff Show More