Commits vergleichen

..

1 Commits

Autor SHA1 Nachricht Datum
Chaoscaot
662c3cdd9b SchemNode v2 Draft
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-21 19:53:45 +02:00
96 geänderte Dateien mit 1101 neuen und 5127 gelöschten Zeilen

22
build.gradle Ausführbare Datei → Normale Datei
Datei anzeigen

@ -74,29 +74,17 @@ repositories {
} }
dependencies { dependencies {
compileOnly 'org.projectlombok:lombok:1.18.32' compileOnly 'org.projectlombok:lombok:1.18.22'
testCompileOnly 'org.projectlombok:lombok:1.18.32' testCompileOnly 'org.projectlombok:lombok:1.18.22'
annotationProcessor 'org.projectlombok:lombok:1.18.32' annotationProcessor 'org.projectlombok:lombok:1.18.22'
testAnnotationProcessor 'org.projectlombok:lombok:1.18.32' testAnnotationProcessor 'org.projectlombok:lombok:1.18.22'
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
testImplementation 'org.hamcrest:hamcrest:2.2' testImplementation 'org.hamcrest:hamcrest:2.2'
compileOnly 'org.xerial:sqlite-jdbc:3.46.0.0' implementation 'org.xerial:sqlite-jdbc:3.36.0'
} }
task buildResources {
doLast {
File to = new File("${buildDir}/classes/java/main/META-INF/services/javax.annotation.processing.Processor")
to.parentFile.mkdirs()
if (!to.exists()) {
to.createNewFile()
to.append("de.steamwar.linkage.LinkageProcessor\n")
}
}
}
classes.finalizedBy(buildResources)
task buildProject { task buildProject {
description 'Build this project' description 'Build this project'
group "Steamwar" group "Steamwar"

Datei anzeigen

@ -20,28 +20,22 @@
package de.steamwar.command; package de.steamwar.command;
import java.lang.annotation.*; import java.lang.annotation.*;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Parameter; import java.lang.reflect.Parameter;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.BiPredicate; import java.util.function.IntPredicate;
import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
public abstract class AbstractSWCommand<T> { public abstract class AbstractSWCommand<T> {
private static final Map<Class<AbstractSWCommand<?>>, List<AbstractSWCommand<?>>> dependencyMap = new HashMap<>();
private Class<?> clazz; // This is used in createMappings() private Class<?> clazz; // This is used in createMappings()
private boolean initialized = false; private boolean initialized = false;
protected final List<SubCommand<T>> commandList = new ArrayList<>(); protected final List<SubCommand<T>> commandList = new ArrayList<>();
protected final List<SubCommand<T>> helpCommandList = new ArrayList<>(); protected final List<SubCommand<T>> commandHelpList = new ArrayList<>();
private final Map<String, AbstractTypeMapper<T, ?>> localTypeMapper = new HashMap<>(); private final Map<String, AbstractTypeMapper<T, ?>> localTypeMapper = new HashMap<>();
private final Map<String, AbstractValidator<T, ?>> localValidators = new HashMap<>(); private final Map<String, AbstractValidator<T, ?>> localValidators = new HashMap<>();
@ -52,13 +46,6 @@ public abstract class AbstractSWCommand<T> {
protected AbstractSWCommand(Class<T> clazz, String command, String... aliases) { protected AbstractSWCommand(Class<T> clazz, String command, String... aliases) {
this.clazz = clazz; this.clazz = clazz;
PartOf partOf = this.getClass().getAnnotation(PartOf.class);
if (partOf != null) {
dependencyMap.computeIfAbsent((Class<AbstractSWCommand<?>>) partOf.value(), k -> new ArrayList<>()).add(this);
return;
}
createAndSafeCommand(command, aliases); createAndSafeCommand(command, aliases);
unregister(); unregister();
register(); register();
@ -78,22 +65,22 @@ public abstract class AbstractSWCommand<T> {
System.out.println(message.get()); System.out.println(message.get());
} }
protected void sendMessage(T sender, String message, Object[] args) { protected void sendMessage(T sender, String message, Object[] args) {}
}
protected void initialisePartOf(AbstractSWCommand parent) {
}
protected final void execute(T sender, String alias, String[] args) { protected final void execute(T sender, String alias, String[] args) {
initialize(); initialize();
List<Runnable> errors = new ArrayList<>(); List<Runnable> errors = new ArrayList<>();
try { try {
if (commandList.stream().noneMatch(s -> s.invoke(errors::add, sender, alias, args))) { if (!commandList.stream().anyMatch(s -> s.invoke(errors::add, sender, alias, args))) {
errors.forEach(Runnable::run); if (!errors.isEmpty()) {
} else return;
if (errors.isEmpty() && helpCommandList.stream().noneMatch(s -> s.invoke(errors::add, sender, alias, args))) {
errors.forEach(Runnable::run); errors.forEach(Runnable::run);
return;
} }
commandHelpList.stream().anyMatch(s -> s.invoke((ignore) -> {
}, sender, alias, args));
}
} catch (CommandNoHelpException e) {
// Ignored
} catch (CommandFrameworkException e) { } catch (CommandFrameworkException e) {
commandSystemError(sender, e); commandSystemError(sender, e);
throw e; throw e;
@ -103,45 +90,85 @@ public abstract class AbstractSWCommand<T> {
protected final List<String> tabComplete(T sender, String alias, String[] args) throws IllegalArgumentException { protected final List<String> tabComplete(T sender, String alias, String[] args) throws IllegalArgumentException {
initialize(); initialize();
String string = args[args.length - 1].toLowerCase(); String string = args[args.length - 1].toLowerCase();
return Stream.concat(commandList.stream(), helpCommandList.stream()) return commandList.stream()
.filter(s -> !s.noTabComplete) .filter(s -> !s.noTabComplete)
.map(s -> s.tabComplete(sender, args)) .map(s -> s.tabComplete(sender, args))
.filter(Objects::nonNull) .filter(Objects::nonNull)
.flatMap(Collection::stream) .flatMap(Collection::stream)
.filter(s -> !s.isEmpty()) .filter(s -> !s.isEmpty())
.filter(s -> s.toLowerCase().startsWith(string) || string.startsWith(s.toLowerCase())) .filter(s -> s.toLowerCase().startsWith(string))
.distinct() .distinct()
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
private synchronized void initialize() { private void initialize() {
if (initialized) return; if (initialized) return;
List<Method> methods = methods().stream() createMapping();
.filter(this::validateMethod) }
.collect(Collectors.toList());
private synchronized void createMapping() {
List<Method> methods = methods();
for (Method method : methods) { for (Method method : methods) {
Cached cached = method.getAnnotation(Cached.class); Cached cached = method.getAnnotation(Cached.class);
this.<Mapper, AbstractTypeMapper<?, ?>>add(Mapper.class, method, (anno, typeMapper) -> { addMapper(Mapper.class, method, i -> i == 0, false, AbstractTypeMapper.class, (anno, typeMapper) -> {
TabCompletionCache.add(typeMapper, cached); TabCompletionCache.add(typeMapper, cached);
(anno.local() ? ((Map) localTypeMapper) : SWCommandUtils.getMAPPER_FUNCTIONS()).put(anno.value(), typeMapper); if (anno.local()) {
localTypeMapper.putIfAbsent(anno.value(), (AbstractTypeMapper<T, ?>) typeMapper);
} else {
SWCommandUtils.getMAPPER_FUNCTIONS().putIfAbsent(anno.value(), typeMapper);
}
}); });
this.<ClassMapper, AbstractTypeMapper<?, ?>>add(ClassMapper.class, method, (anno, typeMapper) -> { addMapper(ClassMapper.class, method, i -> i == 0, false, AbstractTypeMapper.class, (anno, typeMapper) -> {
TabCompletionCache.add(typeMapper, cached); TabCompletionCache.add(typeMapper, cached);
(anno.local() ? ((Map) localTypeMapper) : SWCommandUtils.getMAPPER_FUNCTIONS()).put(anno.value().getName(), typeMapper); if (anno.local()) {
localTypeMapper.putIfAbsent(anno.value().getTypeName(), (AbstractTypeMapper<T, ?>) typeMapper);
} else {
SWCommandUtils.getMAPPER_FUNCTIONS().putIfAbsent(anno.value().getTypeName(), typeMapper);
}
}); });
this.<Validator, AbstractValidator<?, ?>>add(Validator.class, method, (anno, validator) -> { addValidator(Validator.class, method, i -> i == 0, false, AbstractValidator.class, (anno, validator) -> {
(anno.local() ? ((Map) localValidators) : SWCommandUtils.getVALIDATOR_FUNCTIONS()).put(anno.value(), validator); if (anno.local()) {
localValidators.putIfAbsent(anno.value(), (AbstractValidator<T, ?>) validator);
} else {
SWCommandUtils.getVALIDATOR_FUNCTIONS().putIfAbsent(anno.value(), validator);
}
}); });
this.<ClassValidator, AbstractValidator<?, ?>>add(ClassValidator.class, method, (anno, validator) -> { addValidator(ClassValidator.class, method, i -> i == 0, false, AbstractValidator.class, (anno, validator) -> {
(anno.local() ? ((Map) localValidators) : SWCommandUtils.getVALIDATOR_FUNCTIONS()).put(anno.value().getName(), validator); if (anno.local()) {
localValidators.putIfAbsent(anno.value().getTypeName(), (AbstractValidator<T, ?>) validator);
} else {
SWCommandUtils.getVALIDATOR_FUNCTIONS().putIfAbsent(anno.value().getTypeName(), validator);
}
}); });
} }
for (Method method : methods) { for (Method method : methods) {
add(Register.class, method, true, (anno, parameters) -> { add(Register.class, method, i -> i > 0, true, null, (anno, parameters) -> {
if (!anno.help()) return;
boolean error = false;
if (parameters.length != 2) {
commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking parameters or has too many");
error = true;
}
if (!parameters[parameters.length - 1].isVarArgs()) {
commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking the varArgs parameters as last Argument");
error = true;
}
if (parameters[parameters.length - 1].getType().getComponentType() != String.class) {
commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking the varArgs parameters of type '" + String.class.getTypeName() + "' as last Argument");
error = true;
}
if (error) return;
commandHelpList.add(new SubCommand<>(this, method, anno.value(), new HashMap<>(), new HashMap<>(), true, null, anno.noTabComplete()));
});
add(Register.class, method, i -> i > 0, true, null, (anno, parameters) -> {
if (anno.help()) return;
for (int i = 1; i < parameters.length; i++) { for (int i = 1; i < parameters.length; i++) {
Parameter parameter = parameters[i]; Parameter parameter = parameters[i];
Class<?> clazz = parameter.getType(); Class<?> clazz = parameter.getType();
if (parameter.isVarArgs()) clazz = clazz.getComponentType(); if (parameter.isVarArgs() && i == parameters.length - 1) {
clazz = parameter.getType().getComponentType();
}
Mapper mapper = parameter.getAnnotation(Mapper.class); Mapper mapper = parameter.getAnnotation(Mapper.class);
if (clazz.isEnum() && mapper == null && !SWCommandUtils.getMAPPER_FUNCTIONS().containsKey(clazz.getTypeName())) { if (clazz.isEnum() && mapper == null && !SWCommandUtils.getMAPPER_FUNCTIONS().containsKey(clazz.getTypeName())) {
continue; continue;
@ -152,103 +179,66 @@ public abstract class AbstractSWCommand<T> {
return; return;
} }
} }
commandList.add(new SubCommand<>(this, method, anno.value(), localTypeMapper, localValidators, anno.description(), anno.noTabComplete())); commandList.add(new SubCommand<>(this, method, anno.value(), localTypeMapper, localValidators, false, anno.description(), anno.noTabComplete()));
}); });
} }
if (dependencyMap.containsKey(this.getClass())) { this.commandList.sort((o1, o2) -> {
dependencyMap.get(this.getClass()).forEach(abstractSWCommand -> { int compare = Integer.compare(-o1.subCommand.length, -o2.subCommand.length);
abstractSWCommand.localTypeMapper.putAll((Map) localTypeMapper); if (compare != 0) {
abstractSWCommand.localValidators.putAll((Map) localValidators); return compare;
abstractSWCommand.initialisePartOf(this);
abstractSWCommand.initialize();
commandList.addAll((Collection) abstractSWCommand.commandList);
});
}
Collections.sort(commandList);
commandList.removeIf(subCommand -> {
if (subCommand.isHelp) {
helpCommandList.add(subCommand);
return true;
} else { } else {
return false; return Integer.compare(o1.comparableValue, o2.comparableValue);
}
});
commandHelpList.sort((o1, o2) -> {
int compare = Integer.compare(-o1.subCommand.length, -o2.subCommand.length);
if (compare != 0) {
return compare;
} else {
return Integer.compare(o1.method.getDeclaringClass() == AbstractSWCommand.class ? 1 : 0,
o2.method.getDeclaringClass() == AbstractSWCommand.class ? 1 : 0);
} }
}); });
initialized = true; initialized = true;
} }
private boolean validateMethod(Method method) { private <T extends Annotation> void add(Class<T> annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class<?> returnType, BiConsumer<T, Parameter[]> consumer) {
if (!checkType(method.getAnnotations(), method.getReturnType(), false, annotation -> {
CommandMetaData.Method methodMetaData = annotation.annotationType().getAnnotation(CommandMetaData.Method.class);
if (methodMetaData == null) return (aClass, varArg) -> true;
if (method.getParameterCount() > methodMetaData.maxParameterCount() || method.getParameterCount() < methodMetaData.minParameterCount())
return (aClass, varArg) -> false;
return (aClass, varArg) -> {
Class<?>[] types = methodMetaData.value();
if (types == null) return true;
for (Class<?> type : types) {
if (type.isAssignableFrom(aClass)) return true;
}
return false;
};
}, "The method '" + method + "'")) return false;
boolean valid = true;
for (Parameter parameter : method.getParameters()) {
if (!checkType(parameter.getAnnotations(), parameter.getType(), parameter.isVarArgs(), annotation -> {
CommandMetaData.Parameter parameterMetaData = annotation.annotationType().getAnnotation(CommandMetaData.Parameter.class);
if (parameterMetaData == null) return (aClass, varArg) -> true;
Class<?> handler = parameterMetaData.handler();
if (BiPredicate.class.isAssignableFrom(handler)) {
try {
return (BiPredicate<Class<?>, Boolean>) handler.getConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
NoSuchMethodException e) {
}
}
return (aClass, varArg) -> {
if (varArg) aClass = aClass.getComponentType();
Class<?>[] types = parameterMetaData.value();
if (types == null) return true;
for (Class<?> current : types) {
if (current.isAssignableFrom(aClass)) return true;
}
return false;
};
}, "The parameter '" + parameter + "'")) valid = false;
}
return valid;
}
private boolean checkType(Annotation[] annotations, Class<?> clazz, boolean varArg, Function<Annotation, BiPredicate<Class<?>, Boolean>> toApplicableTypes, String warning) {
boolean valid = true;
for (Annotation annotation : annotations) {
BiPredicate<Class<?>, Boolean> predicate = toApplicableTypes.apply(annotation);
if (!predicate.test(clazz, varArg)) {
commandSystemWarning(() -> warning + " is using an unsupported annotation of type '" + annotation.annotationType().getName() + "'");
valid = false;
}
}
return valid;
}
private <T extends Annotation> void add(Class<T> annotation, Method method, boolean firstParameter, BiConsumer<T, Parameter[]> consumer) {
T[] anno = SWCommandUtils.getAnnotation(method, annotation); T[] anno = SWCommandUtils.getAnnotation(method, annotation);
if (anno == null || anno.length == 0) return; if (anno == null || anno.length == 0) return;
Parameter[] parameters = method.getParameters(); Parameter[] parameters = method.getParameters();
if (!parameterTester.test(parameters.length)) {
commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking parameters or has too many");
return;
}
if (firstParameter && !clazz.isAssignableFrom(parameters[0].getType())) { if (firstParameter && !clazz.isAssignableFrom(parameters[0].getType())) {
commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking the first parameter of type '" + clazz.getTypeName() + "'"); commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking the first parameter of type '" + clazz.getTypeName() + "'");
return; return;
} }
if (returnType != null && !returnType.isAssignableFrom(method.getReturnType())) {
commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking the desired return type '" + returnType.getTypeName() + "'");
return;
}
Arrays.stream(anno).forEach(t -> consumer.accept(t, parameters)); Arrays.stream(anno).forEach(t -> consumer.accept(t, parameters));
} }
private <T extends Annotation, K> void add(Class<T> annotation, Method method, BiConsumer<T, K> consumer) { private <T extends Annotation> void addMapper(Class<T> annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class<?> returnType, BiConsumer<T, AbstractTypeMapper<?, ?>> consumer) {
add(annotation, method, false, (anno, parameters) -> { add(annotation, method, parameterTester, firstParameter, returnType, (anno, parameters) -> {
try { try {
method.setAccessible(true); method.setAccessible(true);
consumer.accept(anno, (K) method.invoke(this)); consumer.accept(anno, (AbstractTypeMapper<T, ?>) method.invoke(this));
} catch (Exception e) {
throw new SecurityException(e.getMessage(), e);
}
});
}
private <T extends Annotation> void addValidator(Class<T> annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class<?> returnType, BiConsumer<T, AbstractValidator<T, ?>> consumer) {
add(annotation, method, parameterTester, firstParameter, returnType, (anno, parameters) -> {
try {
method.setAccessible(true);
consumer.accept(anno, (AbstractValidator<T, ?>) method.invoke(this));
} catch (Exception e) { } catch (Exception e) {
throw new SecurityException(e.getMessage(), e); throw new SecurityException(e.getMessage(), e);
} }
@ -272,29 +262,12 @@ public abstract class AbstractSWCommand<T> {
return methods; return methods;
} }
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface PartOf {
Class<?> value();
}
// --- Annotation for the command ---
/**
* Annotation for registering a method as a command
*/
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD}) @Target({ElementType.METHOD})
@Repeatable(Register.Registeres.class) @Repeatable(Register.Registeres.class)
@CommandMetaData.Method(value = void.class, minParameterCount = 1)
protected @interface Register { protected @interface Register {
/**
* Identifier of subcommand
*/
String[] value() default {}; String[] value() default {};
@Deprecated
boolean help() default false; boolean help() default false;
String[] description() default {}; String[] description() default {};
@ -303,7 +276,6 @@ public abstract class AbstractSWCommand<T> {
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD}) @Target({ElementType.METHOD})
@CommandMetaData.Method(value = void.class, minParameterCount = 1)
@interface Registeres { @interface Registeres {
Register[] value(); Register[] value();
} }
@ -311,41 +283,14 @@ public abstract class AbstractSWCommand<T> {
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.METHOD}) @Target({ElementType.PARAMETER, ElementType.METHOD})
@CommandMetaData.Method(value = AbstractTypeMapper.class, maxParameterCount = 0)
@CommandMetaData.ImplicitTypeMapper(handler = Mapper.Handler.class)
protected @interface Mapper { protected @interface Mapper {
String value(); String value();
boolean local() default false; boolean local() default false;
class Handler<T> implements AbstractTypeMapper<T, Object> {
private AbstractTypeMapper<T, Object> inner;
public Handler(AbstractSWCommand.Mapper mapper, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper) {
inner = (AbstractTypeMapper<T, Object>) SWCommandUtils.getTypeMapper(mapper.value(), localTypeMapper);
}
@Override
public Object map(T sender, PreviousArguments previousArguments, String s) {
return inner.map(sender, previousArguments, s);
}
@Override
public boolean validate(T sender, Object value, MessageSender messageSender) {
return inner.validate(sender, value, messageSender);
}
@Override
public Collection<String> tabCompletes(T sender, PreviousArguments previousArguments, String s) {
return inner.tabCompletes(sender, previousArguments, s);
}
}
} }
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD}) @Target({ElementType.METHOD})
@CommandMetaData.Method(value = AbstractTypeMapper.class, maxParameterCount = 0)
protected @interface ClassMapper { protected @interface ClassMapper {
Class<?> value(); Class<?> value();
@ -354,58 +299,30 @@ public abstract class AbstractSWCommand<T> {
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD}) @Target({ElementType.METHOD})
@CommandMetaData.Method(value = AbstractTypeMapper.class, maxParameterCount = 0)
protected @interface Cached { protected @interface Cached {
long cacheDuration() default 5; long cacheDuration() default 5;
TimeUnit timeUnit() default TimeUnit.SECONDS; TimeUnit timeUnit() default TimeUnit.SECONDS;
boolean global() default false; boolean global() default false;
} }
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.METHOD}) @Target({ElementType.PARAMETER, ElementType.METHOD})
@CommandMetaData.Method(value = AbstractValidator.class, maxParameterCount = 0)
@CommandMetaData.ImplicitValidator(handler = Validator.Handler.class, order = 0)
protected @interface Validator { protected @interface Validator {
String value() default ""; String value() default "";
boolean local() default false; boolean local() default false;
boolean invert() default false;
class Handler<T> implements AbstractValidator<T, Object> {
private AbstractValidator<T, Object> inner;
private boolean invert;
public Handler(AbstractSWCommand.Validator validator, Class<?> clazz, Map<String, AbstractValidator<T, ?>> localValidator) {
inner = (AbstractValidator<T, Object>) SWCommandUtils.getValidator(validator, clazz, localValidator);
invert = validator.invert();
}
@Override
public boolean validate(T sender, Object value, MessageSender messageSender) {
return inner.validate(sender, value, messageSender) ^ invert;
}
}
} }
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD}) @Target({ElementType.METHOD})
@CommandMetaData.Method(value = AbstractValidator.class, maxParameterCount = 0)
protected @interface ClassValidator { protected @interface ClassValidator {
Class<?> value(); Class<?> value();
boolean local() default false; boolean local() default false;
} }
// --- Implicit TypeMapper ---
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER}) @Target({ElementType.PARAMETER})
@CommandMetaData.Parameter({String.class, int.class, Integer.class, long.class, Long.class, boolean.class, Boolean.class})
@CommandMetaData.ImplicitTypeMapper(handler = StaticValue.Handler.class)
protected @interface StaticValue { protected @interface StaticValue {
String[] value(); String[] value();
@ -419,49 +336,7 @@ public abstract class AbstractSWCommand<T> {
*/ */
boolean allowISE() default false; boolean allowISE() default false;
int[] falseValues() default {0}; int[] falseValues() default { 0 };
class Handler<T> implements AbstractTypeMapper<T, Object> {
private AbstractTypeMapper inner;
public Handler(StaticValue staticValue, Class<?> clazz) {
if (clazz == String.class) {
inner = SWCommandUtils.createMapper(staticValue.value());
return;
}
if (!staticValue.allowISE()) {
throw new IllegalArgumentException("The parameter type '" + clazz.getTypeName() + "' is not supported by the StaticValue annotation");
}
if (clazz == boolean.class || clazz == Boolean.class) {
List<String> tabCompletes = new ArrayList<>(Arrays.asList(staticValue.value()));
Set<Integer> falseValues = new HashSet<>();
for (int i : staticValue.falseValues()) falseValues.add(i);
inner = SWCommandUtils.createMapper(s -> {
int index = tabCompletes.indexOf(s);
return index == -1 ? null : !falseValues.contains(index);
}, (commandSender, s) -> tabCompletes);
} else if (clazz == int.class || clazz == Integer.class || clazz == long.class || clazz == Long.class) {
List<String> tabCompletes = new ArrayList<>(Arrays.asList(staticValue.value()));
inner = SWCommandUtils.createMapper(s -> {
Number index = tabCompletes.indexOf(s);
return index.longValue() == -1 ? null : index;
}, (commandSender, s) -> tabCompletes);
} else {
throw new IllegalArgumentException("The parameter type '" + clazz.getTypeName() + "' is not supported by the StaticValue annotation");
}
}
@Override
public Object map(T sender, PreviousArguments previousArguments, String s) {
return inner.map(sender, previousArguments, s);
}
@Override
public Collection<String> tabCompletes(T sender, PreviousArguments previousArguments, String s) {
return inner.tabCompletes(sender, previousArguments, s);
}
}
} }
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@ -478,11 +353,8 @@ public abstract class AbstractSWCommand<T> {
boolean onlyUINIG() default false; boolean onlyUINIG() default false;
} }
// --- Implicit Validator ---
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER}) @Target({ElementType.PARAMETER})
@CommandMetaData.ImplicitValidator(handler = ErrorMessage.Handler.class, order = Integer.MAX_VALUE)
protected @interface ErrorMessage { protected @interface ErrorMessage {
/** /**
* Error message to be displayed when the parameter is invalid. * Error message to be displayed when the parameter is invalid.
@ -493,219 +365,10 @@ public abstract class AbstractSWCommand<T> {
* This is the short form for 'allowEmptyArrays'. * This is the short form for 'allowEmptyArrays'.
*/ */
boolean allowEAs() default true; boolean allowEAs() default true;
class Handler<T> implements AbstractValidator<T, Object> {
private AbstractSWCommand.ErrorMessage errorMessage;
public Handler(AbstractSWCommand.ErrorMessage errorMessage) {
this.errorMessage = errorMessage;
}
@Override
public boolean validate(T sender, Object value, MessageSender messageSender) {
if (value == null) messageSender.send(errorMessage.value());
if (!errorMessage.allowEAs() && value != null && value.getClass().isArray() && Array.getLength(value) == 0) {
messageSender.send(errorMessage.value());
return false;
}
return value != null;
}
}
} }
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER}) @Target({ElementType.PARAMETER})
protected @interface AllowNull { protected @interface AllowNull {
} }
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
@CommandMetaData.Parameter({int.class, Integer.class, long.class, Long.class, float.class, Float.class, double.class, Double.class})
@CommandMetaData.ImplicitValidator(handler = Min.Handler.class, order = 2)
protected @interface Min {
int intValue() default Integer.MIN_VALUE;
long longValue() default Long.MIN_VALUE;
float floatValue() default Float.MIN_VALUE;
double doubleValue() default Double.MIN_VALUE;
boolean inclusive() default true;
class Handler<T> implements AbstractValidator<T, Number> {
private int value;
private Function<Number, Number> comparator;
public Handler(AbstractSWCommand.Min min, Class<?> clazz) {
this.value = min.inclusive() ? 0 : 1;
this.comparator = createComparator("Min", clazz, min.intValue(), min.longValue(), min.floatValue(), min.doubleValue());
}
@Override
public boolean validate(T sender, Number value, MessageSender messageSender) {
if (value == null) return true;
return (comparator.apply(value).intValue()) >= this.value;
}
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
@CommandMetaData.Parameter({int.class, Integer.class, long.class, Long.class, float.class, Float.class, double.class, Double.class})
@CommandMetaData.ImplicitValidator(handler = Max.Handler.class, order = 2)
protected @interface Max {
int intValue() default Integer.MAX_VALUE;
long longValue() default Long.MAX_VALUE;
float floatValue() default Float.MAX_VALUE;
double doubleValue() default Double.MAX_VALUE;
boolean inclusive() default true;
class Handler<T> implements AbstractValidator<T, Number> {
private int value;
private Function<Number, Number> comparator;
public Handler(AbstractSWCommand.Max max, Class<?> clazz) {
this.value = max.inclusive() ? 0 : -1;
this.comparator = createComparator("Max", clazz, max.intValue(), max.longValue(), max.floatValue(), max.doubleValue());
}
@Override
public boolean validate(T sender, Number value, MessageSender messageSender) {
if (value == null) return true;
return (comparator.apply(value).intValue()) <= this.value;
}
}
}
private static Function<Number, Number> createComparator(String type, Class<?> clazz, int iValue, long lValue, float fValue, double dValue) {
if (clazz == int.class || clazz == Integer.class) {
return number -> Integer.compare(number.intValue(), iValue);
} else if (clazz == long.class || clazz == Long.class) {
return number -> Long.compare(number.longValue(), lValue);
} else if (clazz == float.class || clazz == Float.class) {
return number -> Float.compare(number.floatValue(), fValue);
} else if (clazz == double.class || clazz == Double.class) {
return number -> Double.compare(number.doubleValue(), dValue);
} else {
throw new IllegalArgumentException(type + " annotation is not supported for " + clazz);
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
@CommandMetaData.ImplicitTypeMapper(handler = Length.Handler.class)
protected @interface Length {
int min() default 0;
int max() default Integer.MAX_VALUE;
class Handler<T> implements AbstractTypeMapper<T, Object> {
private int min;
private int max;
private AbstractTypeMapper<T, Object> inner;
public Handler(Length length, AbstractTypeMapper<T, Object> inner) {
this.min = length.min();
this.max = length.max();
this.inner = inner;
}
@Override
public Object map(T sender, PreviousArguments previousArguments, String s) {
if (s.length() < min || s.length() > max) return null;
return inner.map(sender, previousArguments, s);
}
@Override
public Collection<String> tabCompletes(T sender, PreviousArguments previousArguments, String s) {
List<String> tabCompletes = inner.tabCompletes(sender, previousArguments, s)
.stream()
.filter(str -> str.length() >= min)
.map(str -> str.substring(0, Math.min(str.length(), max)))
.collect(Collectors.toList());
if (s.length() < min) {
tabCompletes.add(0, s);
}
return tabCompletes;
}
@Override
public String normalize(T sender, String s) {
return inner.normalize(sender, s);
}
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
@CommandMetaData.Parameter(handler = ArrayLength.Type.class)
@CommandMetaData.ImplicitTypeMapper(handler = ArrayLength.HandlerTypeMapper.class)
@CommandMetaData.ImplicitValidator(handler = ArrayLength.HandlerValidator.class, order = 1)
protected @interface ArrayLength {
int min() default 0;
int max() default Integer.MAX_VALUE;
class Type implements BiPredicate<Class<?>, Boolean> {
@Override
public boolean test(Class<?> clazz, Boolean isVarArgs) {
return clazz.isArray();
}
}
class HandlerTypeMapper<T> implements AbstractTypeMapper<T, Object> {
private int max;
private AbstractTypeMapper<T, Object> inner;
public HandlerTypeMapper(ArrayLength arrayLength, AbstractTypeMapper<T, Object> inner) {
this.max = arrayLength.max();
this.inner = inner;
}
@Override
public Object map(T sender, PreviousArguments previousArguments, String s) {
return inner.map(sender, previousArguments, s);
}
@Override
public Collection<String> tabCompletes(T sender, PreviousArguments previousArguments, String s) {
Object[] mapped = previousArguments.getMappedArg(0);
if (mapped.length >= max) return Collections.emptyList();
return inner.tabCompletes(sender, previousArguments, s);
}
@Override
public String normalize(T sender, String s) {
return inner.normalize(sender, s);
}
}
class HandlerValidator<T> implements AbstractValidator<T, Object> {
private int min;
private int max;
public HandlerValidator(ArrayLength arrayLength) {
this.min = arrayLength.min();
this.max = arrayLength.max();
}
@Override
public boolean validate(T sender, Object value, MessageSender messageSender) {
if (value == null) return true;
int length = Array.getLength(value);
return length >= min && length <= max;
}
}
}
} }

Datei anzeigen

@ -25,38 +25,12 @@ public interface AbstractTypeMapper<K, T> extends AbstractValidator<K, T> {
/** /**
* The CommandSender can be null! * The CommandSender can be null!
*/ */
@Deprecated T map(K sender, String[] previousArguments, String s);
default T map(K sender, String[] previousArguments, String s) {
throw new IllegalArgumentException("This method is deprecated and should not be used anymore!");
}
/**
* The CommandSender can be null!
*/
default T map(K sender, PreviousArguments previousArguments, String s) {
return map(sender, previousArguments.userArgs, s);
}
@Override @Override
default boolean validate(K sender, T value, MessageSender messageSender) { default boolean validate(K sender, T value, MessageSender messageSender) {
return true; return true;
} }
@Deprecated Collection<String> tabCompletes(K sender, String[] previousArguments, String s);
default Collection<String> tabCompletes(K sender, String[] previousArguments, String s) {
throw new IllegalArgumentException("This method is deprecated and should not be used anymore!");
}
default Collection<String> tabCompletes(K sender, PreviousArguments previousArguments, String s) {
return tabCompletes(sender, previousArguments.userArgs, s);
}
/**
* Normalize the cache key by sender and user provided argument. <br>
* Note: The CommandSender can be null! <br>
* Returning null and the empty string are equivalent.
*/
default String normalize(K sender, String s) {
return null;
}
} }

Datei anzeigen

@ -38,12 +38,10 @@ public interface AbstractValidator<K, T> {
*/ */
boolean validate(K sender, T value, MessageSender messageSender); boolean validate(K sender, T value, MessageSender messageSender);
@Deprecated
default <C> Validator<C> validate(C value, MessageSender messageSender) { default <C> Validator<C> validate(C value, MessageSender messageSender) {
return new Validator<>(value, messageSender); return new Validator<>(value, messageSender);
} }
@Deprecated
@RequiredArgsConstructor @RequiredArgsConstructor
class Validator<C> { class Validator<C> {
private final C value; private final C value;

Datei anzeigen

@ -36,6 +36,12 @@ public class CommandFrameworkException extends RuntimeException {
private String message; private String message;
static CommandFrameworkException commandGetExceptions(String type, Class<?> clazzType, Executable executable, int index) {
return new CommandFrameworkException(throwable -> {
return CommandFrameworkException.class.getTypeName() + ": Error while getting " + type + " for " + clazzType.getTypeName() + " with parameter index " + index;
}, null, throwable -> Stream.empty(), executable.getDeclaringClass().getTypeName() + "." + executable.getName() + "(Unknown Source)");
}
static CommandFrameworkException commandPartExceptions(String type, Throwable cause, String current, Class<?> clazzType, Executable executable, int index) { static CommandFrameworkException commandPartExceptions(String type, Throwable cause, String current, Class<?> clazzType, Executable executable, int index) {
return new CommandFrameworkException(e -> { return new CommandFrameworkException(e -> {
return CommandFrameworkException.class.getTypeName() + ": Error while " + type + " (" + current + ") to type " + clazzType.getTypeName() + " with parameter index " + index; return CommandFrameworkException.class.getTypeName() + ": Error while " + type + " (" + current + ") to type " + clazzType.getTypeName() + " with parameter index " + index;

Datei anzeigen

@ -1,92 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.command;
import java.lang.annotation.*;
public @interface CommandMetaData {
/**
* This annotation is only for internal use.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
@interface Method {
Class<?>[] value();
int minParameterCount() default 0;
int maxParameterCount() default Integer.MAX_VALUE;
}
/**
* This annotation denotes what types are allowed as parameter types the annotation annotated with can use.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
@interface Parameter {
Class<?>[] value() default {};
Class<?> handler() default void.class;
}
/**
* This annotation can be used in conjunction with a class that implements {@link AbstractTypeMapper} to
* create a custom type mapper for a parameter. The class must have one of two constructors with the following
* types:
* <ul>
* <li>Annotation this annotation annotates</li>
* <li>{@link Class}</li>
* <li>{@link AbstractTypeMapper}, optional, if not present only one per parameter</li>
* <li>{@link java.util.Map} with types {@link String} and {@link AbstractValidator}</li>
* </ul>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
@interface ImplicitTypeMapper {
/**
* The validator class that should be used.
*/
Class<?> handler();
}
/**
* This annotation can be used in conjunction with a class that implements {@link AbstractValidator} to
* create a custom validator short hands for commands. The validator class must have one constructor with
* one of the following types:
* <ul>
* <li>Annotation this annotation annotates</li>
* <li>{@link Class}</li>
* <li>{@link java.util.Map} with types {@link String} and {@link AbstractValidator}</li>
* </ul>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
@interface ImplicitValidator {
/**
* The validator class that should be used.
*/
Class<?> handler();
/**
* Defines when this validator should be processed. Negative numbers denote that this will be
* processed before {@link AbstractSWCommand.Validator} and positive numbers
* denote that this will be processed after {@link AbstractSWCommand.Validator}.
*/
int order();
}
}

Datei anzeigen

@ -1,7 +1,7 @@
/* /*
* This file is a part of the SteamWar software. * This file is a part of the SteamWar software.
* *
* Copyright (C) 2022 SteamWar.de-Serverteam * Copyright (C) 2020 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,9 @@
* 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.linkage.api; package de.steamwar.command;
public interface Plain { class CommandNoHelpException extends RuntimeException {
CommandNoHelpException() {}
} }

Datei anzeigen

@ -24,7 +24,6 @@ import lombok.Setter;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.Parameter; import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -32,8 +31,7 @@ import java.util.function.Consumer;
class CommandPart<T> { class CommandPart<T> {
private static final String[] EMPTY_STRING_ARRAY = new String[0]; private static final String[] EMPTY_ARRAY = new String[0];
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
@AllArgsConstructor @AllArgsConstructor
private static class CheckArgumentResult { private static class CheckArgumentResult {
@ -43,7 +41,7 @@ class CommandPart<T> {
private AbstractSWCommand<T> command; private AbstractSWCommand<T> command;
private AbstractTypeMapper<T, ?> typeMapper; private AbstractTypeMapper<T, ?> typeMapper;
private List<AbstractValidator<T, Object>> validators = new ArrayList<>(); private AbstractValidator<T, Object> validator;
private Class<?> varArgType; private Class<?> varArgType;
private String optional; private String optional;
@ -55,25 +53,22 @@ class CommandPart<T> {
@Setter @Setter
private boolean onlyUseIfNoneIsGiven = false; private boolean onlyUseIfNoneIsGiven = false;
@Setter
private boolean allowNullValues = false;
private Parameter parameter; private Parameter parameter;
private int parameterIndex; private int parameterIndex;
public CommandPart(AbstractSWCommand<T> command, AbstractTypeMapper<T, ?> typeMapper, Class<?> varArgType, String optional, Parameter parameter, int parameterIndex) { public CommandPart(AbstractSWCommand<T> command, AbstractTypeMapper<T, ?> typeMapper, AbstractValidator<T, Object> validator, Class<?> varArgType, String optional, Parameter parameter, int parameterIndex) {
this.command = command; this.command = command;
this.typeMapper = typeMapper; this.typeMapper = typeMapper;
this.validator = validator;
this.varArgType = varArgType; this.varArgType = varArgType;
this.optional = optional; this.optional = optional;
this.parameter = parameter; this.parameter = parameter;
this.parameterIndex = parameterIndex; this.parameterIndex = parameterIndex;
if (optional != null && varArgType != null) { validatePart();
throw new IllegalArgumentException("A vararg part can't have an optional part! In method " + parameter.getDeclaringExecutable() + " with parameter " + parameterIndex);
}
}
void addValidator(AbstractValidator<T, Object> validator) {
if (validator == null) return;
validators.add(validator);
} }
public void setNext(CommandPart<T> next) { public void setNext(CommandPart<T> next) {
@ -83,17 +78,9 @@ class CommandPart<T> {
this.next = next; this.next = next;
} }
public boolean isHelp() { private void validatePart() {
if (next == null) { if (optional != null && varArgType != null) {
if (varArgType == null) { throw new IllegalArgumentException("A vararg part can't have an optional part! In method " + parameter.getDeclaringExecutable() + " with parameter " + parameterIndex);
return false;
}
if (varArgType != String.class) {
return false;
}
return typeMapper == SWCommandUtils.STRING_MAPPER;
} else {
return next.isHelp();
} }
} }
@ -101,29 +88,31 @@ class CommandPart<T> {
if (varArgType != null) { if (varArgType != null) {
Object array = Array.newInstance(varArgType, args.length - startIndex); Object array = Array.newInstance(varArgType, args.length - startIndex);
for (int i = startIndex; i < args.length; i++) { for (int i = startIndex; i < args.length; i++) {
CheckArgumentResult validArgument = checkArgument(null, sender, args, current, i); CheckArgumentResult validArgument = checkArgument(null, sender, args, i);
if (!validArgument.success) throw new CommandParseException(); if (!validArgument.success) {
throw new CommandParseException();
}
Array.set(array, i - startIndex, validArgument.value); Array.set(array, i - startIndex, validArgument.value);
} }
for (AbstractValidator<T, Object> validator : validators) { if (validator != null && !validator.validate(sender, array, (s, objects) -> {
if (!validator.validate(sender, array, (s, objects) -> {
errors.accept(() -> command.sendMessage(sender, s, objects)); errors.accept(() -> command.sendMessage(sender, s, objects));
})) throw new CommandParseException(); })) {
throw new CommandParseException();
} }
current.add(array); current.add(array);
return; return;
} }
CheckArgumentResult validArgument = checkArgument(errors, sender, args, current, startIndex); CheckArgumentResult validArgument = checkArgument(errors, sender, args, startIndex);
if (!validArgument.success && optional == null) { if (!validArgument.success && optional == null) {
throw new CommandParseException(); throw new CommandParseException();
} }
if (!validArgument.success) { if (!validArgument.success) {
if (!ignoreAsArgument) { if (!ignoreAsArgument) {
if (!onlyUseIfNoneIsGiven) { if (!onlyUseIfNoneIsGiven) {
current.add(typeMapper.map(sender, new PreviousArguments(EMPTY_STRING_ARRAY, EMPTY_OBJECT_ARRAY), optional)); current.add(typeMapper.map(sender, EMPTY_ARRAY, optional));
} else if (startIndex >= args.length) { } else if (startIndex >= args.length) {
current.add(typeMapper.map(sender, new PreviousArguments(EMPTY_STRING_ARRAY, EMPTY_OBJECT_ARRAY), optional)); current.add(typeMapper.map(sender, EMPTY_ARRAY, optional));
} else { } else {
throw new CommandParseException(); throw new CommandParseException();
} }
@ -138,23 +127,18 @@ class CommandPart<T> {
} }
if (next != null) { if (next != null) {
next.generateArgumentArray(errors, current, sender, args, startIndex + 1); next.generateArgumentArray(errors, current, sender, args, startIndex + 1);
} else if (startIndex + 1 < args.length) {
throw new CommandParseException();
} }
} }
public void generateTabComplete(List<String> current, T sender, String[] args, List<Object> mappedArgs, int startIndex) { public void generateTabComplete(List<String> current, T sender, String[] args, int startIndex) {
if (varArgType != null) { if (varArgType != null) {
List<Object> currentArgs = new ArrayList<>(mappedArgs);
List<Object> varArgs = new ArrayList<>();
for (int i = startIndex; i < args.length - 1; i++) { for (int i = startIndex; i < args.length - 1; i++) {
CheckArgumentResult validArgument = checkArgument((ignore) -> {}, sender, args, mappedArgs, i); CheckArgumentResult validArgument = checkArgument((ignore) -> {}, sender, args, i);
if (!validArgument.success) return; if (!validArgument.success) {
varArgs.add(validArgument.value); return;
} }
}
currentArgs.add(varArgs.toArray()); Collection<String> strings = tabCompletes(sender, args, args.length - 1);
Collection<String> strings = tabCompletes(sender, args, currentArgs, args.length - 1);
if (strings != null) { if (strings != null) {
current.addAll(strings); current.addAll(strings);
} }
@ -162,65 +146,56 @@ class CommandPart<T> {
} }
if (args.length - 1 > startIndex) { if (args.length - 1 > startIndex) {
CheckArgumentResult checkArgumentResult = checkArgument((ignore) -> {}, sender, args, mappedArgs, startIndex); CheckArgumentResult checkArgumentResult = checkArgument((ignore) -> {}, sender, args, startIndex);
if (checkArgumentResult.success && next != null) { if (checkArgumentResult.success && next != null) {
if (!ignoreAsArgument) { next.generateTabComplete(current, sender, args, startIndex + 1);
mappedArgs.add(checkArgumentResult.value);
}
next.generateTabComplete(current, sender, args, mappedArgs, startIndex + 1);
return; return;
} }
if (optional != null && next != null) { if (optional != null && next != null) {
next.generateTabComplete(current, sender, args, mappedArgs, startIndex); next.generateTabComplete(current, sender, args, startIndex);
} }
return; return;
} }
Collection<String> strings = tabCompletes(sender, args, mappedArgs, startIndex); Collection<String> strings = tabCompletes(sender, args, startIndex);
if (strings != null) { if (strings != null) {
current.addAll(strings); current.addAll(strings);
} }
if (optional != null && next != null) { if (optional != null && next != null) {
next.generateTabComplete(current, sender, args, mappedArgs, startIndex); next.generateTabComplete(current, sender, args, startIndex);
} }
} }
private Collection<String> tabCompletes(T sender, String[] args, List<Object> mappedArgs, int startIndex) { private Collection<String> tabCompletes(T sender, String[] args, int startIndex) {
return TabCompletionCache.tabComplete(sender, args[startIndex], (AbstractTypeMapper<Object, ?>) typeMapper, () -> { return TabCompletionCache.tabComplete(sender, typeMapper, command, () -> {
try { try {
return typeMapper.tabCompletes(sender, new PreviousArguments(Arrays.copyOf(args, startIndex), mappedArgs.toArray()), args[startIndex]); return typeMapper.tabCompletes(sender, Arrays.copyOf(args, startIndex), args[startIndex]);
} catch (Throwable e) { } catch (Throwable e) {
throw CommandFrameworkException.commandPartExceptions("tabcompleting", e, args[startIndex], (varArgType != null ? varArgType : parameter.getType()), parameter.getDeclaringExecutable(), parameterIndex); throw CommandFrameworkException.commandPartExceptions("tabcompleting", e, args[startIndex], (varArgType != null ? varArgType : parameter.getType()), parameter.getDeclaringExecutable(), parameterIndex);
} }
}); });
} }
private CheckArgumentResult checkArgument(Consumer<Runnable> errors, T sender, String[] args, List<Object> mappedArgs, int index) { private CheckArgumentResult checkArgument(Consumer<Runnable> errors, T sender, String[] args, int index) {
Object value; Object value;
try { try {
value = typeMapper.map(sender, new PreviousArguments(Arrays.copyOf(args, index), mappedArgs.toArray()), args[index]); value = typeMapper.map(sender, Arrays.copyOf(args, index), args[index]);
} catch (Exception e) { } catch (Exception e) {
return new CheckArgumentResult(false, null); return new CheckArgumentResult(false, null);
} }
boolean success = true; if (validator != null && errors != null) {
for (AbstractValidator<T, Object> validator : validators) {
try { try {
if (!validator.validate(sender, value, (s, objects) -> { if (!validator.validate(sender, value, (s, objects) -> {
errors.accept(() -> { errors.accept(() -> {
command.sendMessage(sender, s, objects); command.sendMessage(sender, s, objects);
}); });
})) { })) {
success = false; return new CheckArgumentResult(false, null);
value = null;
} }
} catch (Throwable e) { } catch (Throwable e) {
throw CommandFrameworkException.commandPartExceptions("validating", e, args[index], (varArgType != null ? varArgType : parameter.getType()), parameter.getDeclaringExecutable(), parameterIndex); throw CommandFrameworkException.commandPartExceptions("validating", e, args[index], (varArgType != null ? varArgType : parameter.getType()), parameter.getDeclaringExecutable(), parameterIndex);
} }
} }
return new CheckArgumentResult(success, value); return new CheckArgumentResult(allowNullValues || value != null, value);
}
public Class<?> getType() {
return varArgType != null ? varArgType : parameter.getType();
} }
} }

Datei anzeigen

@ -1,62 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.command;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class PreviousArguments {
public final String[] userArgs;
public final Object[] mappedArgs;
public PreviousArguments(String[] userArgs, Object[] mappedArgs) {
this.userArgs = userArgs;
this.mappedArgs = mappedArgs;
}
public String getUserArg(int index) {
return userArgs[userArgs.length - index - 1];
}
public <T> T getMappedArg(int index) {
return (T) mappedArgs[mappedArgs.length - index - 1];
}
public <T> Optional<T> getFirst(Class<T> clazz) {
for (Object o : mappedArgs) {
if (clazz.isInstance(o)) {
return Optional.of((T) o);
}
}
return Optional.empty();
}
public <T> List<T> getAll(Class<T> clazz) {
List<T> list = new ArrayList<>();
for (Object o : mappedArgs) {
if (clazz.isInstance(o)) {
list.add((T) o);
}
}
return list;
}
}

Datei anzeigen

@ -23,6 +23,7 @@ import lombok.Getter;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Parameter; import java.lang.reflect.Parameter;
import java.util.*; import java.util.*;
@ -41,29 +42,27 @@ public class SWCommandUtils {
private SWTypeMapperCreator swTypeMapperCreator = (mapper, tabCompleter) -> new AbstractTypeMapper<Object, Object>() { private SWTypeMapperCreator swTypeMapperCreator = (mapper, tabCompleter) -> new AbstractTypeMapper<Object, Object>() {
@Override @Override
public Object map(Object sender, PreviousArguments previousArguments, String s) { public Object map(Object sender, String[] previousArguments, String s) {
return mapper.apply(s); return mapper.apply(s);
} }
@Override @Override
public Collection<String> tabCompletes(Object sender, PreviousArguments previousArguments, String s) { public Collection<String> tabCompletes(Object sender, String[] previousArguments, String s) {
return ((BiFunction<Object, Object, Collection<String>>) tabCompleter).apply(sender, s); return ((BiFunction<Object, Object, Collection<String>>) tabCompleter).apply(sender, s);
} }
}; };
static final AbstractTypeMapper<Object, String> STRING_MAPPER = createMapper(s -> s, Collections::singletonList);
static { static {
addMapper(boolean.class, Boolean.class, createMapper(s -> { addMapper(boolean.class, Boolean.class, createMapper(s -> {
if (s.equalsIgnoreCase("true")) return true; if (s.equalsIgnoreCase("true")) return true;
if (s.equalsIgnoreCase("false")) return false; if (s.equalsIgnoreCase("false")) return false;
return null; return null;
}, s -> Arrays.asList("true", "false"))); }, s -> Arrays.asList("true", "false")));
addMapper(float.class, Float.class, createMapper(numberMapper(Float::parseFloat), numberCompleter(Float::parseFloat, true))); addMapper(float.class, Float.class, createMapper(numberMapper(Float::parseFloat), numberCompleter(Float::parseFloat)));
addMapper(double.class, Double.class, createMapper(numberMapper(Double::parseDouble), numberCompleter(Double::parseDouble, true))); addMapper(double.class, Double.class, createMapper(numberMapper(Double::parseDouble), numberCompleter(Double::parseDouble)));
addMapper(int.class, Integer.class, createMapper(numberMapper(Integer::parseInt), numberCompleter(Integer::parseInt, false))); addMapper(int.class, Integer.class, createMapper(numberMapper(Integer::parseInt), numberCompleter(Integer::parseInt)));
addMapper(long.class, Long.class, createMapper(numberMapper(Long::parseLong), numberCompleter(Long::parseLong, false))); addMapper(long.class, Long.class, createMapper(numberMapper(Long::parseLong), numberCompleter(Long::parseLong)));
MAPPER_FUNCTIONS.put(String.class.getTypeName(), STRING_MAPPER); MAPPER_FUNCTIONS.put(String.class.getTypeName(), createMapper(s -> s, Collections::singletonList));
} }
public static <T extends AbstractTypeMapper<K, V>, K, V> void init(SWTypeMapperCreator<T, K, V> swTypeMapperCreator) { public static <T extends AbstractTypeMapper<K, V>, K, V> void init(SWTypeMapperCreator<T, K, V> swTypeMapperCreator) {
@ -75,12 +74,40 @@ public class SWCommandUtils {
MAPPER_FUNCTIONS.put(alternativeClazz.getTypeName(), mapper); MAPPER_FUNCTIONS.put(alternativeClazz.getTypeName(), mapper);
} }
public static <T> AbstractTypeMapper<T, ?> getTypeMapper(String name, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper) { static <T> CommandPart<T> generateCommandPart(AbstractSWCommand<T> command, boolean help, String[] subCommand, Parameter[] parameters, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper, Map<String, AbstractValidator<T, ?>> localValidator) {
AbstractTypeMapper<T, ?> typeMapper = localTypeMapper.getOrDefault(name, (AbstractTypeMapper<T, ?>) MAPPER_FUNCTIONS.getOrDefault(name, null)); CommandPart<T> first = null;
if (typeMapper == null) { CommandPart<T> current = null;
throw new IllegalArgumentException("No mapper found for " + name); for (String s : subCommand) {
CommandPart commandPart = new CommandPart(command, createMapper(s), null, null, null, null, -1);
commandPart.setIgnoreAsArgument(true);
if (current != null) {
current.setNext(commandPart);
} }
return typeMapper; current = commandPart;
if (first == null) {
first = current;
}
}
for (int i = 1; i < parameters.length; i++) {
Parameter parameter = parameters[i];
AbstractTypeMapper<T, ?> typeMapper = getTypeMapper(parameter, localTypeMapper);
AbstractValidator<T, Object> validator = (AbstractValidator<T, Object>) getValidator(parameter, localValidator);
Class<?> varArgType = parameter.isVarArgs() ? parameter.getType().getComponentType() : null;
AbstractSWCommand.OptionalValue optionalValue = parameter.getAnnotation(AbstractSWCommand.OptionalValue.class);
AbstractSWCommand.AllowNull allowNull = parameter.getAnnotation(AbstractSWCommand.AllowNull.class);
CommandPart<T> commandPart = new CommandPart<>(command, typeMapper, validator, varArgType, optionalValue != null ? optionalValue.value() : null, parameter, i);
commandPart.setOnlyUseIfNoneIsGiven(optionalValue != null && optionalValue.onlyUINIG());
commandPart.setAllowNullValues(allowNull != null);
if (current != null) {
current.setNext(commandPart);
}
current = commandPart;
if (first == null) {
first = current;
}
}
return first;
} }
public static <T> AbstractTypeMapper<T, ?> getTypeMapper(Parameter parameter, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper) { public static <T> AbstractTypeMapper<T, ?> getTypeMapper(Parameter parameter, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper) {
@ -88,19 +115,90 @@ public class SWCommandUtils {
if (parameter.isVarArgs()) { if (parameter.isVarArgs()) {
clazz = clazz.getComponentType(); clazz = clazz.getComponentType();
} }
if (clazz.isEnum() && !MAPPER_FUNCTIONS.containsKey(clazz.getTypeName()) && !localTypeMapper.containsKey(clazz.getTypeName())) {
AbstractSWCommand.ClassMapper classMapper = parameter.getAnnotation(AbstractSWCommand.ClassMapper.class);
AbstractSWCommand.Mapper mapper = parameter.getAnnotation(AbstractSWCommand.Mapper.class);
if (clazz.isEnum() && classMapper == null && mapper == null && !MAPPER_FUNCTIONS.containsKey(clazz.getTypeName()) && !localTypeMapper.containsKey(clazz.getTypeName())) {
return createEnumMapper((Class<Enum<?>>) clazz); return createEnumMapper((Class<Enum<?>>) clazz);
} }
return getTypeMapper(clazz.getTypeName(), localTypeMapper);
String name = clazz.getTypeName();
if (classMapper != null) {
name = classMapper.value().getTypeName();
} else if (mapper != null) {
name = mapper.value();
} else {
AbstractSWCommand.StaticValue staticValue = parameter.getAnnotation(AbstractSWCommand.StaticValue.class);
if (staticValue != null) {
if (parameter.getType() == String.class) {
return createMapper(staticValue.value());
}
if (staticValue.allowISE()) {
if ((parameter.getType() == boolean.class || parameter.getType() == Boolean.class)) {
List<String> tabCompletes = new ArrayList<>(Arrays.asList(staticValue.value()));
Set<Integer> falseValues = new HashSet<>();
for (int i : staticValue.falseValues()) falseValues.add(i);
return createMapper(s -> {
int index = tabCompletes.indexOf(s);
return index == -1 ? null : !falseValues.contains(index);
}, (commandSender, s) -> tabCompletes);
}
if ((parameter.getType() == int.class || parameter.getType() == Integer.class || parameter.getType() == long.class || parameter.getType() == Long.class) && staticValue.value().length >= 2) {
List<String> tabCompletes = new ArrayList<>(Arrays.asList(staticValue.value()));
return createMapper(s -> {
Number index = tabCompletes.indexOf(s);
return index.longValue() == -1 ? null : index;
}, (commandSender, s) -> tabCompletes);
}
}
}
}
AbstractTypeMapper<T, ?> typeMapper = localTypeMapper.getOrDefault(name, (AbstractTypeMapper<T, ?>) MAPPER_FUNCTIONS.getOrDefault(name, null));
if (typeMapper == null) {
throw new IllegalArgumentException("No mapper found for " + name);
}
return typeMapper;
} }
public static <T> AbstractValidator<T, ?> getValidator(AbstractSWCommand.Validator validator, Class<?> type, Map<String, AbstractValidator<T, ?>> localValidator) { public static <T> AbstractValidator<T, ?> getValidator(Parameter parameter, Map<String, AbstractValidator<T, ?>> localValidator) {
String s = validator.value() != null && !validator.value().isEmpty() ? validator.value() : type.getTypeName(); Class<?> clazz = parameter.getType();
AbstractValidator<T, ?> concreteValidator = localValidator.getOrDefault(s, (AbstractValidator<T, ?>) VALIDATOR_FUNCTIONS.getOrDefault(s, null));
if (concreteValidator == null) { AbstractSWCommand.ClassValidator classValidator = parameter.getAnnotation(AbstractSWCommand.ClassValidator.class);
if (classValidator != null) {
if (classValidator.value() != null) {
return getValidator(classValidator.value().getTypeName(), localValidator);
}
return getValidator(clazz.getTypeName(), localValidator);
}
AbstractSWCommand.Validator validator = parameter.getAnnotation(AbstractSWCommand.Validator.class);
if (validator != null) {
if (validator.value() != null && !validator.value().isEmpty()) {
return getValidator(validator.value(), localValidator);
}
return getValidator(clazz.getTypeName(), localValidator);
}
AbstractSWCommand.ErrorMessage errorMessage = parameter.getAnnotation(AbstractSWCommand.ErrorMessage.class);
if (errorMessage != null) {
return (AbstractValidator<T, Object>) (sender, value, messageSender) -> {
if (value == null) messageSender.send(errorMessage.value());
if (!errorMessage.allowEAs() && value != null && value.getClass().isArray() && Array.getLength(value) == 0) {
messageSender.send(errorMessage.value());
return false;
}
return value != null;
};
}
return null;
}
private static <T> AbstractValidator<T, ?> getValidator(String s, Map<String, AbstractValidator<T, ?>> localGuardChecker) {
AbstractValidator<T, ?> validator = localGuardChecker.getOrDefault(s, (AbstractValidator<T, ?>) VALIDATOR_FUNCTIONS.getOrDefault(s, null));
if (validator == null) {
throw new IllegalArgumentException("No validator found for " + s); throw new IllegalArgumentException("No validator found for " + s);
} }
return concreteValidator; return validator;
} }
public static <K, T> void addMapper(Class<T> clazz, AbstractTypeMapper<K, T> mapper) { public static <K, T> void addMapper(Class<T> clazz, AbstractTypeMapper<K, T> mapper) {
@ -157,25 +255,10 @@ public class SWCommandUtils {
}; };
} }
private static Function<String, Collection<String>> numberCompleter(Function<String, ?> mapper, boolean comma) { private static Function<String, Collection<String>> numberCompleter(Function<String, ?> mapper) {
return s -> { return s -> numberMapper(mapper).apply(s) != null
if (numberMapper(mapper).apply(s) == null) { ? Collections.singletonList(s)
return Collections.emptyList(); : Collections.emptyList();
}
List<String> strings = new ArrayList<>();
if (s.length() == 0) {
strings.add("-");
} else {
strings.add(s);
}
for (int i = 0; i < 10; i++) {
strings.add(s + i);
}
if (comma && (!s.contains(".") || !s.contains(","))) {
strings.add(s + ".");
}
return strings;
};
} }
static <T extends Annotation> T[] getAnnotation(Method method, Class<T> annotation) { static <T extends Annotation> T[] getAnnotation(Method method, Class<T> annotation) {

Datei anzeigen

@ -19,20 +19,17 @@
package de.steamwar.command; package de.steamwar.command;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Parameter; import java.lang.reflect.Parameter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
public class SubCommand<T> implements Comparable<SubCommand<T>> { public class SubCommand<T> {
private AbstractSWCommand<T> abstractSWCommand; private AbstractSWCommand<T> abstractSWCommand;
Method method; Method method;
@ -42,14 +39,11 @@ public class SubCommand<T> implements Comparable<SubCommand<T>> {
private Function<T, ?> senderFunction; private Function<T, ?> senderFunction;
AbstractValidator<T, T> validator; AbstractValidator<T, T> validator;
boolean noTabComplete; boolean noTabComplete;
int comparableValue;
private Parameter[] parameters;
private CommandPart<T> commandPart; private CommandPart<T> commandPart;
boolean isHelp = false; SubCommand(AbstractSWCommand<T> abstractSWCommand, Method method, String[] subCommand, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper, Map<String, AbstractValidator<T, ?>> localValidator, boolean help, String[] description, boolean noTabComplete) {
SubCommand(AbstractSWCommand<T> abstractSWCommand, Method method, String[] subCommand, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper, Map<String, AbstractValidator<T, ?>> localValidator, String[] description, boolean noTabComplete) {
this.abstractSWCommand = abstractSWCommand; this.abstractSWCommand = abstractSWCommand;
this.method = method; this.method = method;
try { try {
@ -61,39 +55,17 @@ public class SubCommand<T> implements Comparable<SubCommand<T>> {
this.description = description; this.description = description;
this.noTabComplete = noTabComplete; this.noTabComplete = noTabComplete;
parameters = method.getParameters(); Parameter[] parameters = method.getParameters();
AbstractSWCommand.Validator validator = parameters[0].getAnnotation(AbstractSWCommand.Validator.class); comparableValue = parameters[parameters.length - 1].isVarArgs() ? Integer.MAX_VALUE : -parameters.length;
if (validator != null) {
this.validator = (AbstractValidator<T, T>) SWCommandUtils.getValidator(validator, parameters[0].getType(), localValidator);
if (validator.invert()) {
AbstractValidator<T, T> current = this.validator;
this.validator = (sender, value, messageSender) -> !current.validate(sender, value, messageSender);
}
}
commandPart = generateCommandPart(abstractSWCommand, subCommand, parameters, localTypeMapper, localValidator); validator = (AbstractValidator<T, T>) SWCommandUtils.getValidator(parameters[0], localValidator);
if (commandPart != null) isHelp = commandPart.isHelp();
commandPart = SWCommandUtils.generateCommandPart(abstractSWCommand, help, subCommand, parameters, localTypeMapper, localValidator);
senderPredicate = t -> parameters[0].getType().isAssignableFrom(t.getClass()); senderPredicate = t -> parameters[0].getType().isAssignableFrom(t.getClass());
senderFunction = t -> parameters[0].getType().cast(t); senderFunction = t -> parameters[0].getType().cast(t);
} }
@Override
public int compareTo(SubCommand<T> o) {
int tLength = parameters.length + subCommand.length;
int oLength = o.parameters.length + o.subCommand.length;
boolean tVarArgs = parameters[parameters.length - 1].isVarArgs();
boolean oVarArgs = o.parameters[o.parameters.length - 1].isVarArgs();
if (tVarArgs) tLength *= -1;
if (oVarArgs) oLength *= -1;
if (tVarArgs && oVarArgs) return Integer.compare(tLength, oLength);
return -Integer.compare(tLength, oLength);
}
boolean invoke(Consumer<Runnable> errors, T sender, String alias, String[] args) { boolean invoke(Consumer<Runnable> errors, T sender, String alias, String[] args) {
try { try {
if (!senderPredicate.test(sender)) { if (!senderPredicate.test(sender)) {
@ -125,7 +97,7 @@ public class SubCommand<T> implements Comparable<SubCommand<T>> {
objects.add(0, senderFunction.apply(sender)); objects.add(0, senderFunction.apply(sender));
method.invoke(abstractSWCommand, objects.toArray()); method.invoke(abstractSWCommand, objects.toArray());
} }
} catch (CommandFrameworkException e) { } catch (CommandNoHelpException | CommandFrameworkException e) {
throw e; throw e;
} catch (CommandParseException e) { } catch (CommandParseException e) {
return false; return false;
@ -138,155 +110,18 @@ public class SubCommand<T> implements Comparable<SubCommand<T>> {
} }
List<String> tabComplete(T sender, String[] args) { List<String> tabComplete(T sender, String[] args) {
if (validator != null && !validator.validate(sender, sender, (s, objects) -> {})) { if (validator != null) {
if (!validator.validate(sender, sender, (s, objects) -> {
// ignore
})) {
return null; return null;
} }
}
if (commandPart == null) { if (commandPart == null) {
return null; return null;
} }
List<String> list = new ArrayList<>(); List<String> list = new ArrayList<>();
commandPart.generateTabComplete(list, sender, args, new ArrayList<>(), 0); commandPart.generateTabComplete(list, sender, args, 0);
return list; return list;
} }
private static <T> CommandPart<T> generateCommandPart(AbstractSWCommand<T> command, String[] subCommand, Parameter[] parameters, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper, Map<String, AbstractValidator<T, ?>> localValidator) {
CommandPart<T> first = null;
CommandPart<T> current = null;
for (String s : subCommand) {
CommandPart commandPart = new CommandPart(command, SWCommandUtils.createMapper(s), null, null, null, -1);
commandPart.addValidator(NULL_FILTER);
commandPart.setIgnoreAsArgument(true);
if (current != null) {
current.setNext(commandPart);
}
current = commandPart;
if (first == null) {
first = current;
}
}
for (int i = 1; i < parameters.length; i++) {
Parameter parameter = parameters[i];
AbstractTypeMapper<T, ?> typeMapper = handleImplicitTypeMapper(parameter, localTypeMapper);
Class<?> varArgType = parameter.isVarArgs() ? parameter.getType().getComponentType() : null;
AbstractSWCommand.OptionalValue optionalValue = parameter.getAnnotation(AbstractSWCommand.OptionalValue.class);
CommandPart<T> commandPart = new CommandPart<>(command, typeMapper, varArgType, optionalValue != null ? optionalValue.value() : null, parameter, i);
commandPart.setOnlyUseIfNoneIsGiven(optionalValue != null && optionalValue.onlyUINIG());
handleImplicitTypeValidator(parameter, commandPart, localValidator);
if (parameter.getAnnotation(AbstractSWCommand.AllowNull.class) == null) {
commandPart.addValidator((AbstractValidator<T, Object>) NULL_FILTER);
}
if (current != null) {
current.setNext(commandPart);
}
current = commandPart;
if (first == null) {
first = current;
}
}
return first;
}
private static <T> AbstractTypeMapper<T, Object> handleImplicitTypeMapper(Parameter parameter, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper) {
Class<?> type = parameter.getType();
if (parameter.isVarArgs()) {
type = type.getComponentType();
}
Annotation[] annotations = parameter.getAnnotations();
Constructor<?> sourceConstructor = null;
Annotation sourceAnnotation = null;
List<Constructor<?>> parentConstructors = new ArrayList<>();
List<Annotation> parentAnnotations = new ArrayList<>();
for (Annotation annotation : annotations) {
CommandMetaData.ImplicitTypeMapper implicitTypeMapper = annotation.annotationType().getAnnotation(CommandMetaData.ImplicitTypeMapper.class);
if (implicitTypeMapper == null) continue;
Class<?> clazz = implicitTypeMapper.handler();
if (!AbstractTypeMapper.class.isAssignableFrom(clazz)) continue;
Constructor<?>[] constructors = clazz.getConstructors();
if (constructors.length != 1) continue;
Constructor<?> constructor = constructors[0];
if (needsTypeMapper(constructor)) {
parentConstructors.add(constructor);
parentAnnotations.add(annotation);
} else {
if (sourceAnnotation != null) {
throw new IllegalArgumentException("Multiple source type mappers found for parameter " + parameter);
}
sourceConstructor = constructor;
sourceAnnotation = annotation;
}
}
AbstractTypeMapper<T, Object> current;
if (sourceAnnotation != null) {
current = createInstance(sourceConstructor, sourceAnnotation, type, localTypeMapper);
} else {
current = (AbstractTypeMapper<T, Object>) SWCommandUtils.getTypeMapper(parameter, localTypeMapper);
}
for (int i = 0; i < parentConstructors.size(); i++) {
Constructor<?> constructor = parentConstructors.get(i);
Annotation annotation = parentAnnotations.get(i);
current = createInstance(constructor, annotation, type, localTypeMapper, current);
}
return current;
}
private static boolean needsTypeMapper(Constructor<?> constructor) {
Class<?>[] parameterTypes = constructor.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
if (AbstractTypeMapper.class.isAssignableFrom(parameterType)) {
return true;
}
}
return false;
}
private static <T> void handleImplicitTypeValidator(Parameter parameter, CommandPart<T> commandPart, Map<String, AbstractValidator<T, ?>> localValidator) {
Annotation[] annotations = parameter.getAnnotations();
Map<Integer, List<AbstractValidator<T, Object>>> validators = new HashMap<>();
for (Annotation annotation : annotations) {
CommandMetaData.ImplicitValidator implicitValidator = annotation.annotationType().getAnnotation(CommandMetaData.ImplicitValidator.class);
if (implicitValidator == null) continue;
Class<?> clazz = implicitValidator.handler();
if (!AbstractValidator.class.isAssignableFrom(clazz)) continue;
Constructor<?>[] constructors = clazz.getConstructors();
if (constructors.length != 1) continue;
AbstractValidator<T, Object> validator = createInstance(constructors[0], annotation, commandPart.getType(), localValidator);
validators.computeIfAbsent(implicitValidator.order(), integer -> new ArrayList<>()).add(validator);
}
List<Integer> keys = new ArrayList<>(validators.keySet());
keys.sort(Integer::compareTo);
for (Integer key : keys) {
List<AbstractValidator<T, Object>> list = validators.get(key);
for (AbstractValidator<T, Object> validator : list) {
commandPart.addValidator(validator);
}
}
}
private static <T> T createInstance(Constructor<?> constructor, Object... parameter) {
Class<?>[] types = constructor.getParameterTypes();
List<Object> objects = new ArrayList<>();
for (Class<?> clazz : types) {
boolean found = false;
for (Object o : parameter) {
if (clazz.isAssignableFrom(o.getClass())) {
objects.add(o);
found = true;
break;
}
}
if (!found) {
throw new RuntimeException("Could not find type " + clazz + " for constructor " + constructor);
}
}
try {
return (T) constructor.newInstance(objects.toArray());
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
private static final AbstractValidator<?, Object> NULL_FILTER = (sender, value, messageSender) -> value != null;
} }

Datei anzeigen

@ -51,29 +51,25 @@ public class TabCompletionCache {
@AllArgsConstructor @AllArgsConstructor
private static class Key { private static class Key {
private Object sender; private Object sender;
private String arg;
private AbstractTypeMapper<?, ?> typeMapper; private AbstractTypeMapper<?, ?> typeMapper;
} }
@AllArgsConstructor @AllArgsConstructor
private static class TabCompletions { private static class TabCompletions {
private AbstractSWCommand<?> command;
private long timestamp; private long timestamp;
private Collection<String> tabCompletions; private Collection<String> tabCompletions;
} }
Collection<String> tabComplete(Object sender, String arg, AbstractTypeMapper<Object, ?> typeMapper, Supplier<Collection<String>> tabCompleteSupplier) { Collection<String> tabComplete(Object sender, AbstractTypeMapper<?, ?> typeMapper, AbstractSWCommand<?> command, Supplier<Collection<String>> tabCompleteSupplier) {
if (!cached.contains(typeMapper)) return tabCompleteSupplier.get(); if (!cached.contains(typeMapper)) return tabCompleteSupplier.get();
Key key = global.contains(typeMapper) ? new Key(null, typeMapper) : new Key(sender, typeMapper);
String normalizedArg = typeMapper.normalize(sender, arg);
if (normalizedArg == null) normalizedArg = "";
Key key = new Key(global.contains(typeMapper) ? null : sender, normalizedArg, typeMapper);
TabCompletions tabCompletions = tabCompletionCache.computeIfAbsent(key, ignore -> { TabCompletions tabCompletions = tabCompletionCache.computeIfAbsent(key, ignore -> {
return new TabCompletions(System.currentTimeMillis(), tabCompleteSupplier.get()); return new TabCompletions(command, System.currentTimeMillis(), tabCompleteSupplier.get());
}); });
if (tabCompletions.command != command || System.currentTimeMillis() - tabCompletions.timestamp > cacheDuration.get(typeMapper)) {
if (System.currentTimeMillis() - tabCompletions.timestamp > cacheDuration.get(typeMapper)) { tabCompletions = new TabCompletions(command, System.currentTimeMillis(), tabCompleteSupplier.get());
tabCompletions.tabCompletions = tabCompleteSupplier.get(); tabCompletionCache.put(key, tabCompletions);
} }
tabCompletions.timestamp = System.currentTimeMillis(); tabCompletions.timestamp = System.currentTimeMillis();
return tabCompletions.tabCompletions; return tabCompletions.tabCompletions;

Datei anzeigen

@ -1,36 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.linkage;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface AllowedContexts {
/**
* The context in which this annotation is valid.
*/
Context[] value();
}

Datei anzeigen

@ -1,25 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.linkage;
public enum Context {
BUNGEE,
SPIGOT
}

Datei anzeigen

@ -1,44 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.linkage;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@AllowedContexts(Context.BUNGEE)
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
public @interface EventMode {
Mode value();
@AllArgsConstructor
enum Mode {
EventOnly(""),
NonEvent("!");
@Getter
private String prefix;
}
}

Datei anzeigen

@ -1,352 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.linkage;
import de.steamwar.linkage.plan.BuildPlan;
import de.steamwar.linkage.plan.FieldBuilder;
import de.steamwar.linkage.plan.MethodBuilder;
import de.steamwar.linkage.types.Plain_GENERIC;
import lombok.Cleanup;
import lombok.Getter;
import lombok.SneakyThrows;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import javax.tools.StandardLocation;
import java.io.*;
import java.lang.annotation.Annotation;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@SupportedAnnotationTypes("de.steamwar.linkage.Linked")
public class LinkageProcessor extends AbstractProcessor {
private static Context context;
@Getter
private static String pluginMain;
private String name;
private String className;
private Set<String> disabledFeatures = new HashSet<>();
private Messager messager;
private boolean processed = false;
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@SneakyThrows
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
name = new File(System.getProperty("user.dir")).getName().replaceAll("[^a-zA-Z]", "").toLowerCase();
messager = processingEnv.getMessager();
className = "LinkageUtils";
mainClass();
disabledFeatures();
}
@SneakyThrows
private void mainClass() {
File file = new File(System.getProperty("user.dir"));
Optional<File> pluginYMLFile = Files.walk(file.toPath())
.map(Path::toFile)
.filter(File::isFile)
.filter(f -> f.getName().equals("plugin.yml") || f.getName().equals("bungee.yml"))
.findFirst();
if (!pluginYMLFile.isPresent()) {
messager.printMessage(Diagnostic.Kind.ERROR, "Could not find plugin.yml or bungee.yml");
return;
}
context = pluginYMLFile.get().getName().equals("bungee.yml") ? Context.BUNGEE : Context.SPIGOT;
@Cleanup BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(pluginYMLFile.get())));
Optional<String> mainName = reader.lines()
.filter(line -> line.startsWith("main:"))
.map(line -> line.substring(line.indexOf(':') + 1).trim())
.findFirst();
if (mainName.isPresent()) {
pluginMain = mainName.get();
} else {
messager.printMessage(Diagnostic.Kind.ERROR, "Could not find main class in plugin.yml or bungee.yml");
}
}
@SneakyThrows
private void disabledFeatures() {
File file = new File(System.getProperty("user.dir"), "disabled-features.txt");
if (!file.exists()) return;
@Cleanup BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
reader.lines()
.map(String::trim)
.filter(line -> !line.isEmpty())
.filter(line -> !line.startsWith("#"))
.forEach(disabledFeatures::add);
}
@SneakyThrows
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (processed) return false;
processed = true;
Writer writer = processingEnv.getFiler().createSourceFile("de.steamwar." + name + ".linkage.LinkageUtils").openWriter();
BuildPlan buildPlan = new BuildPlan("de.steamwar." + name + ".linkage", className);
Set<TypeElement> disabledElements = new HashSet<>();
Set<TypeElement> elements = roundEnv.getElementsAnnotatedWith(Linked.class).stream()
.filter(element -> element.getKind() == ElementKind.CLASS)
.map(TypeElement.class::cast)
.peek(element -> {
String featureName = element.getAnnotation(Linked.class).feature();
if (featureName.isEmpty()) {
String tempName = element.getQualifiedName().toString();
if (tempName.contains(".features.")) {
tempName = tempName.substring(tempName.indexOf(".features.") + 10);
featureName = tempName.substring(0, tempName.indexOf('.'));
} else {
tempName = tempName.substring(0, tempName.lastIndexOf('.'));
featureName = tempName.substring(tempName.lastIndexOf('.') + 1);
}
}
if (disabledFeatures.contains(featureName) || disabledFeatures.contains("*")) {
disabledElements.add(element);
}
})
.peek(typeElement -> System.out.println("Found element: " + typeElement.getQualifiedName().toString()))
.collect(Collectors.toSet());
Map<Set<String>, List<TypeElement>> groupedByChecks = elements.stream()
.collect(Collectors.groupingBy(element -> checks(element, buildPlan)));
Map<String, TypeElement> neededFields = new HashMap<>();
Set<Runnable> fieldInjections = new HashSet<>();
for (TypeElement typeElement : elements) {
if (getLinkagesOfType(typeElement).size() > 1) {
neededFields.put(typeElement.getQualifiedName().toString(), typeElement);
}
List<VariableElement> variableElements = typeElement.getEnclosedElements().stream()
.filter(e -> e.getKind() == ElementKind.FIELD)
.map(VariableElement.class::cast)
.filter(e -> e.getAnnotation(LinkedInstance.class) != null)
.collect(Collectors.toList());
if (variableElements.isEmpty()) continue;
for (VariableElement variableElement : variableElements) {
if (!variableElement.getModifiers().contains(Modifier.PUBLIC)) {
messager.printMessage(Diagnostic.Kind.ERROR, "Field " + variableElement.getSimpleName() + " must be public", variableElement);
continue;
}
if (variableElement.getModifiers().contains(Modifier.STATIC)) {
messager.printMessage(Diagnostic.Kind.ERROR, "Field " + variableElement.getSimpleName() + " must be non static", variableElement);
continue;
}
if (variableElement.getModifiers().contains(Modifier.FINAL)) {
messager.printMessage(Diagnostic.Kind.ERROR, "Field " + variableElement.getSimpleName() + " must be non final", variableElement);
continue;
}
TypeElement fieldType = (TypeElement) ((DeclaredType) variableElement.asType()).asElement();
if (disabledElements.contains(fieldType)) {
continue;
}
if (disabledElements.contains(typeElement)) {
continue;
}
neededFields.put(typeElement.getQualifiedName().toString(), typeElement);
neededFields.put(fieldType.getQualifiedName().toString(), fieldType);
fieldInjections.add(() -> {
specialElements(typeElement, buildPlan, buildPlan::addStaticLine, () -> {
buildPlan.addStaticLine(getElement(typeElement, neededFields) + "." + variableElement.getSimpleName().toString() + " = " + getElement((TypeElement) ((DeclaredType) variableElement.asType()).asElement(), neededFields) + ";");
});
});
}
}
neededFields.forEach((s, typeElement) -> {
if (disabledElements.contains(typeElement)) return;
buildPlan.addImport(typeElement.getQualifiedName().toString());
String t = typeElement.getSimpleName().toString();
t = t.substring(0, 1).toLowerCase() + t.substring(1);
buildPlan.addField(new FieldBuilder(typeElement.getSimpleName().toString(), t));
String finalT = t;
specialElements(typeElement, buildPlan, buildPlan::addStaticLine, () -> {
buildPlan.addStaticLine(finalT + " = new " + typeElement.getSimpleName().toString() + "();");
});
});
fieldInjections.forEach(Runnable::run);
Map<String, MethodBuilder> methods = new HashMap<>();
for (Map.Entry<Set<String>, List<TypeElement>> entry : groupedByChecks.entrySet()) {
Map<String, Map<TypeElement, List<LinkageType>>> groupedByMethod = new HashMap<>();
for (TypeElement typeElement : entry.getValue()) {
for (Map.Entry<String, List<LinkageType>> linkages : getLinkagesOfType(typeElement).entrySet()) {
groupedByMethod.computeIfAbsent(linkages.getKey(), ignored -> new HashMap<>())
.put(typeElement, linkages.getValue());
}
}
for (Map.Entry<String, Map<TypeElement, List<LinkageType>>> group : groupedByMethod.entrySet()) {
MethodBuilder method = methods.computeIfAbsent(group.getKey(), s -> {
MethodBuilder methodBuilder = new MethodBuilder(s, "void");
buildPlan.addMethod(methodBuilder);
return methodBuilder;
});
boolean generated = false;
for (Map.Entry<TypeElement, List<LinkageType>> toGenerate : group.getValue().entrySet()) {
if (disabledElements.contains(toGenerate.getKey())) continue;
if (!generated && !entry.getKey().isEmpty()) {
method.addLine("if (" + String.join(" && ", entry.getKey()) + ") {");
generated = true;
}
TypeElement typeElement = toGenerate.getKey();
String instance = getElement(typeElement, neededFields);
if (toGenerate.getValue().size() > 1 && instance.startsWith("new ")) {
method.addLine(typeElement.getSimpleName() + " local" + typeElement.getSimpleName().toString() + " = " + instance + ";");
instance = "local" + typeElement.getSimpleName().toString();
}
String finalInstance = instance;
toGenerate.getValue().forEach(linkageType -> {
buildPlan.addImport(typeElement.getQualifiedName().toString());
linkageType.generateCode(buildPlan, method, finalInstance, typeElement);
});
}
if (generated && !entry.getKey().isEmpty()) method.addLine("}");
}
}
BufferedWriter bufferedWriter = new BufferedWriter(writer);
buildPlan.write(bufferedWriter);
bufferedWriter.close();
return true;
}
private String getElement(TypeElement typeElement, Map<String, TypeElement> neededFields) {
String s = typeElement.getSimpleName().toString();
if (neededFields.containsKey(typeElement.getQualifiedName().toString())) {
return s.substring(0, 1).toLowerCase() + s.substring(1);
}
return "new " + s + "()";
}
private Set<String> checks(TypeElement typeElement, BuildPlan buildPlan) {
Set<String> checks = new HashSet<>();
MinVersion minVersion = typeElement.getAnnotation(MinVersion.class);
MaxVersion maxVersion = typeElement.getAnnotation(MaxVersion.class);
EventMode eventMode = typeElement.getAnnotation(EventMode.class);
PluginCheck[] pluginChecks = typeElement.getAnnotationsByType(PluginCheck.class);
if (context == Context.SPIGOT) {
errorOnNonNull(typeElement, eventMode);
if (minVersion != null) {
buildPlan.addImport("de.steamwar.core.Core");
checks.add("Core.getVersion() >= " + minVersion.value());
}
if (maxVersion != null) {
buildPlan.addImport("de.steamwar.core.Core");
checks.add("Core.getVersion() <= " + maxVersion.value());
}
if (pluginChecks.length != 0) {
buildPlan.addImport("org.bukkit.Bukkit");
Arrays.stream(pluginChecks).map(pluginCheck -> {
return "Bukkit.getPluginManager().getPlugin(\"" + pluginCheck.value() + "\") " + (pluginCheck.has() == PluginCheck.Has.THIS ? "!" : "=") + "= null";
}).forEach(checks::add);
}
} else {
errorOnNonNull(typeElement, minVersion, maxVersion);
if (eventMode != null) {
buildPlan.addImport("de.steamwar.bungeecore.BungeeCore");
checks.add(eventMode.value().getPrefix() + "BungeeCore.EVENT_MODE");
}
if (pluginChecks.length != 0) {
buildPlan.addImport("net.md_5.bungee.BungeeCord");
Arrays.stream(pluginChecks).map(pluginCheck -> {
return "BungeeCord.getPluginManager().getPlugin(\"" + pluginCheck.value() + "\") " + (pluginCheck.has() == PluginCheck.Has.THIS ? "!" : "=") + "= null";
}).forEach(checks::add);
}
}
return checks;
}
private void specialElements(TypeElement typeElement, BuildPlan buildPlan, Consumer<String> stringConsumer, Runnable inner) {
Set<String> checks = checks(typeElement, buildPlan);
if (!checks.isEmpty()) stringConsumer.accept("if (" + String.join(" && ", checks) + ") {");
inner.run();
if (!checks.isEmpty()) stringConsumer.accept("}");
}
private void errorOnNonNull(TypeElement typeElement, Annotation... annotations) {
for (Annotation annotation : annotations) {
if (annotation != null) {
messager.printMessage(Diagnostic.Kind.ERROR, annotation.annotationType().getSimpleName() + " is not supported in " + context.name(), typeElement);
}
}
}
private Plain_GENERIC plain_GENERIC = new Plain_GENERIC();
private Map<String, List<LinkageType>> getLinkagesOfType(TypeElement typeElement) {
Map<String, List<LinkageType>> linkages = new HashMap<>();
Stream.concat(Stream.of(typeElement.getSuperclass()), typeElement.getInterfaces().stream())
.map(this::resolveSingle)
.filter(Objects::nonNull)
.forEach(linkageType -> linkages.computeIfAbsent(linkageType.method(), s -> new ArrayList<>()).add(linkageType));
if (linkages.size() == 1 && linkages.containsKey("unlink")) {
linkages.put(plain_GENERIC.method(), Collections.singletonList(plain_GENERIC));
}
return linkages;
}
private LinkageType resolveSingle(TypeMirror typeMirror) {
String qualifier = typeMirror.toString();
if (qualifier.contains("<")) qualifier = qualifier.substring(0, qualifier.indexOf('<'));
qualifier = qualifier.substring(qualifier.lastIndexOf('.') + 1);
try {
return (LinkageType) Class.forName("de.steamwar.linkage.types." + qualifier + "_" + context.name()).getDeclaredConstructor().newInstance();
} catch (Exception e) {
// Ignore
}
try {
return (LinkageType) Class.forName("de.steamwar.linkage.types." + qualifier + "_GENERIC").getDeclaredConstructor().newInstance();
} catch (Exception e) {
// Ignore
}
return null;
}
}

Datei anzeigen

@ -1,36 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.linkage;
import de.steamwar.linkage.plan.BuildPlan;
import de.steamwar.linkage.plan.MethodBuilder;
import javax.lang.model.element.TypeElement;
public interface LinkageType {
default String getPluginMain() {
return LinkageProcessor.getPluginMain();
}
String method();
void generateCode(BuildPlan buildPlan, MethodBuilder method, String instance, TypeElement typeElement);
}

Datei anzeigen

@ -1,28 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.linkage;
import java.lang.annotation.*;
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
public @interface Linked {
String feature() default "";
}

Datei anzeigen

@ -1,30 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.linkage;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.FIELD})
public @interface LinkedInstance {
}

Datei anzeigen

@ -1,32 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.linkage;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@AllowedContexts(Context.SPIGOT)
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
public @interface MaxVersion {
int value();
}

Datei anzeigen

@ -1,32 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.linkage;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@AllowedContexts(Context.SPIGOT)
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
public @interface MinVersion {
int value();
}

Datei anzeigen

@ -1,42 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.linkage;
import java.lang.annotation.*;
@AllowedContexts({Context.BUNGEE, Context.SPIGOT})
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
@Repeatable(PluginCheck.PluginChecks.class)
public @interface PluginCheck {
Has has() default Has.THIS;
String value();
enum Has {
THIS,
NOT
}
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
@interface PluginChecks {
@SuppressWarnings("unused") PluginCheck[] value() default {};
}
}

Datei anzeigen

@ -1,24 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.linkage.api;
public interface Disable {
void disable();
}

Datei anzeigen

@ -1,24 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.linkage.api;
public interface Enable {
void enable();
}

Datei anzeigen

@ -1,89 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.linkage.plan;
import lombok.RequiredArgsConstructor;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.*;
@RequiredArgsConstructor
public class BuildPlan {
private final String packageName;
private Set<String> imports = new HashSet<>();
private final String className;
private List<FieldBuilder> fieldBuilders = new ArrayList<>();
private Map<String, MethodBuilder> methodBuilderMap = new HashMap<>();
private List<String> staticLines = new ArrayList<>();
public void addImport(String importName) {
imports.add(importName);
}
public void addField(FieldBuilder fieldBuilder) {
fieldBuilders.add(fieldBuilder);
}
public void addMethod(MethodBuilder methodBuilder) {
methodBuilderMap.put(methodBuilder.getMethodName(), methodBuilder);
}
public boolean hasMethod(String methodName) {
return methodBuilderMap.containsKey(methodName);
}
public void addStaticLine(String line) {
staticLines.add(line);
}
public void write(BufferedWriter writer) throws IOException {
writer.write("package " + packageName + ";\n");
if (!imports.isEmpty()) {
writer.write("\n");
for (String importName : imports) {
writer.write("import " + importName + ";\n");
}
}
writer.write("\n");
writer.write("public class " + className + " {\n");
if (!fieldBuilders.isEmpty()) {
for (FieldBuilder fieldBuilder : fieldBuilders) {
fieldBuilder.write(writer);
}
writer.write("\n");
}
if (!staticLines.isEmpty()) {
writer.write(" static {\n");
for (String line : staticLines) {
writer.write(" " + line + "\n");
}
writer.write(" }\n");
writer.write("\n");
}
for (MethodBuilder methodBuilder : methodBuilderMap.values()) {
methodBuilder.write(writer);
writer.write("\n");
}
writer.write("}\n");
}
}

Datei anzeigen

@ -1,44 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.linkage.plan;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.io.BufferedWriter;
import java.io.IOException;
@RequiredArgsConstructor
@AllArgsConstructor
public class FieldBuilder {
@Getter
private final String type;
private final String name;
private String initializer;
public String getFieldName() {
return name;
}
public void write(BufferedWriter writer) throws IOException {
writer.write(" private static " + type + " " + getFieldName() + (initializer == null ? "" : " = " + initializer) + ";\n");
}
}

Datei anzeigen

@ -1,70 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.linkage.plan;
import lombok.RequiredArgsConstructor;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@RequiredArgsConstructor
public class MethodBuilder {
private final String name;
private final String returnType;
private List<ParameterBuilder> parameters = new ArrayList<>();
private List<String> lines = new ArrayList<>();
private boolean isPrivate = false;
public void addParameter(ParameterBuilder parameterBuilder) {
parameters.add(parameterBuilder);
}
public void addLine(String line) {
lines.add(line);
}
public String getMethodName() {
return name;
}
public void setPrivate(boolean isPrivate) {
this.isPrivate = isPrivate;
}
public void write(BufferedWriter writer) throws IOException {
writer.write(" " + (isPrivate ? "private" : "public") + " static " + returnType + " " + getMethodName() + "(");
for (int i = 0; i < parameters.size(); i++) {
parameters.get(i).write(writer);
if (i < parameters.size() - 1) {
writer.write(", ");
}
}
writer.write(") {");
for (String line : lines) {
writer.write("\n");
writer.write(" " + line);
}
writer.write("\n");
writer.write(" }\n");
}
}

Datei anzeigen

@ -1,35 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.linkage.plan;
import lombok.AllArgsConstructor;
import java.io.BufferedWriter;
import java.io.IOException;
@AllArgsConstructor
public class ParameterBuilder {
private String type;
private String name;
public void write(BufferedWriter writer) throws IOException {
writer.write(type + " " + name);
}
}

Datei anzeigen

@ -1,39 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 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.linkage.types;
import de.steamwar.linkage.LinkageType;
import de.steamwar.linkage.plan.BuildPlan;
import de.steamwar.linkage.plan.MethodBuilder;
import javax.lang.model.element.TypeElement;
public class Disable_GENERIC implements LinkageType {
@Override
public String method() {
return "unlink";
}
@Override
public void generateCode(BuildPlan buildPlan, MethodBuilder method, String instance, TypeElement typeElement) {
method.addLine(instance + ".disable();");
}
}

Datei anzeigen

@ -1,39 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 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.linkage.types;
import de.steamwar.linkage.LinkageType;
import de.steamwar.linkage.plan.BuildPlan;
import de.steamwar.linkage.plan.MethodBuilder;
import javax.lang.model.element.TypeElement;
public class Enable_GENERIC implements LinkageType {
@Override
public String method() {
return "link";
}
@Override
public void generateCode(BuildPlan buildPlan, MethodBuilder method, String instance, TypeElement typeElement) {
method.addLine(instance + ".enable();");
}
}

Datei anzeigen

@ -1,41 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 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.linkage.types;
import de.steamwar.linkage.LinkageType;
import de.steamwar.linkage.plan.BuildPlan;
import de.steamwar.linkage.plan.MethodBuilder;
import javax.lang.model.element.TypeElement;
public class Listener_BUNGEE implements LinkageType {
@Override
public String method() {
return "link";
}
@Override
public void generateCode(BuildPlan buildPlan, MethodBuilder method, String instance, TypeElement typeElement) {
buildPlan.addImport("net.md_5.bungee.api.ProxyServer");
buildPlan.addImport("de.steamwar.bungeecore.BungeeCore");
method.addLine("ProxyServer.getInstance().getPluginManager().registerListener(BungeeCore.get(), " + instance + ");");
}
}

Datei anzeigen

@ -1,105 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 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.linkage.types;
import de.steamwar.linkage.LinkageType;
import de.steamwar.linkage.plan.BuildPlan;
import de.steamwar.linkage.plan.FieldBuilder;
import de.steamwar.linkage.plan.MethodBuilder;
import de.steamwar.linkage.plan.ParameterBuilder;
import javax.lang.model.element.*;
import javax.lang.model.type.DeclaredType;
import java.util.HashMap;
import java.util.Map;
public class Listener_SPIGOT implements LinkageType {
@Override
public String method() {
return "link";
}
@Override
public void generateCode(BuildPlan buildPlan, MethodBuilder method, String instance, TypeElement typeElement) {
Map<String, TypeElement> eventClasses = new HashMap<>();
Map<TypeElement, ExecutableElement> eventMethods = new HashMap<>();
typeElement.getEnclosedElements().stream().filter(e -> e.getKind() == ElementKind.METHOD).map(ExecutableElement.class::cast).filter(e -> {
return e.getAnnotationMirrors().stream().anyMatch(annotationMirror -> {
return annotationMirror.getAnnotationType().asElement().getSimpleName().toString().equals("EventHandler");
});
}).forEach(e -> {
TypeElement current = ((TypeElement)((DeclaredType) e.getParameters().get(0).asType()).asElement());
eventClasses.put(current.getQualifiedName().toString(), current);
eventMethods.put(current, e);
});
eventClasses.forEach((s, eventType) -> {
if (buildPlan.hasMethod(eventType.getSimpleName().toString())) return;
buildPlan.addImport("org.bukkit.event.HandlerList");
buildPlan.addImport("org.bukkit.event.Listener");
buildPlan.addImport("java.util.function.Consumer");
buildPlan.addImport("org.bukkit.event.EventPriority");
buildPlan.addImport("org.bukkit.plugin.RegisteredListener");
buildPlan.addImport("org.bukkit.plugin.EventExecutor");
buildPlan.addImport(s);
buildPlan.addField(new FieldBuilder("HandlerList", "handlerList" + eventType.getSimpleName()));
MethodBuilder methodBuilder = new MethodBuilder(eventType.getSimpleName().toString(), "void");
methodBuilder.addParameter(new ParameterBuilder("Listener", "listener"));
methodBuilder.addParameter(new ParameterBuilder("Consumer<" + eventType.getSimpleName() + ">", "consumer"));
methodBuilder.addParameter(new ParameterBuilder("EventPriority", "eventPriority"));
methodBuilder.addParameter(new ParameterBuilder("boolean", "ignoreCancelled"));
methodBuilder.setPrivate(true);
methodBuilder.addLine("EventExecutor eventExecutor = (l, event) -> {");
methodBuilder.addLine(" if (event instanceof " + eventType.getSimpleName() + ") {");
methodBuilder.addLine(" consumer.accept((" + eventType.getSimpleName() + ") event);");
methodBuilder.addLine(" }");
methodBuilder.addLine("};");
methodBuilder.addLine("handlerList" + eventType.getSimpleName() + ".register(new RegisteredListener(listener, eventExecutor, eventPriority, " + getPluginMain() + ".getInstance(), ignoreCancelled));");
buildPlan.addMethod(methodBuilder);
method.addLine("handlerList" + eventType.getSimpleName() + " = " + eventType.getSimpleName() + ".getHandlerList();");
});
String localInstance = "local" + typeElement.getSimpleName().toString();
if (!instance.startsWith("new ")) {
localInstance = instance;
} else {
method.addLine(typeElement.getSimpleName() + " " + localInstance + " = " + instance + ";");
}
String finalLocalInstance = localInstance;
eventMethods.forEach((type, executableElement) -> {
AnnotationMirror eventHandler = executableElement.getAnnotationMirrors().stream().filter(annotationMirror -> annotationMirror.getAnnotationType().asElement().getSimpleName().toString().equals("EventHandler")).findFirst().orElse(null);
if (eventHandler == null) {
return;
}
String priority = "NORMAL";
String ignoreCancelled = "false";
for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : eventHandler.getElementValues().entrySet()) {
if (entry.getKey().getSimpleName().toString().equals("priority")) {
priority = entry.getValue().getValue().toString();
} else if (entry.getKey().getSimpleName().toString().equals("ignoreCancelled")) {
ignoreCancelled = entry.getValue().getValue().toString();
}
}
method.addLine(type.getSimpleName().toString() + "(" + finalLocalInstance + ", " + finalLocalInstance + "::" + executableElement.getSimpleName().toString() + ", EventPriority." + priority + ", " + ignoreCancelled + ");");
});
}
}

Datei anzeigen

@ -1,39 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.linkage.types;
import de.steamwar.linkage.LinkageType;
import de.steamwar.linkage.plan.BuildPlan;
import de.steamwar.linkage.plan.MethodBuilder;
import javax.lang.model.element.TypeElement;
public class PacketHandler_GENERIC implements LinkageType {
@Override
public String method() {
return "link";
}
@Override
public void generateCode(BuildPlan buildPlan, MethodBuilder method, String instance, TypeElement typeElement) {
method.addLine(instance + ".register();");
}
}

Datei anzeigen

@ -1,39 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 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.linkage.types;
import de.steamwar.linkage.LinkageType;
import de.steamwar.linkage.plan.BuildPlan;
import de.steamwar.linkage.plan.MethodBuilder;
import javax.lang.model.element.TypeElement;
public class Plain_GENERIC implements LinkageType {
@Override
public String method() {
return "link";
}
@Override
public void generateCode(BuildPlan buildPlan, MethodBuilder method, String instance, TypeElement typeElement) {
if (instance.startsWith("new ")) method.addLine(instance + ";");
}
}

Datei anzeigen

@ -1,39 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 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.linkage.types;
import de.steamwar.linkage.LinkageType;
import de.steamwar.linkage.plan.BuildPlan;
import de.steamwar.linkage.plan.MethodBuilder;
import javax.lang.model.element.TypeElement;
public class SWCommand_BUNGEE implements LinkageType {
@Override
public String method() {
return "link";
}
@Override
public void generateCode(BuildPlan buildPlan, MethodBuilder method, String instance, TypeElement typeElement) {
method.addLine(instance + ";");
}
}

Datei anzeigen

@ -1,39 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 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.linkage.types;
import de.steamwar.linkage.LinkageType;
import de.steamwar.linkage.plan.BuildPlan;
import de.steamwar.linkage.plan.MethodBuilder;
import javax.lang.model.element.TypeElement;
public class SWCommand_SPIGOT implements LinkageType {
@Override
public String method() {
return "link";
}
@Override
public void generateCode(BuildPlan buildPlan, MethodBuilder method, String instance, TypeElement typeElement) {
method.addLine(instance + ".setMessage(" + getPluginMain() + ".MESSAGE);");
}
}

Datei anzeigen

@ -21,6 +21,7 @@ package de.steamwar.network.packets.client;
import de.steamwar.network.packets.NetworkPacket; import de.steamwar.network.packets.NetworkPacket;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class ImALobbyPacket extends NetworkPacket { public class ImALobbyPacket extends NetworkPacket {

Datei anzeigen

@ -1,39 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 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.network.packets.client;
import de.steamwar.network.packets.NetworkPacket;
import lombok.*;
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Getter
public class RequestSchematicSearchPacket extends NetworkPacket {
private static final long serialVersionUID = -6525229932332581648L;
private int playerId;
private int schematicId;
private boolean ignoreBlockData;
private boolean ignoreAir;
private boolean airAsAny;
}

Datei anzeigen

@ -38,5 +38,4 @@ public class FightEndsPacket extends NetworkPacket {
private List<Integer> bluePlayers; private List<Integer> bluePlayers;
private List<Integer> redPlayers; private List<Integer> redPlayers;
private String gameMode; private String gameMode;
private int duration;
} }

Datei anzeigen

@ -1,67 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 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.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.List;
@AllArgsConstructor
public class BannedUserIPs {
private static final Table<BannedUserIPs> table = new Table<>(BannedUserIPs.class);
private static final SelectStatement<BannedUserIPs> getByID = table.selectFields("UserID");
private static final SelectStatement<BannedUserIPs> getByIP = new SelectStatement<>(table, "SELECT * FROM BannedUserIPs WHERE IP = ? ORDER BY Timestamp DESC");
private static final Statement banIP = table.insertAll();
private static final Statement unbanIPs = table.deleteFields("UserID");
@Getter
@Field(keys = {Table.PRIMARY})
private final int userID;
@Getter
@Field(def = "CURRENT_TIMESTAMP")
private final Timestamp timestamp;
@Field(keys = {Table.PRIMARY})
private final String ip;
public static List<BannedUserIPs> get(int userID) {
return getByID.listSelect(userID);
}
public static List<BannedUserIPs> get(String ip) {
return getByIP.listSelect(ip);
}
public static void banIP(int userID, String ip){
banIP.update(userID, Timestamp.from(Instant.now()), ip);
}
public static void unbanIPs(int userID) {
unbanIPs.update(userID);
}
}

Datei anzeigen

@ -21,7 +21,6 @@ package de.steamwar.sql;
import de.steamwar.sql.internal.Field; import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement; import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table; import de.steamwar.sql.internal.Table;
import lombok.Getter; import lombok.Getter;
@ -37,12 +36,6 @@ public class BauweltMember {
private static final Table<BauweltMember> table = new Table<>(BauweltMember.class); private static final Table<BauweltMember> table = new Table<>(BauweltMember.class);
private static final SelectStatement<BauweltMember> getMember = table.select(Table.PRIMARY); private static final SelectStatement<BauweltMember> getMember = table.select(Table.PRIMARY);
private static final SelectStatement<BauweltMember> getMembers = table.selectFields("BauweltID"); private static final SelectStatement<BauweltMember> getMembers = table.selectFields("BauweltID");
private static final Statement update = table.insertAll();
private static final Statement delete = table.delete(Table.PRIMARY);
public static void addMember(UUID ownerID, UUID memberID) {
new BauweltMember(SteamwarUser.get(ownerID).getId(), SteamwarUser.get(memberID).getId(), false, false).updateDB();
}
public static BauweltMember getBauMember(UUID ownerID, UUID memberID){ public static BauweltMember getBauMember(UUID ownerID, UUID memberID){
return getBauMember(SteamwarUser.get(ownerID).getId(), SteamwarUser.get(memberID).getId()); return getBauMember(SteamwarUser.get(ownerID).getId(), SteamwarUser.get(memberID).getId());
@ -50,7 +43,7 @@ public class BauweltMember {
public static BauweltMember getBauMember(int ownerID, int memberID){ public static BauweltMember getBauMember(int ownerID, int memberID){
BauweltMember member = memberCache.get(memberID); BauweltMember member = memberCache.get(memberID);
if(member != null && member.bauweltID == ownerID) if(member != null)
return member; return member;
return getMember.select(ownerID, memberID); return getMember.select(ownerID, memberID);
} }
@ -70,11 +63,11 @@ public class BauweltMember {
@Field(keys = {Table.PRIMARY}) @Field(keys = {Table.PRIMARY})
private final int memberID; private final int memberID;
@Getter @Getter
@Field(def = "0") @Field
private boolean worldEdit; private final boolean worldEdit;
@Getter @Getter
@Field(def = "0") @Field
private boolean world; private final boolean world;
public BauweltMember(int bauweltID, int memberID, boolean worldEdit, boolean world) { public BauweltMember(int bauweltID, int memberID, boolean worldEdit, boolean world) {
this.bauweltID = bauweltID; this.bauweltID = bauweltID;
@ -83,41 +76,4 @@ public class BauweltMember {
this.world = world; this.world = world;
memberCache.put(memberID, this); memberCache.put(memberID, this);
} }
public void setWorldEdit(boolean worldEdit) {
this.worldEdit = worldEdit;
updateDB();
}
public void setWorld(boolean world) {
this.world = world;
updateDB();
}
private void updateDB(){
update.update(bauweltID, memberID, worldEdit, world);
}
public void remove(){
delete.update(bauweltID, memberID);
memberCache.remove(memberID);
}
public boolean isBuild() {
return worldEdit;
}
public boolean isSupervisor() {
return world;
}
public void setBuild(boolean build) {
this.worldEdit = build;
updateDB();
}
public void setSupervisor(boolean supervisor) {
this.world = supervisor;
updateDB();
}
} }

Datei anzeigen

@ -21,7 +21,6 @@ package de.steamwar.sql;
import de.steamwar.sql.internal.Field; import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement; import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table; import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
@ -34,25 +33,11 @@ public class CheckedSchematic {
private static final Table<CheckedSchematic> table = new Table<>(CheckedSchematic.class); private static final Table<CheckedSchematic> table = new Table<>(CheckedSchematic.class);
private static final SelectStatement<CheckedSchematic> statusOfNode = new SelectStatement<>(table, "SELECT * FROM CheckedSchematic WHERE NodeId = ? AND DeclineReason != 'Prüfvorgang abgebrochen' ORDER BY EndTime DESC"); private static final SelectStatement<CheckedSchematic> statusOfNode = new SelectStatement<>(table, "SELECT * FROM CheckedSchematic WHERE NodeId = ? AND DeclineReason != 'Prüfvorgang abgebrochen' ORDER BY EndTime DESC");
private static final SelectStatement<CheckedSchematic> nodeHistory = new SelectStatement<>(table, "SELECT * FROM CheckedSchematic WHERE NodeId = ? AND DeclineReason != '' AND DeclineReason != 'Prüfvorgang abgebrochen' ORDER BY EndTime DESC");
private static final Statement insert = table.insertAll();
public static void create(int nodeId, String name, int owner, int validator, Timestamp startTime, Timestamp endTime, String reason){
insert.update(nodeId, owner, name, validator, startTime, endTime, reason);
}
public static void create(SchematicNode node, int validator, Timestamp startTime, Timestamp endTime, String reason){
create(node.getId(), node.getName(), node.getOwner(), validator, startTime, endTime, reason);
}
public static List<CheckedSchematic> getLastDeclinedOfNode(int node){ public static List<CheckedSchematic> getLastDeclinedOfNode(int node){
return statusOfNode.listSelect(node); return statusOfNode.listSelect(node);
} }
public static List<CheckedSchematic> previousChecks(SchematicNode node) {
return nodeHistory.listSelect(node.getId());
}
@Field(nullable = true) @Field(nullable = true)
private final Integer nodeId; private final Integer nodeId;
@Field @Field

Datei anzeigen

@ -26,45 +26,17 @@ import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.time.Instant;
import java.util.List;
@AllArgsConstructor @AllArgsConstructor
public class Event { public class Event {
static {
SchematicType.Normal.name(); // Ensure SchematicType is loaded.
}
private static final Table<Event> table = new Table<>(Event.class); private static final Table<Event> table = new Table<>(Event.class);
private static final SelectStatement<Event> byCurrent = new SelectStatement<>(table, "SELECT * FROM Event WHERE Start < now() AND End > now()");
private static final SelectStatement<Event> byId = table.select(Table.PRIMARY); private static final SelectStatement<Event> byId = table.select(Table.PRIMARY);
private static final SelectStatement<Event> byName = table.select("eventName");
private static final SelectStatement<Event> byComing = new SelectStatement<>(table, "SELECT * FROM Event WHERE Start > now()");
private static Event current = null;
public static Event get(){
if(current != null && current.now())
return current;
current = byCurrent.select();
return current;
}
public static Event get(int eventID){ public static Event get(int eventID){
return byId.select(eventID); return byId.select(eventID);
} }
public static Event get(String eventName) {
return byName.select(eventName);
}
public static List<Event> getComing() {
return byComing.listSelect();
}
@Getter @Getter
@Field(keys = {Table.PRIMARY}, autoincrement = true) @Field(keys = {Table.PRIMARY}, autoincrement = true)
private final int eventID; private final int eventID;
@ -83,11 +55,11 @@ public class Event {
@Getter @Getter
@Field @Field
private final int maximumTeamMembers; private final int maximumTeamMembers;
@Getter
@Field(nullable = true) @Field(nullable = true)
private final SchematicType schemType; private final SchematicType schematicType;
@Field @Field
private final boolean publicSchemsOnly; private final boolean publicSchemsOnly;
@Deprecated
@Field @Field
private final boolean spectateSystem; private final boolean spectateSystem;
@ -97,13 +69,4 @@ public class Event {
public boolean spectateSystem(){ public boolean spectateSystem(){
return spectateSystem; return spectateSystem;
} }
public SchematicType getSchematicType() {
return schemType;
}
private boolean now() {
Instant now = Instant.now();
return now.isAfter(start.toInstant()) && now.isBefore(end.toInstant());
}
} }

Datei anzeigen

@ -26,41 +26,18 @@ import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import java.sql.Timestamp;
import java.util.*;
import static java.time.temporal.ChronoUnit.SECONDS;
@AllArgsConstructor @AllArgsConstructor
public class EventFight implements Comparable<EventFight> { public class EventFight {
private static final Table<EventFight> table = new Table<>(EventFight.class); private static final Table<EventFight> table = new Table<>(EventFight.class);
private static final SelectStatement<EventFight> byId = table.select(Table.PRIMARY); private static final SelectStatement<EventFight> byId = table.select(Table.PRIMARY);
private static final SelectStatement<EventFight> allComing = new SelectStatement<>(table, "SELECT * FROM EventFight WHERE StartTime > now() ORDER BY StartTime ASC");
private static final SelectStatement<EventFight> event = new SelectStatement<>(table, "SELECT * FROM EventFight WHERE EventID = ? ORDER BY StartTime ASC");
private static final Statement reschedule = table.update(Table.PRIMARY, "StartTime");
private static final Statement setResult = table.update(Table.PRIMARY, "Ergebnis"); private static final Statement setResult = table.update(Table.PRIMARY, "Ergebnis");
private static final Statement setFight = table.update(Table.PRIMARY, "Fight"); private static final Statement setFight = table.update(Table.PRIMARY, "Fight");
private static final Queue<EventFight> fights = new PriorityQueue<>();
public static EventFight get(int fightID) { public static EventFight get(int fightID) {
return byId.select(fightID); return byId.select(fightID);
} }
public static void loadAllComingFights() {
fights.clear();
fights.addAll(allComing.listSelect());
}
public static List<EventFight> getEvent(int eventID) {
return event.listSelect(eventID);
}
public static Queue<EventFight> getFights() {
return fights;
}
@Getter @Getter
@Field @Field
private final int eventID; private final int eventID;
@ -69,27 +46,14 @@ public class EventFight implements Comparable<EventFight> {
private final int fightID; private final int fightID;
@Getter @Getter
@Field @Field
private Timestamp startTime;
@Getter
@Field
private final String spielmodus;
@Getter
@Field
private final String map;
@Getter
@Field
private final int teamBlue; private final int teamBlue;
@Getter @Getter
@Field @Field
private final int teamRed; private final int teamRed;
@Getter @Getter
@Field @Field
@Deprecated
private final int kampfleiter; private final int kampfleiter;
@Getter @Getter
@Field
private final int spectatePort;
@Getter
@Field(def = "0") @Field(def = "0")
private int ergebnis; private int ergebnis;
@Field(nullable = true) @Field(nullable = true)
@ -105,32 +69,4 @@ public class EventFight implements Comparable<EventFight> {
this.fight = fight; this.fight = fight;
setFight.update(fight, fightID); setFight.update(fight, fightID);
} }
public boolean hasFinished() {
return fight != 0 || ergebnis != 0;
}
public void reschedule() {
startTime = Timestamp.from(new Date().toInstant().plus(30, SECONDS));
reschedule.update(startTime, fightID);
}
@Override
public int hashCode(){
return fightID;
}
@Override
public boolean equals(Object o){
if(o == null)
return false;
if(!(o instanceof EventFight))
return false;
return fightID == ((EventFight) o).fightID;
}
@Override
public int compareTo(EventFight o) {
return startTime.compareTo(o.startTime);
}
} }

Datei anzeigen

@ -20,49 +20,24 @@
package de.steamwar.sql; package de.steamwar.sql;
import de.steamwar.sql.internal.Field; import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement; import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table; import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@AllArgsConstructor @AllArgsConstructor
public class Fight { public class Fight {
private static final Table<Fight> table = new Table<>(Fight.class); private static final Table<Fight> table = new Table<>(Fight.class);
private static final SelectStatement<Fight> getPage = new SelectStatement<>(table, "SELECT f.*, (b.NodeId IS NULL OR b.AllowReplay) AND (r.NodeId IS NULL OR r.AllowReplay) AS ReplayAllowed, (SELECT COUNT(1) FROM Replay WHERE Replay.FightID = f.FightID) as ReplayAvailable FROM Fight f LEFT OUTER JOIN SchematicNode b ON f.BlueSchem = b.NodeId LEFT OUTER JOIN SchematicNode r ON f.RedSchem = r.NodeId ORDER BY FightID DESC LIMIT ?, ?");
private static final Statement insert = table.insertFields(true, "GameMode", "Server", "StartTime", "Duration", "BlueLeader", "RedLeader", "BlueSchem", "RedSchem", "Win", "WinCondition"); private static final Statement insert = table.insertFields(true, "GameMode", "Server", "StartTime", "Duration", "BlueLeader", "RedLeader", "BlueSchem", "RedSchem", "Win", "WinCondition");
public static List<Fight> getPage(int page, int elementsPerPage) {
List<Fight> fights = getPage.listSelect(page * elementsPerPage, elementsPerPage);
List<FightPlayer> fightPlayers = FightPlayer.batchGet(fights.stream().map(f -> f.fightID));
for(Fight fight : fights) {
fight.initPlayers(fightPlayers);
}
SteamwarUser.batchCache(fightPlayers.stream().map(FightPlayer::getUserID).collect(Collectors.toSet()));
return fights;
}
public static int create(String gamemode, String server, Timestamp starttime, int duration, int blueleader, int redleader, Integer blueschem, Integer redschem, int win, String wincondition){
return insert.insertGetKey(gamemode, server, starttime, duration, blueleader, redleader, blueschem, redschem, win, wincondition);
}
@Getter
@Field(keys = {Table.PRIMARY}, autoincrement = true) @Field(keys = {Table.PRIMARY}, autoincrement = true)
private final int fightID; private final int fightID;
@Field @Field
private final String gameMode; private final String gameMode;
@Getter
@Field @Field
private final String server; private final String server;
@Getter
@Field @Field
private final Timestamp startTime; private final Timestamp startTime;
@Field @Field
@ -75,50 +50,12 @@ public class Fight {
private final Integer blueSchem; private final Integer blueSchem;
@Field(nullable = true) @Field(nullable = true)
private final Integer redSchem; private final Integer redSchem;
@Getter
@Field @Field
private final int win; private final int win;
@Field @Field
private final String wincondition; private final String wincondition;
@Field // Virtual field for easy select
private final boolean replayAllowed;
@Field // Virtual field for easy select
private final boolean replayAvailable;
@Getter public static int create(String gamemode, String server, Timestamp starttime, int duration, int blueleader, int redleader, Integer blueschem, Integer redschem, int win, String wincondition){
private final List<FightPlayer> bluePlayers = new ArrayList<>(); return insert.insertGetKey(gamemode, server, starttime, duration, blueleader, redleader, blueschem, redschem, win, wincondition);
@Getter
private final List<FightPlayer> redPlayers = new ArrayList<>();
public SchematicType getSchemType() {
return SchematicType.fromDB(gameMode);
}
public SteamwarUser getBlueLeader() {
return SteamwarUser.get(blueLeader);
}
public SteamwarUser getRedLeader() {
return SteamwarUser.get(redLeader);
}
public boolean replayAllowed() {
return replayExists() && replayAllowed;
}
public boolean replayExists() {
return getSchemType() != null && replayAvailable;
}
private void initPlayers(List<FightPlayer> fightPlayers) {
for(FightPlayer fp : fightPlayers) {
if(fp.getFightID() != fightID)
continue;
if(fp.getTeam() == 1)
bluePlayers.add(fp);
else
redPlayers.add(fp);
}
} }
} }

Datei anzeigen

@ -20,30 +20,20 @@
package de.steamwar.sql; package de.steamwar.sql;
import de.steamwar.sql.internal.Field; import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement; import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table; import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@AllArgsConstructor @AllArgsConstructor
public class FightPlayer { public class FightPlayer {
private static final Table<FightPlayer> table = new Table<>(FightPlayer.class); private static final Table<FightPlayer> table = new Table<>(FightPlayer.class);
private static final Statement create = table.insertAll(); private static final Statement create = table.insertAll();
private static final SelectStatement<FightPlayer> batchGet = new SelectStatement<>(table, "SELECT * FROM FightPlayer WHERE FightID IN ?");
@Getter
@Field(keys = {Table.PRIMARY}) @Field(keys = {Table.PRIMARY})
private final int fightID; private final int fightID;
@Getter
@Field(keys = {Table.PRIMARY}) @Field(keys = {Table.PRIMARY})
private final int userID; private final int userID;
@Getter
@Field @Field
private final int team; private final int team;
@Field @Field
@ -56,10 +46,4 @@ public class FightPlayer {
public static void create(int fightID, int userID, boolean blue, String kit, int kills, boolean isOut) { public static void create(int fightID, int userID, boolean blue, String kit, int kills, boolean isOut) {
create.update(fightID, userID, blue ? 1 : 2, kit, kills, isOut); create.update(fightID, userID, blue ? 1 : 2, kit, kills, isOut);
} }
public static List<FightPlayer> batchGet(Stream<Integer> fightIds) {
try (SelectStatement<FightPlayer> batch = new SelectStatement<>(table, "SELECT * FROM FightPlayer WHERE FightID IN (" + fightIds.map(Object::toString).collect(Collectors.joining(", ")) + ")")) {
return batch.listSelect();
}
}
} }

Datei anzeigen

@ -1,59 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 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.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import java.sql.ResultSet;
import java.util.UUID;
@AllArgsConstructor
public class IgnoreSystem {
private static final Table<IgnoreSystem> table = new Table<>(IgnoreSystem.class, "IgnoredPlayers");
private static final SelectStatement<IgnoreSystem> select = table.select(Table.PRIMARY);
private static final Statement insert = table.insertAll();
private static final Statement delete = table.delete(Table.PRIMARY);
@Field(keys = {Table.PRIMARY})
private final int ignorer;
@Field(keys = {Table.PRIMARY})
private final int ignored;
public static boolean isIgnored(UUID ignorer, UUID ignored){
return isIgnored(SteamwarUser.get(ignorer), SteamwarUser.get(ignored));
}
public static boolean isIgnored(SteamwarUser ignorer, SteamwarUser ignored) {
return select.select(ResultSet::next, ignorer.getId(), ignored.getId());
}
public static void ignore(SteamwarUser ignorer, SteamwarUser ignored) {
insert.update(ignorer.getId(), ignored.getId());
}
public static void unIgnore(SteamwarUser ignorer, SteamwarUser ignored) {
delete.update(ignorer.getId(), ignored.getId());
}
}

Datei anzeigen

@ -1,101 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 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.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
@AllArgsConstructor
public class Mod {
static {
SqlTypeMapper.ordinalEnumMapper(Platform.class);
SqlTypeMapper.ordinalEnumMapper(ModType.class);
}
private static final Table<Mod> table = new Table<>(Mod.class, "Mods");
private static final SelectStatement<Mod> get = table.select(Table.PRIMARY);
private static final SelectStatement<Mod> findFirst = new SelectStatement<>(table, "SELECT * FROM Mods WHERE ModType = 0 LIMIT 1");
private static final SelectStatement<Mod> getPageOfType = new SelectStatement<>(table, "SELECT * FROM Mods WHERE ModType = ? ORDER BY ModName DESC LIMIT ?, ?");
private static final Statement insert = table.insert(Table.PRIMARY);
private static final Statement set = table.update(Table.PRIMARY, "ModType");
public static Mod get(String name, Platform platform) {
return get.select(platform, name);
}
public static Mod getOrCreate(String name, Platform platform) {
Mod mod = get(name, platform);
if(mod != null)
return mod;
insert.update(platform, name);
return new Mod(platform, name, ModType.UNKLASSIFIED);
}
public static List<Mod> getAllModsFiltered(int page, int elementsPerPage, Mod.ModType filter) {
return Mod.getPageOfType.listSelect(filter, page * elementsPerPage, elementsPerPage);
}
public static Mod findFirstMod() {
return findFirst.select();
}
@Getter
@Field(keys = {Table.PRIMARY})
private final Platform platform;
@Getter
@Field(keys = {Table.PRIMARY})
private final String modName;
@Getter
@Field(def = "0")
private ModType modType;
public void setModType(Mod.ModType modType) {
set.update(modType, platform, modName);
this.modType = modType;
}
public enum Platform {
FORGE,
LABYMOD,
FABRIC
}
public enum ModType {
UNKLASSIFIED("7"),
GREEN("a"),
YELLOW("e"),
RED("c"),
YOUTUBER_ONLY("6");
ModType(String colorcode) {
this.colorcode = colorcode;
}
private final String colorcode;
public String getColorCode() {
return colorcode;
}
}
}

Datei anzeigen

@ -1,92 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 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.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.io.*;
import java.sql.PreparedStatement;
import java.util.zip.GZIPInputStream;
@AllArgsConstructor
public class NodeData {
static {
new SqlTypeMapper<>(PipedInputStream.class, "BLOB", (rs, identifier) -> { throw new SecurityException("PipedInputStream is write only datatype"); }, PreparedStatement::setBinaryStream);
new SqlTypeMapper<>(ByteArrayInputStream.class, "BLOB", (rs, identifier) -> { throw new SecurityException("ByteArrayInputStream is write only datatype"); }, PreparedStatement::setBinaryStream);
new SqlTypeMapper<>(BufferedInputStream.class, "BLOB", (rs, identifier) -> { throw new SecurityException("BufferedInputStream is write only datatype"); }, PreparedStatement::setBinaryStream);
}
private static final Table<NodeData> table = new Table<>(NodeData.class);
private static final Statement updateDatabase = new Statement("INSERT INTO NodeData(NodeId, NodeFormat, SchemData) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE NodeFormat = VALUES(NodeFormat), SchemData = VALUES(SchemData)");
private static final Statement selSchemData = new Statement("SELECT SchemData FROM NodeData WHERE NodeId = ?");
private static final SelectStatement<NodeData> get = table.select(Table.PRIMARY);
public static NodeData get(SchematicNode node) {
if(node.isDir())
throw new IllegalArgumentException("Node is a directory");
return get.select(rs -> {
if(rs.next()) {
return new NodeData(node.getId(), rs.getBoolean("NodeFormat"));
} else {
return new NodeData(node.getId(), false);
}
}, node);
}
@Getter
@Field(keys = {Table.PRIMARY})
private final int nodeId;
@Field
private boolean nodeFormat;
public InputStream schemData() throws IOException {
try {
return selSchemData.select(rs -> {
rs.next();
InputStream schemData = rs.getBinaryStream("SchemData");
try {
if(rs.wasNull() || schemData.available() == 0) {
throw new SecurityException("SchemData is null");
}
return new GZIPInputStream(schemData);
} catch (IOException e) {
throw new SecurityException("SchemData is wrong", e);
}
}, nodeId);
} catch (Exception e) {
throw new IOException(e);
}
}
public void saveFromStream(InputStream blob, boolean newFormat) {
updateDatabase.update(nodeId, newFormat, blob);
nodeFormat = newFormat;
}
public boolean getNodeFormat() {
return nodeFormat;
}
}

Datei anzeigen

@ -26,7 +26,7 @@ import lombok.AllArgsConstructor;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.sql.Timestamp; import java.security.Timestamp;
import java.time.Instant; import java.time.Instant;
@AllArgsConstructor @AllArgsConstructor
@ -63,7 +63,7 @@ public class NodeDownload {
public static String base16encode(byte[] byteArray) { public static String base16encode(byte[] byteArray) {
StringBuilder hexBuffer = new StringBuilder(byteArray.length * 2); StringBuilder hexBuffer = new StringBuilder(byteArray.length * 2);
for (byte b : byteArray) for (byte b : byteArray)
hexBuffer.append(HEX[(b >>> 4) & 0xF]).append(HEX[b & 0xF]); hexBuffer.append(HEX[b >> 4]).append(HEX[b & 0xF]);
return hexBuffer.toString(); return hexBuffer.toString();
} }
} }

Datei anzeigen

@ -24,52 +24,35 @@ import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement; import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table; import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.HashSet; import java.util.HashSet;
import java.util.Optional;
import java.util.Set; import java.util.Set;
@AllArgsConstructor @AllArgsConstructor
public class NodeMember { public class NodeMember {
public static void init() {
// enforce class initialization
}
private static final Table<NodeMember> table = new Table<>(NodeMember.class); private static final Table<NodeMember> table = new Table<>(NodeMember.class);
private static final SelectStatement<NodeMember> getNodeMember = table.select(Table.PRIMARY); private static final SelectStatement<NodeMember> getNodeMember = table.select(Table.PRIMARY);
private static final SelectStatement<NodeMember> getNodeMembers = table.selectFields("NodeId"); private static final SelectStatement<NodeMember> getNodeMembers = table.selectFields("Node");
private static final SelectStatement<NodeMember> getSchematics = table.selectFields("UserId"); private static final SelectStatement<NodeMember> getSchematics = table.selectFields("Member");
private static final Statement create = table.insert(Table.PRIMARY); private static final Statement create = table.insertAll();
private static final Statement delete = table.delete(Table.PRIMARY); private static final Statement delete = table.delete(Table.PRIMARY);
private static final Statement updateParent = table.update(Table.PRIMARY, "ParentId");
@Getter
@Field(keys = {Table.PRIMARY}) @Field(keys = {Table.PRIMARY})
private final int nodeId; private final int node;
@Getter
@Field(keys = {Table.PRIMARY}) @Field(keys = {Table.PRIMARY})
private final int userId; private final int member;
@Field(nullable = true, def = "null")
private Integer parentId;
public int getNode() {
return nodeId;
}
public int getMember() {
return userId;
}
public Optional<Integer> getParent() {
return Optional.ofNullable(parentId);
}
public void delete() { public void delete() {
delete.update(nodeId, userId); delete.update(node, member);
} }
public static NodeMember createNodeMember(int node, int member) { public static NodeMember createNodeMember(int node, int member) {
create.update(node, member); create.update(node, member);
return new NodeMember(node, member, null); return new NodeMember(node, member);
} }
public static NodeMember getNodeMember(int node, int member) { public static NodeMember getNodeMember(int node, int member) {
@ -83,9 +66,4 @@ public class NodeMember {
public static Set<NodeMember> getSchematics(int member) { public static Set<NodeMember> getSchematics(int member) {
return new HashSet<>(getSchematics.listSelect(member)); return new HashSet<>(getSchematics.listSelect(member));
} }
public void setParentId(Integer parentId) {
this.parentId = parentId;
updateParent.update(this.parentId, nodeId, userId);
}
} }

Datei anzeigen

@ -1,77 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 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.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import java.util.HashMap;
import java.util.Map;
@AllArgsConstructor
public class PollAnswer {
@Getter
@Setter
private static String currentPoll;
private static final Table<PollAnswer> table = new Table<>(PollAnswer.class);
private static final SelectStatement<PollAnswer> get = table.select(Table.PRIMARY);
private static final Statement getResults = new Statement("SELECT Count(UserID) AS Times, Answer FROM PollAnswer WHERE Question = ? GROUP BY Answer ORDER BY Times ASC");
private static final Statement insert = table.insertAll();
@Field(keys = {Table.PRIMARY})
private final int userID;
@Field(keys = {Table.PRIMARY})
private final String question;
@Field(def = "0")
private int answer;
public static PollAnswer get(int userID) {
PollAnswer answer = get.select(userID, currentPoll);
if(answer == null)
return new PollAnswer(userID, currentPoll, 0);
return answer;
}
public static Map<Integer, Integer> getCurrentResults() {
return getResults.select(rs -> {
Map<Integer, Integer> retMap = new HashMap<>();
while (rs.next())
retMap.put(rs.getInt("Answer")-1, rs.getInt("Times"));
return retMap;
}, currentPoll);
}
public boolean hasAnswered(){
return answer != 0;
}
public void setAnswer(int answer){
this.answer = answer;
insert.update(userID, question, answer);
}
}

Datei anzeigen

@ -19,16 +19,16 @@
package de.steamwar.sql; package de.steamwar.sql;
import de.steamwar.sql.internal.*; import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.SqlTypeMapper;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.time.Instant;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.Date; import java.util.Date;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -40,49 +40,15 @@ public class Punishment {
SqlTypeMapper.nameEnumMapper(PunishmentType.class); SqlTypeMapper.nameEnumMapper(PunishmentType.class);
} }
public static final Timestamp PERMA_TIME = Timestamp.from(Instant.ofEpochSecond(946674800));
private static final Table<Punishment> table = new Table<>(Punishment.class, "Punishments"); private static final Table<Punishment> table = new Table<>(Punishment.class, "Punishments");
private static final SelectStatement<Punishment> getPunishments = new SelectStatement<>(table, "SELECT * FROM Punishments WHERE PunishmentId IN (SELECT MAX(PunishmentId) FROM Punishments WHERE UserId = ? GROUP BY Type)"); private static final SelectStatement<Punishment> getPunishments = new SelectStatement<>(table, "SELECT * FROM Punishments WHERE PunishmentId IN (SELECT MAX(PunishmentId) FROM Punishments WHERE UserId = ? GROUP BY Type)");
private static final SelectStatement<Punishment> getPunishment = new SelectStatement<>(table, "SELECT * FROM Punishments WHERE UserId = ? AND Type = ? ORDER BY PunishmentId DESC LIMIT 1"); private static final SelectStatement<Punishment> getPunishment = new SelectStatement<>(table, "SELECT * FROM Punishments WHERE UserId = ? AND Type = ? ORDER BY PunishmentId DESC LIMIT 1");
private static final SelectStatement<Punishment> getAllPunishments = new SelectStatement<>(table, "SELECT * FROM Punishments WHERE UserId = ? ORDER BY `PunishmentId` DESC");
private static final Statement insert = table.insertFields(true, "UserId", "Punisher", "Type", "EndTime", "Perma", "Reason");
public static Punishment getPunishmentOfPlayer(int user, PunishmentType type) {
return getPunishment.select(user, type);
}
public static Map<PunishmentType, Punishment> getPunishmentsOfPlayer(int user) {
return getPunishments.listSelect(user).stream().collect(Collectors.toMap(Punishment::getType, punishment -> punishment));
}
public static List<Punishment> getAllPunishmentsOfPlayer(int user) {
return getAllPunishments.listSelect(user);
}
public static boolean isPunished(SteamwarUser user, Punishment.PunishmentType type, Consumer<Punishment> callback) {
Punishment punishment = Punishment.getPunishmentOfPlayer(user.getId(), type);
if(punishment == null || !punishment.isCurrent()) {
return false;
} else {
callback.accept(punishment);
return true;
}
}
public static Punishment createPunishment(int user, int executor, PunishmentType type, String reason, Timestamp endTime, boolean perma) {
if(perma && !endTime.equals(PERMA_TIME)) {
throw new IllegalArgumentException("Permanent punishments must have an end time of `Punishment.PERMA_TIME`");
}
int punishmentId = insert.insertGetKey(user, executor, type.name(), endTime, perma, reason);
return new Punishment(punishmentId, user, executor, type, Timestamp.from(Instant.now()), endTime, perma, reason);
}
@Field(keys = {Table.PRIMARY}, autoincrement = true) @Field(keys = {Table.PRIMARY}, autoincrement = true)
private final int punishmentId; private final int punishmentId;
@Field @Field
@Getter @Getter
private final int userId; private final int user;
@Field @Field
@Getter @Getter
private final int punisher; private final int punisher;
@ -102,6 +68,24 @@ public class Punishment {
@Getter @Getter
private final String reason; private final String reason;
public static Punishment getPunishmentOfPlayer(int user, PunishmentType type) {
return getPunishment.select(user, type);
}
public static Map<PunishmentType, Punishment> getPunishmentsOfPlayer(int user) {
return getPunishments.listSelect(user).stream().collect(Collectors.toMap(Punishment::getType, punishment -> punishment));
}
public static boolean isPunished(SteamwarUser user, Punishment.PunishmentType type, Consumer<Punishment> callback) {
Punishment punishment = Punishment.getPunishmentOfPlayer(user.getId(), type);
if(punishment == null || !punishment.isCurrent()) {
return false;
} else {
callback.accept(punishment);
return true;
}
}
@Deprecated // Not multiling, misleading title @Deprecated // Not multiling, misleading title
public String getBantime(Timestamp endTime, boolean perma) { public String getBantime(Timestamp endTime, boolean perma) {
if (perma) { if (perma) {
@ -116,7 +100,6 @@ public class Punishment {
} }
@AllArgsConstructor @AllArgsConstructor
@RequiredArgsConstructor
@Getter @Getter
public enum PunishmentType { public enum PunishmentType {
Ban(false, "BAN_TEAM", "BAN_PERMA", "BAN_UNTIL", "UNBAN_ERROR", "UNBAN"), Ban(false, "BAN_TEAM", "BAN_PERMA", "BAN_UNTIL", "UNBAN_ERROR", "UNBAN"),
@ -124,10 +107,7 @@ public class Punishment {
NoSchemReceiving(false, "NOSCHEMRECEIVING_TEAM", "NOSCHEMRECEIVING_PERMA", "NOSCHEMRECEIVING_UNTIL", "UNNOSCHEMRECEIVING_ERROR", "UNNOSCHEMRECEIVING"), NoSchemReceiving(false, "NOSCHEMRECEIVING_TEAM", "NOSCHEMRECEIVING_PERMA", "NOSCHEMRECEIVING_UNTIL", "UNNOSCHEMRECEIVING_ERROR", "UNNOSCHEMRECEIVING"),
NoSchemSharing(false, "NOSCHEMSHARING_TEAM", "NOSCHEMSHARING_PERMA", "NOSCHEMSHARING_UNTIL", "UNNOSCHEMSHARING_ERROR", "UNNOSCHEMSHARING"), NoSchemSharing(false, "NOSCHEMSHARING_TEAM", "NOSCHEMSHARING_PERMA", "NOSCHEMSHARING_UNTIL", "UNNOSCHEMSHARING_ERROR", "UNNOSCHEMSHARING"),
NoSchemSubmitting(true, "NOSCHEMSUBMITTING_TEAM", "NOSCHEMSUBMITTING_PERMA", "NOSCHEMSUBMITTING_UNTIL", "UNNOSCHEMSUBMITTING_ERROR", "UNNOSCHEMSUBMITTING"), NoSchemSubmitting(true, "NOSCHEMSUBMITTING_TEAM", "NOSCHEMSUBMITTING_PERMA", "NOSCHEMSUBMITTING_UNTIL", "UNNOSCHEMSUBMITTING_ERROR", "UNNOSCHEMSUBMITTING"),
NoDevServer(true, "NODEVSERVER_TEAM", "NODEVSERVER_PERMA", "NODEVSERVER_UNTIL", "UNNODEVSERVER_ERROR", "UNNODEVSERVER"), NoDevServer(true, "NODEVSERVER_TEAM", "NODEVSERVER_PERMA", "NODEVSERVER_UNTIL", "UNNODEVSERVER_ERROR", "UNNODEVSERVER");
NoFightServer(false, "NOFIGHTSERVER_TEAM", "NOFIGHTSERVER_PERMA", "NOFIGHTSERVER_UNTIL", "UNNOFIGHTSERVER_ERROR", "UNNOFIGHTSERVER"),
NoTeamServer(true, "NOTEAMSERVER_TEAM", "NOTEAMSERVER_PERMA", "NOTEAMSERVER_UNTIL", "UNNOTEAMSERVER_ERROR", "UNNOTEAMSERVER"),
Note(false, "NOTE_TEAM", null, null, null, null, true);
private final boolean needsAdmin; private final boolean needsAdmin;
private final String teamMessage; private final String teamMessage;
@ -135,6 +115,5 @@ public class Punishment {
private final String playerMessageUntil; private final String playerMessageUntil;
private final String usageNotPunished; private final String usageNotPunished;
private final String unpunishmentMessage; private final String unpunishmentMessage;
private boolean multi = false;
} }
} }

Datei anzeigen

@ -1,44 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2024 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.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import java.util.Set;
import java.util.stream.Collectors;
@AllArgsConstructor
public class Referee {
private static final Table<Referee> table = new Table<>(Referee.class);
private static final SelectStatement<Referee> byEvent = table.selectFields("eventID");
public static Set<Integer> get(int eventID) {
return byEvent.listSelect(eventID).stream().map(referee -> referee.userID).collect(Collectors.toSet());
}
@Field(keys = {"eventReferee"})
private final int eventID;
@Field(keys = {"eventReferee"})
private final int userID;
}

Datei anzeigen

@ -28,7 +28,6 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.sql.SQLException; import java.sql.SQLException;
@AllArgsConstructor @AllArgsConstructor
@ -37,9 +36,9 @@ public class Replay {
static { static {
new SqlTypeMapper<>(File.class, "BLOB", (rs, identifier) -> { new SqlTypeMapper<>(File.class, "BLOB", (rs, identifier) -> {
try { try {
File file = File.createTempFile("replay", ".replay"); File file = File.createTempFile("replay", "replay");
file.deleteOnExit(); file.deleteOnExit();
Files.copy(rs.getBinaryStream(identifier), file.toPath(), StandardCopyOption.REPLACE_EXISTING); Files.copy(rs.getBinaryStream(identifier), file.toPath());
return file; return file;
} catch (IOException e) { } catch (IOException e) {
throw new SQLException(e); throw new SQLException(e);

Datei anzeigen

@ -25,25 +25,16 @@ import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import java.io.File; import java.io.File;
import java.sql.Timestamp;
@AllArgsConstructor @AllArgsConstructor
public class SWException { public class SWException {
public static void init() {
// force class initialialisation
}
private static final String CWD = System.getProperty("user.dir"); private static final String CWD = System.getProperty("user.dir");
private static final String SERVER_NAME = new File(CWD).getName(); private static final String SERVER_NAME = new File(CWD).getName();
private static final Table<SWException> table = new Table<>(SWException.class, "Exception"); private static final Table<SWException> table = new Table<>(SWException.class, "Exception");
private static final Statement insert = table.insertFields(true, "server", "message", "stacktrace"); private static final Statement insert = table.insertAll();
@Field(keys = {Table.PRIMARY}, autoincrement = true)
private final int id;
@Field(def = "CURRENT_TIMESTAMP")
private final Timestamp time;
@Field @Field
private final String server; private final String server;
@Field @Field
@ -52,17 +43,10 @@ public class SWException {
private final String stacktrace; private final String stacktrace;
public static void log(String message, String stacktrace){ public static void log(String message, String stacktrace){
insert.update(SERVER_NAME, generateMessage(message), stacktrace);
}
public static int logGetId(String message, String stacktrace) {
return insert.insertGetKey(SERVER_NAME, generateMessage(message), stacktrace);
}
private static String generateMessage(String message) {
StringBuilder msgBuilder = new StringBuilder(message); StringBuilder msgBuilder = new StringBuilder(message);
SQLWrapper.impl.additionalExceptionMetadata(msgBuilder); SQLWrapper.impl.additionalExceptionMetadata(msgBuilder);
msgBuilder.append("\nCWD: ").append(CWD); msgBuilder.append("\nCWD: ").append(CWD);
return msgBuilder.toString();
insert.update(SERVER_NAME, msgBuilder.toString(), stacktrace);
} }
} }

Datei anzeigen

@ -21,31 +21,15 @@ package de.steamwar.sql;
import de.steamwar.sql.internal.Field; import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement; import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table; import de.steamwar.sql.internal.Table;
import de.steamwar.sql.v2.SchematicNode;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@AllArgsConstructor @AllArgsConstructor
public class SchemElo { public class SchemElo {
private static final int ELO_DEFAULT = 1000;
private static final Table<SchemElo> table = new Table<>(SchemElo.class); private static final Table<SchemElo> table = new Table<>(SchemElo.class);
private static final SelectStatement<SchemElo> select = table.select(Table.PRIMARY); private static final SelectStatement<SchemElo> select = table.select(Table.PRIMARY);
private static final Statement setElo = table.insertAll();
public static int getElo(SchematicNode node, int season) {
SchemElo elo = select.select(node, season);
return elo != null ? elo.elo : 0;
}
public static int getCurrentElo(int schemID) {
SchemElo elo = select.select(schemID, Season.getSeason());
return elo != null ? elo.elo : ELO_DEFAULT;
}
public static void setElo(int schemID, int elo) {
setElo.update(schemID, elo, Season.getSeason());
}
@Field(keys = {Table.PRIMARY}) @Field(keys = {Table.PRIMARY})
private final int schemId; private final int schemId;
@ -53,4 +37,8 @@ public class SchemElo {
private final int elo; private final int elo;
@Field(keys = {Table.PRIMARY}) @Field(keys = {Table.PRIMARY})
private final int season; private final int season;
public static int getElo(SchematicNode node, int season) {
return select.select(node, season).elo;
}
} }

Datei anzeigen

@ -19,173 +19,43 @@
package de.steamwar.sql; package de.steamwar.sql;
import de.steamwar.sql.internal.*;
import lombok.Getter;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.time.Instant;
import java.util.*; import java.util.*;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors;
@Deprecated
public class SchematicNode { public class SchematicNode {
static { private static int nullableInt(Integer i) {
SchematicType.Normal.name(); // Ensure SchematicType is loaded. return i == null ? 0 : i;
new SqlTypeMapper<>(SchematicNode.class, null, (rs, identifier) -> { throw new SecurityException("SchematicNode cannot be used as type (recursive select)"); }, (st, index, value) -> st.setInt(index, value.nodeId));
} }
private final de.steamwar.sql.v2.SchematicNode node;
private static final Map<Integer, Map<String, List<String>>> TAB_CACHE = new HashMap<>(); private final Map<Integer, String> brCache = new HashMap<>();
public static void clear() {
TAB_CACHE.clear();
}
private static final String nodeSelector = "SELECT NodeId, NodeOwner, NodeOwner AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay FROM SchematicNode "; public SchematicNode(de.steamwar.sql.v2.SchematicNode node) {
this.node = node;
private static final Table<SchematicNode> table = new Table<>(SchematicNode.class);
private static final Statement create = table.insertFields(true, "NodeOwner", "NodeName", "ParentNode", "NodeItem", "NodeType");
private static final Statement update = table.update(Table.PRIMARY, "NodeName", "ParentNode", "NodeItem", "NodeType", "NodeRank", "ReplaceColor", "AllowReplay");
private static final Statement delete = table.delete(Table.PRIMARY);
private static final SelectStatement<SchematicNode> byId = new SelectStatement<>(table, nodeSelector + "WHERE NodeId = ?");
private static final SelectStatement<SchematicNode> byOwnerNameParent = new SelectStatement<>(table, nodeSelector + "WHERE NodeOwner = ? AND NodeName = ? AND ParentNode " + Statement.NULL_SAFE_EQUALS + "?");
private static final SelectStatement<SchematicNode> byParent = new SelectStatement<>(table, nodeSelector + "WHERE ParentNode" + Statement.NULL_SAFE_EQUALS + "? ORDER BY NodeName");
private static final SelectStatement<SchematicNode> dirsByParent = new SelectStatement<>(table, nodeSelector + "WHERE ParentNode" + Statement.NULL_SAFE_EQUALS + "? AND NodeType is NULL ORDER BY NodeName");
private static final SelectStatement<SchematicNode> byOwnerType = new SelectStatement<>(table, nodeSelector + "WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName");
private static final SelectStatement<SchematicNode> byType = new SelectStatement<>(table, nodeSelector + "WHERE NodeType = ? ORDER BY NodeName");
private static final SelectStatement<SchematicNode> all = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? ORDER BY NodeName");
private static final SelectStatement<SchematicNode> list = new SelectStatement<>(table, "SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, NM.ParentId AS ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay FROM SchematicNode INNER JOIN NodeMember NM on SchematicNode.NodeId = NM.NodeId WHERE NM.ParentId " + Statement.NULL_SAFE_EQUALS + "? AND NM.UserId = ? UNION ALL SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay FROM SchematicNode WHERE (? IS NULL AND ParentNode IS NULL AND NodeOwner = ?) OR (? IS NOT NULL AND ParentNode = ?) ORDER BY NodeName");
private static final SelectStatement<SchematicNode> byParentName = new SelectStatement<>(table, "SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, NM.ParentId AS ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay FROM SchematicNode INNER JOIN NodeMember NM on SchematicNode.NodeId = NM.NodeId WHERE NM.ParentId " + Statement.NULL_SAFE_EQUALS + "? AND NM.UserId = ? AND SchematicNode.NodeName = ? UNION ALL SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay FROM SchematicNode WHERE ((? IS NULL AND ParentNode IS NULL AND NodeOwner = ?) OR (? IS NOT NULL AND ParentNode = ?)) AND NodeName = ?");
private static final SelectStatement<SchematicNode> schematicAccessibleForUser = new SelectStatement<>(table, "SELECT COUNT(DISTINCT NodeId) FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeId = ?");
private static final SelectStatement<SchematicNode> accessibleByUserTypeInParent = new SelectStatement<>(table, "WITH RECURSIVE RSASN AS(WITH RECURSIVE RSAN AS (WITH RSANH AS (WITH RECURSIVE RSA AS (SELECT SN.NodeId, NM.ParentId FROM SchematicNode SN LEFT JOIN NodeMember NM on SN.NodeId = NM.NodeId WHERE NM.UserId = ? UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN INNER JOIN RSA ON RSA.NodeId = SN.ParentNode) SELECT * FROM RSA UNION SELECT NodeId, ParentNode FROM SchematicNode WHERE NodeOwner = ?) SELECT * FROM RSANH UNION SELECT SN.NodeId, SN.ParentNode FROM RSANH JOIN SchematicNode SN ON SN.ParentNode = RSANH.NodeId) SELECT RSAN.NodeId, RSAN.ParentId FROM RSAN JOIN SchematicNode SN ON SN.NodeId = RSAN.NodeId WHERE NodeType = ? UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN JOIN RSASN ON SN.NodeId = RSASN.ParentId) SELECT SN.*, ? as EffectiveOwner, RSASN.ParentId AS ParentNode FROM RSASN JOIN SchematicNode SN ON SN.NodeId = RSASN.NodeId WHERE RSASN.ParentId" + Statement.NULL_SAFE_EQUALS + "? ORDER BY NodeName");
private static final SelectStatement<SchematicNode> accessibleByUserType = new SelectStatement<>(table, "WITH RECURSIVE Nodes AS (SELECT NodeId, ParentId as ParentNode FROM NodeMember WHERE UserId = ? UNION SELECT NodeId, ParentNode FROM SchematicNode WHERE NodeOwner = ?), RSN AS ( SELECT NodeId, ParentNode FROM Nodes UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN, RSN WHERE SN.ParentNode = RSN.NodeId ) SELECT SN.*, ? AS EffectiveOwner FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId WHERE NodeType = ?");
private static final SelectStatement<SchematicNode> byIdAndUser = new SelectStatement<>(table, "SELECT NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay FROM SchematicNode WHERE NodeId = ?");
private static final SelectStatement<SchematicNode> allParentsOfNode = new SelectStatement<>(table, "WITH RECURSIVE R AS (SELECT NodeId, ParentNode FROM EffectiveSchematicNode WHERE NodeId = ? AND EffectiveOwner = ? UNION SELECT E.NodeId, E.ParentNode FROM R, EffectiveSchematicNode E WHERE R.ParentNode = E.NodeId AND E.EffectiveOwner = ?) SELECT SN.NodeId, SN.NodeOwner, ? AS EffectiveOwner, SN.NodeName, R.ParentNode, SN.LastUpdate, SN.NodeItem, SN.NodeType, SN.NodeRank, SN.ReplaceColor, SN.AllowReplay FROM R INNER JOIN SchematicNode SN ON SN.NodeId = R.NodeId");
static {
NodeMember.init();
}
@Field(keys = {Table.PRIMARY}, autoincrement = true)
private final int nodeId;
@Field(keys = {"OwnerNameParent"})
private final int nodeOwner;
@Field(def = "0")
@Getter
private final int effectiveOwner;
@Field(keys = {"OwnerNameParent"})
private String nodeName;
@Field(keys = {"OwnerNameParent"}, nullable = true)
private Integer parentNode;
@Field(def = "CURRENT_TIMESTAMP")
private Timestamp lastUpdate;
@Field(def = "''")
private String nodeItem;
@Field(def = "'normal'", nullable = true)
private SchematicType nodeType;
@Field(def = "0")
private int nodeRank;
@Field(def = "1")
private boolean replaceColor;
@Field(def = "1")
private boolean allowReplay;
private String brCache;
public SchematicNode(
int nodeId,
int nodeOwner,
int effectiveOwner,
String nodeName,
Integer parentNode,
Timestamp lastUpdate,
String nodeItem,
SchematicType nodeType,
int nodeRank,
boolean replaceColor,
boolean allowReplay
) {
this.nodeId = nodeId;
this.nodeOwner = nodeOwner;
this.effectiveOwner = effectiveOwner;
this.nodeName = nodeName;
this.parentNode = parentNode;
this.nodeItem = nodeItem;
this.nodeType = nodeType;
this.lastUpdate = lastUpdate;
this.nodeRank = nodeRank;
this.replaceColor = replaceColor;
this.allowReplay = allowReplay;
}
public static List<SchematicNode> getAll(SteamwarUser user) {
return all.listSelect(user);
}
public static Map<Integer, List<SchematicNode>> getAllMap(SteamwarUser user) {
return map(getAll(user));
}
public static List<SchematicNode> list(SteamwarUser user, Integer schematicId) {
return list.listSelect(user, schematicId, user, user, schematicId, user, schematicId, schematicId);
}
public static SchematicNode byParentName(SteamwarUser user, Integer schematicId, String name) {
return byParentName.select(user, schematicId, user, name, user, schematicId, user, schematicId, schematicId, name);
}
public static List<SchematicNode> accessibleByUserType(SteamwarUser user, SchematicType type) {
return accessibleByUserType.listSelect(user, user, user, type);
}
public static Map<Integer, List<SchematicNode>> accessibleByUserTypeMap(SteamwarUser user, SchematicType type) {
return map(accessibleByUserType(user, type));
}
public static boolean schematicAccessibleForUser(SteamwarUser user, Integer schematicId) {
return schematicAccessibleForUser.select(user, schematicId) != null;
}
public static List<SchematicNode> accessibleByUserTypeParent(SteamwarUser user, SchematicType type, Integer parentId) {
return accessibleByUserTypeInParent.listSelect(user, user, type, user, parentId);
}
public static SchematicNode byIdAndUser(SteamwarUser user, Integer id) {
return byIdAndUser.select(user, id);
}
public static List<SchematicNode> parentsOfNode(SteamwarUser user, Integer id) {
return allParentsOfNode.listSelect(id, user, user, user);
}
private static Map<Integer, List<SchematicNode>> map(List<SchematicNode> in) {
Map<Integer, List<SchematicNode>> map = new HashMap<>();
for (SchematicNode effectiveSchematicNode : in) {
map.computeIfAbsent(effectiveSchematicNode.getOptionalParent().orElse(0), k -> new ArrayList<>()).add(effectiveSchematicNode);
}
return map;
} }
public static SchematicNode createSchematic(int owner, String name, Integer parent) { public static SchematicNode createSchematic(int owner, String name, Integer parent) {
return createSchematicNode(owner, name, parent, SchematicType.Normal.toDB(), ""); return new SchematicNode(de.steamwar.sql.v2.SchematicNode.createSchematic(SteamwarUser.get(owner), name, nullableInt(parent)));
} }
public static SchematicNode createSchematicDirectory(int owner, String name, Integer parent) { public static SchematicNode createSchematicDirectory(int owner, String name, Integer parent) {
return createSchematicNode(owner, name, parent, null, ""); return new SchematicNode(de.steamwar.sql.v2.SchematicNode.createSchematicNode(SteamwarUser.get(owner), name, nullableInt(parent), null, ""));
} }
public static SchematicNode createSchematicNode(int owner, String name, Integer parent, String type, String item) { public static SchematicNode createSchematicNode(int owner, String name, Integer parent, String type, String item) {
if (parent != null && parent == 0) return new SchematicNode(de.steamwar.sql.v2.SchematicNode.createSchematicNode(SteamwarUser.get(owner), name, nullableInt(parent), type, item));
parent = null;
int nodeId = create.insertGetKey(owner, name, parent, item, type);
return getSchematicNode(nodeId);
} }
public static SchematicNode getSchematicNode(int owner, String name, SchematicNode parent) { public static SchematicNode getSchematicNode(int owner, String name, SchematicNode parent) {
return getSchematicNode(owner, name, parent.getId()); return de.steamwar.sql.v2.SchematicNode.getSchematicNode(SteamwarUser.get(owner), name, parent.node).map(SchematicNode::new).orElse(null);
} }
public static SchematicNode getSchematicNode(int owner, String name, Integer parent) { public static SchematicNode getSchematicNode(int owner, String name, Integer parent) {
return byOwnerNameParent.select(owner, name, parent); return de.steamwar.sql.v2.SchematicNode.getSchematicNode(SteamwarUser.get(owner), name, de.steamwar.sql.v2.SchematicNode.getSchematicNode(nullableInt(parent))).map(SchematicNode::new).orElse(null);
} }
public static List<SchematicNode> getSchematicNodeInNode(SchematicNode parent) { public static List<SchematicNode> getSchematicNodeInNode(SchematicNode parent) {
@ -193,11 +63,11 @@ public class SchematicNode {
} }
public static List<SchematicNode> getSchematicNodeInNode(Integer parent) { public static List<SchematicNode> getSchematicNodeInNode(Integer parent) {
return byParent.listSelect(parent); return de.steamwar.sql.v2.SchematicNode.getSchematicNodeInNode(de.steamwar.sql.v2.SchematicNode.getSchematicNode(nullableInt(parent))).stream().map(SchematicNode::new).collect(Collectors.toList());
} }
public static List<SchematicNode> getSchematicDirectoryInNode(Integer parent) { public static List<SchematicNode> getSchematicDirectoryInNode(Integer parent) {
return dirsByParent.listSelect(parent); return de.steamwar.sql.v2.SchematicNode.getSchematicDirectoryInNode(de.steamwar.sql.v2.SchematicNode.getSchematicNode(nullableInt(parent))).stream().map(SchematicNode::new).collect(Collectors.toList());
} }
@Deprecated @Deprecated
@ -211,56 +81,44 @@ public class SchematicNode {
} }
public static SchematicNode getSchematicNode(String name, Integer parent) { public static SchematicNode getSchematicNode(String name, Integer parent) {
return byParentName.select(name, parent); return de.steamwar.sql.v2.SchematicNode.getSchematicNode(name, de.steamwar.sql.v2.SchematicNode.getSchematicNode(nullableInt(parent))).map(SchematicNode::new).orElse(null);
} }
public static SchematicNode getSchematicNode(int id) { public static SchematicNode getSchematicNode(int id) {
return byId.select(id); return new SchematicNode(de.steamwar.sql.v2.SchematicNode.getSchematicNode(id));
} }
public static List<SchematicNode> getAccessibleSchematicsOfTypeInParent(int owner, String schemType, Integer parent) { public static List<SchematicNode> getAccessibleSchematicsOfTypeInParent(int owner, String schemType, Integer parent) {
return accessibleByUserTypeParent(SteamwarUser.get(owner), SchematicType.fromDB(schemType), parent); return de.steamwar.sql.v2.SchematicNode.getAccessibleSchematicsOfTypeInParent(SteamwarUser.get(owner), schemType, de.steamwar.sql.v2.SchematicNode.getSchematicNode(nullableInt(parent))).stream().map(SchematicNode::new).collect(Collectors.toList());
} }
public static List<SchematicNode> getAllAccessibleSchematicsOfType(int user, String schemType) { public static List<SchematicNode> getAllAccessibleSchematicsOfType(int user, String schemType) {
return accessibleByUserType(SteamwarUser.get(user), SchematicType.fromDB(schemType)); return de.steamwar.sql.v2.SchematicNode.getAllAccessibleSchematicsOfType(SteamwarUser.get(user), SchematicType.fromDB(schemType)).stream().map(SchematicNode::new).collect(Collectors.toList());
} }
public static List<SchematicNode> getAllSchematicsOfType(int owner, String schemType) { public static List<SchematicNode> getAllSchematicsOfType(int owner, String schemType) {
return byOwnerType.listSelect(owner, schemType); return de.steamwar.sql.v2.SchematicNode.getAllSchematicsOfType(SteamwarUser.get(owner), SchematicType.fromDB(schemType)).stream().map(SchematicNode::new).collect(Collectors.toList());
} }
@Deprecated @Deprecated
public static List<SchematicNode> getAllSchematicsOfType(String schemType) { public static List<SchematicNode> getAllSchematicsOfType(String schemType) {
return byType.listSelect(schemType); return de.steamwar.sql.v2.SchematicNode.getAllSchematicsOfType(SchematicType.fromDB(schemType)).stream().map(SchematicNode::new).collect(Collectors.toList());
} }
public static List<SchematicNode> getAllSchematicsOfType(SchematicType schemType) { public static List<SchematicNode> getAllSchematicsOfType(SchematicType schemType) {
return byType.listSelect(schemType); return de.steamwar.sql.v2.SchematicNode.getAllSchematicsOfType(schemType).stream().map(SchematicNode::new).collect(Collectors.toList());
} }
public static List<SchematicNode> deepGet(Integer parent, Predicate<SchematicNode> filter) { public static List<SchematicNode> deepGet(Integer parent, Predicate<SchematicNode> filter) {
List<SchematicNode> finalList = new ArrayList<>(); return de.steamwar.sql.v2.SchematicNode.deepGet(de.steamwar.sql.v2.SchematicNode.getSchematicNode(nullableInt(parent)), schematicNode -> filter.test(new SchematicNode(schematicNode))).stream().map(SchematicNode::new).collect(Collectors.toList());
List<SchematicNode> nodes = SchematicNode.getSchematicNodeInNode(parent);
nodes.forEach(node -> {
if (node.isDir()) {
finalList.addAll(deepGet(node.getId(), filter));
} else {
if (filter.test(node))
finalList.add(node);
}
});
return finalList;
} }
@Deprecated
public static List<SchematicNode> getSchematicsAccessibleByUser(int user, Integer parent) { public static List<SchematicNode> getSchematicsAccessibleByUser(int user, Integer parent) {
return list(SteamwarUser.get(user), parent); return de.steamwar.sql.v2.SchematicNode.getSchematicsAccessibleByUser(SteamwarUser.get(user), de.steamwar.sql.v2.SchematicNode.getSchematicNode(nullableInt(parent))).stream().map(SchematicNode::new).collect(Collectors.toList());
} }
@Deprecated
public static List<SchematicNode> getAllSchematicsAccessibleByUser(int user) { public static List<SchematicNode> getAllSchematicsAccessibleByUser(int user) {
return getAll(SteamwarUser.get(user)); return de.steamwar.sql.v2.SchematicNode.getAllSchematicsAccessibleByUser(SteamwarUser.get(user)).stream().map(SchematicNode::new).collect(Collectors.toList());
} }
public static List<SchematicNode> getAllParentsOfNode(SchematicNode node) { public static List<SchematicNode> getAllParentsOfNode(SchematicNode node) {
@ -268,292 +126,156 @@ public class SchematicNode {
} }
public static List<SchematicNode> getAllParentsOfNode(int node) { public static List<SchematicNode> getAllParentsOfNode(int node) {
return allParentsOfNode.listSelect(node); return de.steamwar.sql.v2.SchematicNode.getAllParentsOfNode(de.steamwar.sql.v2.SchematicNode.getSchematicNode(node)).stream().map(SchematicNode::new).collect(Collectors.toList());
} }
public static SchematicNode getNodeFromPath(SteamwarUser user, String s) { public static SchematicNode getNodeFromPath(SteamwarUser user, String s) {
if (s.startsWith("/")) { return de.steamwar.sql.v2.SchematicNode.getNodeFromPath(user, s).map(SchematicNode::new).orElse(null);
s = s.substring(1);
}
if (s.endsWith("/")) {
s = s.substring(0, s.length() - 1);
}
if (s.isEmpty()) {
return null;
}
if (s.contains("/")) {
String[] layers = s.split("/");
Optional<SchematicNode> currentNode = Optional.ofNullable(SchematicNode.byParentName(user, null, layers[0]));
for (int i = 1; i < layers.length; i++) {
int finalI = i;
Optional<SchematicNode> node = currentNode.map(effectiveSchematicNode -> SchematicNode.byParentName(user, effectiveSchematicNode.getId(), layers[finalI]));
if (!node.isPresent()) {
return null;
} else {
currentNode = node;
if (!currentNode.map(SchematicNode::isDir).orElse(false) && i != layers.length - 1) {
return null;
}
}
}
return currentNode.orElse(null);
} else {
return SchematicNode.byParentName(user, null, s);
}
} }
public static List<SchematicNode> filterSchems(int user, Predicate<SchematicNode> filter) { public static List<SchematicNode> filterSchems(int user, Predicate<SchematicNode> filter) {
List<SchematicNode> finalList = new ArrayList<>(); return de.steamwar.sql.v2.SchematicNode.filterSchems(SteamwarUser.get(user), schematicNode -> filter.test(new SchematicNode(schematicNode))).stream().map(SchematicNode::new).collect(Collectors.toList());
List<SchematicNode> nodes = getSchematicsAccessibleByUser(user, null);
nodes.forEach(node -> {
if (node.isDir()) {
finalList.addAll(deepGet(node.getId(), filter));
} else {
if (filter.test(node))
finalList.add(node);
} }
});
return finalList; @Deprecated
public static Integer countNodes() {
return -1;
} }
public int getId() { public int getId() {
return nodeId; return node.getId();
} }
public int getOwner() { public int getOwner() {
return nodeOwner; return node.getOwner().getId();
} }
public String getName() { public String getName() {
return nodeName; return node.getName();
} }
public void setName(String name) { public void setName(String name) {
this.nodeName = name; node.setName(name);
updateDB();
} }
public Integer getParent() { public Integer getParent() {
return parentNode; return node.getParent() == null ? null : node.getParent().getId();
}
public Optional<Integer> getOptionalParent() {
return Optional.ofNullable(parentNode);
} }
public void setParent(Integer parent) { public void setParent(Integer parent) {
this.parentNode = parent; node.setParent(de.steamwar.sql.v2.SchematicNode.getSchematicNode(nullableInt(parent)));
updateDB();
} }
public String getItem() { public String getItem() {
if (nodeItem.isEmpty()) { return node.getItem();
return isDir() ? "CHEST" : "CAULDRON_ITEM";
}
return nodeItem;
} }
public void setItem(String item) { public void setItem(String item) {
this.nodeItem = item; node.setItem(item);
updateDB();
} }
@Deprecated @Deprecated
public String getType() { public String getType() {
return nodeType.name(); return node.getSchemtype().toDB();
} }
@Deprecated @Deprecated
public void setType(String type) { public void setType(String type) {
if(isDir()) node.setSchemtype(SchematicType.fromDB(type));
throw new SecurityException("Node is Directory");
this.nodeType = SchematicType.fromDB(type);
updateDB();
} }
public boolean isDir() { public boolean isDir() {
return nodeType == null; return node.isDir();
} }
@Deprecated
public boolean getSchemFormat() { public boolean getSchemFormat() {
if(isDir()) return node.getSchemFormat();
throw new SecurityException("Node is Directory");
return NodeData.get(this).getNodeFormat();
} }
public int getRank() { public int getRank() {
if(isDir()) return node.getRank();
throw new SecurityException("Node is Directory");
return nodeRank;
} }
@Deprecated @Deprecated
public int getRankUnsafe() { public int getRankUnsafe() {
return nodeRank; return node.getRank();
} }
public void setRank(int rank) { public void setRank(int rank) {
if(isDir()) node.setRank(rank);
throw new SecurityException("Node is Directory");
this.nodeRank = rank;
} }
public SchematicType getSchemtype() { public SchematicType getSchemtype() {
if(isDir()) return node.getSchemtype();
throw new SecurityException("Is Directory");
return nodeType;
} }
public void setSchemtype(SchematicType type) { public void setSchemtype(SchematicType type) {
if(isDir()) node.setSchemtype(type);
throw new SecurityException("Is Directory");
this.nodeType = type;
updateDB();
} }
public boolean replaceColor() { public boolean replaceColor() {
return replaceColor; return node.replaceColor();
} }
public void setReplaceColor(boolean replaceColor) { public void setReplaceColor(boolean replaceColor) {
if(isDir()) node.setReplaceColor(replaceColor);
throw new SecurityException("Is Directory");
this.replaceColor = replaceColor;
updateDB();
} }
public boolean allowReplay() { public boolean allowReplay() {
return allowReplay; return node.allowReplay();
} }
public void setAllowReplay(boolean allowReplay) { public void setAllowReplay(boolean allowReplay) {
if(isDir()) node.setAllowReplay(allowReplay);
throw new SecurityException("Is Directory");
this.allowReplay = allowReplay;
updateDB();
} }
public SchematicNode getParentNode() { public SchematicNode getParentNode() {
if(parentNode == null) return null; return node.getParent() == de.steamwar.sql.v2.SchematicNode.ROOT ? null : new SchematicNode(node.getParent());
return SchematicNode.getSchematicNode(parentNode);
} }
public int getElo(int season) { public int getElo(int season) {
return SchemElo.getElo(this, season); return node.getElo(season);
} }
public boolean accessibleByUser(int user) { public boolean accessibleByUser(int user) {
return NodeMember.getNodeMember(nodeId, user) != null; return node.accessibleByUser(user);
} }
public Set<NodeMember> getMembers() { public Set<NodeMember> getMembers() {
return NodeMember.getNodeMembers(nodeId); return node.getMembers();
} }
public Timestamp getLastUpdate() { public Timestamp getLastUpdate() {
return lastUpdate; return node.getLastUpdate();
}
private void updateDB() {
this.lastUpdate = Timestamp.from(Instant.now());
update.update(nodeName, parentNode, nodeItem, nodeType, nodeRank, replaceColor, allowReplay, nodeId);
TAB_CACHE.clear();
} }
public void delete() { public void delete() {
delete.update(nodeId); node.delete();
} }
@Override @Override
public int hashCode() { public int hashCode() {
return nodeId; return node.hashCode();
} }
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (!(obj instanceof SchematicNode)) return node.equals(obj);
return false;
return ((SchematicNode) obj).getId() == nodeId;
} }
public String generateBreadcrumbs(SteamwarUser user) { public String generateBreadcrumbs(SteamwarUser user) {
return byIdAndUser(user, nodeId).generateBreadcrumbs(); return node.generateBreadcrumbs(user);
} }
public String generateBreadcrumbs(String split, SteamwarUser user) { public String generateBreadcrumbs(String split, SteamwarUser user) {
return byIdAndUser(user, nodeId).generateBreadcrumbs(split); return node.generateBreadcrumbs(split, user);
} }
public String generateBreadcrumbs() {
if(brCache == null) {
brCache = generateBreadcrumbs("/");
}
return brCache;
}
public String generateBreadcrumbs(String split) {
StringBuilder builder = new StringBuilder(getName());
Optional<SchematicNode> currentNode = Optional.of(this);
if(currentNode.map(SchematicNode::isDir).orElse(false)) {
builder.append(split);
}
while (currentNode.isPresent()) {
currentNode = currentNode.flatMap(schematicNode -> Optional.ofNullable(NodeMember.getNodeMember(schematicNode.getId(), effectiveOwner)).map(NodeMember::getParent).orElse(schematicNode.getOptionalParent())).map(SchematicNode::getSchematicNode);
currentNode.ifPresent(node -> builder.insert(0, split).insert(0, node.getName()));
}
return builder.toString();
}
private static final List<String> FORBIDDEN_NAMES = Collections.unmodifiableList(Arrays.asList("public"));
public static boolean invalidSchemName(String[] layers) { public static boolean invalidSchemName(String[] layers) {
for (String layer : layers) { return de.steamwar.sql.v2.SchematicNode.invalidSchemName(layers);
if (layer.isEmpty()) {
return true;
}
if (layer.contains("/") ||
layer.contains("\\") ||
layer.contains("<") ||
layer.contains(">") ||
layer.contains("^") ||
layer.contains("°") ||
layer.contains("'") ||
layer.contains("\"") ||
layer.contains(" ")) {
return true;
}
if(FORBIDDEN_NAMES.contains(layer.toLowerCase())) {
return true;
}
}
return false;
} }
public static List<String> getNodeTabcomplete(SteamwarUser user, String s) { public static List<String> getNodeTabcomplete(SteamwarUser user, String s) {
boolean sws = s.startsWith("/"); return de.steamwar.sql.v2.SchematicNode.getNodeTabcomplete(user, s);
if (sws) {
s = s.substring(1);
}
int index = s.lastIndexOf("/");
String cacheKey = index == -1 ? "" : s.substring(0, index);
if(TAB_CACHE.containsKey(user.getId()) && TAB_CACHE.get(user.getId()).containsKey(cacheKey)) {
return new ArrayList<>(TAB_CACHE.get(user.getId()).get(cacheKey));
}
List<String> list = new ArrayList<>();
if (s.contains("/")) {
String preTab = s.substring(0, s.lastIndexOf("/") + 1);
SchematicNode pa = SchematicNode.getNodeFromPath(user, preTab);
if (pa == null) return new ArrayList<>();
List<SchematicNode> nodes = SchematicNode.list(user, pa.getId());
String br = pa.generateBreadcrumbs();
nodes.forEach(node -> list.add((sws ? "/" : "") + br + node.getName() + (node.isDir() ? "/" : "")));
} else {
List<SchematicNode> nodes = SchematicNode.list(user, null);
nodes.forEach(node -> list.add((sws ? "/" : "") + node.getName() + (node.isDir() ? "/" : "")));
}
list.remove("//copy");
TAB_CACHE.computeIfAbsent(user.getId(), integer -> new HashMap<>()).putIfAbsent(cacheKey, list);
return list;
} }
} }

Datei anzeigen

@ -20,13 +20,12 @@
package de.steamwar.sql; package de.steamwar.sql;
import de.steamwar.sql.internal.SqlTypeMapper; import de.steamwar.sql.internal.SqlTypeMapper;
import lombok.Getter;
import java.util.*; import java.util.*;
public class SchematicType { public class SchematicType {
public static final SchematicType Normal = new SchematicType("Normal", "", Type.NORMAL, null, "STONE_BUTTON", false); public static final SchematicType Normal = new SchematicType("Normal", "", Type.NORMAL, null, "STONE_BUTTON");
private static final Map<String, SchematicType> fromDB; private static final Map<String, SchematicType> fromDB;
private static final List<SchematicType> types; private static final List<SchematicType> types;
@ -52,39 +51,24 @@ public class SchematicType {
} }
private final String name; private final String name;
@Getter
private final String kuerzel; private final String kuerzel;
private final Type type; private final Type type;
private final SchematicType checkType; private final SchematicType checkType;
@Getter
private final String material; private final String material;
@Getter
private final Date deadline;
@Getter
private final boolean manualCheck;
SchematicType(String name, String kuerzel, Type type, SchematicType checkType, String material, boolean manualCheck){ SchematicType(String name, String kuerzel, Type type, SchematicType checkType, String material){
this(name, kuerzel, type, checkType, material, null, manualCheck);
}
SchematicType(String name, String kuerzel, Type type, SchematicType checkType, String material, Date deadline, boolean manualCheck){
this.name = name; this.name = name;
this.kuerzel = kuerzel; this.kuerzel = kuerzel;
this.type = type; this.type = type;
this.checkType = checkType; this.checkType = checkType;
this.material = material; this.material = material;
this.deadline = deadline;
this.manualCheck = manualCheck;
} }
public boolean isAssignable(){ public boolean isAssignable(){
return type == Type.NORMAL || (type == Type.FIGHT_TYPE && checkType != null) || !manualCheck; return type == Type.NORMAL || (type == Type.FIGHT_TYPE && checkType != null);
} }
public SchematicType checkType(){ public SchematicType checkType(){
if (!manualCheck) {
return this;
}
return checkType; return checkType;
} }
@ -104,6 +88,14 @@ public class SchematicType {
return name; return name;
} }
public String getKuerzel() {
return kuerzel;
}
public String getMaterial() {
return material;
}
public String toDB(){ public String toDB(){
return name.toLowerCase(); return name.toLowerCase();
} }

Datei anzeigen

@ -1,84 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 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.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
@AllArgsConstructor
@Getter
public class Script {
private static final Table<Script> table = new Table<>(Script.class);
private static final SelectStatement<Script> byId = table.select(Table.PRIMARY);
private static final SelectStatement<Script> byUserName = table.select("nameUser");
private static final SelectStatement<Script> byUser = table.selectFields("userId");
private static final Statement insert = table.insertFields(true, "userId", "name", "code");
private static final Statement updateName = table.update(Table.PRIMARY, "name");
private static final Statement updateCode = table.update(Table.PRIMARY, "code");
private static final Statement delete = table.delete(Table.PRIMARY);
public static Script get(int id) {
return byId.select(id);
}
public static Script get(SteamwarUser user, String name) {
return byUserName.select(user, name);
}
public static Script create(SteamwarUser user, String name, String code) {
return new Script(insert.insertGetKey(user, name, code), user.getId(), name, code);
}
public static List<Script> list(SteamwarUser user) {
return byUser.listSelect(user);
}
@Field(keys = Table.PRIMARY, autoincrement = true)
private final int id;
@Field(keys = "nameUser")
private final int userId;
@Field(keys = "nameUser")
private String name;
@Field
private String code;
public void setName(String name) {
this.name = name;
updateName.update(name, id);
}
public void setCode(String code) {
this.code = code;
updateCode.update(code, id);
}
public void delete() {
delete.update(id);
}
}

Datei anzeigen

@ -32,14 +32,7 @@ public class Season {
public static String getSeasonStart() { public static String getSeasonStart() {
Calendar calendar = Calendar.getInstance(); Calendar calendar = Calendar.getInstance();
int month = calendar.get(Calendar.MONTH); return calendar.get(Calendar.YEAR) + "-" + (calendar.get(Calendar.MONTH) / 4 * 3 + 1) + "-1";
if (month <= 3) {
return calendar.get(Calendar.YEAR) + "-1-1";
} else if (month <= 7) {
return calendar.get(Calendar.YEAR) + "-5-1";
} else {
return calendar.get(Calendar.YEAR) + "-9-1";
}
} }
public static String convertSeasonToString(int season){ public static String convertSeasonToString(int season){

Datei anzeigen

@ -1,45 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 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.Field;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import java.sql.Timestamp;
@AllArgsConstructor
public class Session {
private static final Table<Session> table = new Table<>(Session.class);
private static final Statement insert = table.insert(Table.PRIMARY);
public static void insertSession(int userID, Timestamp startTime){
insert.update(userID, startTime);
}
@Field(keys = {Table.PRIMARY})
private int userId;
@Field(keys = {Table.PRIMARY})
private Timestamp startTime;
@Field(def = "CURRENT_TIMESTAMP")
private Timestamp endTime;
}

Datei anzeigen

@ -21,36 +21,18 @@ package de.steamwar.sql;
import de.steamwar.sql.internal.*; import de.steamwar.sql.internal.*;
import lombok.Getter; import lombok.Getter;
import lombok.SneakyThrows;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.sql.Timestamp;
import java.util.*; import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.stream.Collectors;
public class SteamwarUser { public class SteamwarUser {
private static final SecureRandom random = new SecureRandom();
private static final SecretKeyFactory factory;
static { static {
try {
factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
} catch (NoSuchAlgorithmException e) {
throw new SecurityException(e);
}
new SqlTypeMapper<>(UUID.class, "CHAR(36)", (rs, identifier) -> UUID.fromString(rs.getString(identifier)), (st, index, value) -> st.setString(index, value.toString())); new SqlTypeMapper<>(UUID.class, "CHAR(36)", (rs, identifier) -> UUID.fromString(rs.getString(identifier)), (st, index, value) -> st.setString(index, value.toString()));
new SqlTypeMapper<>(Locale.class, "VARCHAR(32)", (rs, identifier) -> { new SqlTypeMapper<>(Locale.class, "VARCHAR(32)", (rs, identifier) -> {
String l = rs.getString(identifier); String l = rs.getString(identifier);
return l != null ? Locale.forLanguageTag(l) : null; return l != null ? Locale.forLanguageTag(l) : null;
}, (st, index, value) -> st.setString(index, value.toLanguageTag())); }, (st, index, value) -> st.setString(index, value.toLanguageTag()));
SqlTypeMapper.nameEnumMapper(UserGroup.class);
new SqlTypeMapper<>(SteamwarUser.class, null, (rs, identifier) -> { throw new SecurityException("SteamwarUser cannot be used as type (recursive select)"); }, (st, index, value) -> st.setInt(index, value.id)); new SqlTypeMapper<>(SteamwarUser.class, null, (rs, identifier) -> { throw new SecurityException("SteamwarUser cannot be used as type (recursive select)"); }, (st, index, value) -> st.setInt(index, value.id));
} }
@ -62,17 +44,12 @@ public class SteamwarUser {
private static final SelectStatement<SteamwarUser> byDiscord = table.selectFields("DiscordId"); private static final SelectStatement<SteamwarUser> byDiscord = table.selectFields("DiscordId");
private static final SelectStatement<SteamwarUser> byTeam = table.selectFields("Team"); private static final SelectStatement<SteamwarUser> byTeam = table.selectFields("Team");
private static final SelectStatement<SteamwarUser> getServerTeam = new SelectStatement<>(table, "SELECT * FROM UserData WHERE UserGroup != 'Member' AND UserGroup != 'YouTuber'"); private static final SelectStatement<SteamwarUser> getServerTeam = new SelectStatement<>(table, "SELECT * FROM UserData WHERE UserGroup != 'Member' AND UserGroup != 'YouTuber'");
private static final Statement updateName = table.update(Table.PRIMARY, "UserName"); private static final Statement updateName = table.update(Table.PRIMARY, "UserName");
private static final Statement updatePassword = table.update(Table.PRIMARY, "Password");
private static final Statement updateLocale = table.update(Table.PRIMARY, "Locale", "ManualLocale"); private static final Statement updateLocale = table.update(Table.PRIMARY, "Locale", "ManualLocale");
private static final Statement updateTeam = table.update(Table.PRIMARY, "Team"); private static final Statement updateTeam = table.update(Table.PRIMARY, "Team");
private static final Statement updateLeader = table.update(Table.PRIMARY, "Leader"); private static final Statement updateLeader = table.update(Table.PRIMARY, "Leader");
private static final Statement updateDiscord = table.update(Table.PRIMARY, "DiscordId"); private static final Statement updateDiscord = table.update(Table.PRIMARY, "DiscordId");
private static final Statement getPlaytime = new Statement("SELECT SUM(UNIX_TIMESTAMP(EndTime) - UNIX_TIMESTAMP(StartTime)) as Playtime FROM Session WHERE UserID = ?");
private static final Statement getFirstjoin = new Statement("SELECT MIN(StartTime) AS FirstJoin FROM Session WHERE UserID = ?");
private static final Map<Integer, SteamwarUser> usersById = new HashMap<>(); private static final Map<Integer, SteamwarUser> usersById = new HashMap<>();
private static final Map<UUID, SteamwarUser> usersByUUID = new HashMap<>(); private static final Map<UUID, SteamwarUser> usersByUUID = new HashMap<>();
private static final Map<String, SteamwarUser> usersByName = new HashMap<>(); private static final Map<String, SteamwarUser> usersByName = new HashMap<>();
@ -90,7 +67,58 @@ public class SteamwarUser {
return; return;
usersByName.remove(user.getUserName()); usersByName.remove(user.getUserName());
usersByUUID.remove(user.getUUID()); usersByUUID.remove(user.getUUID());
usersByDiscord.remove(user.getDiscordId()); }
@Getter
@Field(keys = {Table.PRIMARY}, autoincrement = true)
private final int id;
@Field(keys = {"uuid"})
private final UUID uuid;
@Getter
@Field
private String userName;
@Getter
@Field(def = "'Member'")
private final UserGroup userGroup;
@Getter
@Field(def = "0")
private int team;
@Field(def = "0")
private boolean leader;
@Field(nullable = true)
private Locale locale;
@Field(def = "0")
private boolean manualLocale;
@Field(keys = {"discordId"}, nullable = true)
private Long discordId;
public SteamwarUser(int id, UUID uuid, String userName, UserGroup userGroup, int team, boolean leader, Locale locale, boolean manualLocale, Long discordId) {
this.id = id;
this.uuid = uuid;
this.userName = userName;
this.userGroup = userGroup;
this.team = team;
this.leader = leader;
this.locale = locale;
this.manualLocale = manualLocale;
this.discordId = discordId != 0 ? discordId : null;
usersById.put(id, this);
usersByName.put(userName.toLowerCase(), this);
usersByUUID.put(uuid, this);
if (this.discordId != null) {
usersByDiscord.put(discordId, this);
}
}
public UUID getUUID() {
return uuid;
}
public Locale getLocale() {
if(locale != null)
return locale;
return Locale.getDefault(); //TODO test correct locale on join (z.B. FightSystem Hotbarkit)
} }
public static SteamwarUser get(String userName){ public static SteamwarUser get(String userName){
@ -120,22 +148,8 @@ public class SteamwarUser {
return byDiscord.select(discordId); return byDiscord.select(discordId);
} }
public static SteamwarUser getOrCreate(UUID uuid, String name, Consumer<UUID> newPlayer, BiConsumer<String, String> nameUpdate) { public static void createOrUpdateUsername(UUID uuid, String userName) {
SteamwarUser user = get(uuid); insert.update(uuid, userName);
if (user != null) {
if (!user.userName.equals(name)) {
updateName.update(name, user.id);
nameUpdate.accept(user.userName, name);
user.userName = name;
}
return user;
} else {
insert.update(uuid, name);
newPlayer.accept(uuid);
return get(uuid);
}
} }
public static List<SteamwarUser> getServerTeam() { public static List<SteamwarUser> getServerTeam() {
@ -145,218 +159,4 @@ public class SteamwarUser {
public static List<SteamwarUser> getTeam(int teamId) { public static List<SteamwarUser> getTeam(int teamId) {
return byTeam.listSelect(teamId); return byTeam.listSelect(teamId);
} }
public static void batchCache(Set<Integer> ids) {
ids.removeIf(usersById::containsKey);
if(ids.isEmpty())
return;
try (SelectStatement<SteamwarUser> batch = new SelectStatement<>(table, "SELECT * FROM UserData WHERE id IN (" + ids.stream().map(Object::toString).collect(Collectors.joining(", ")) + ")")) {
batch.listSelect();
}
}
@Getter
@Field(keys = {Table.PRIMARY}, autoincrement = true)
private final int id;
@Field(keys = {"uuid"})
private final UUID uuid;
@Getter
@Field
private String userName;
@Field(nullable = true)
private String password;
@Getter
@Field(def = "0")
private int team;
@Getter
@Field(def = "0")
private boolean leader;
@Field(nullable = true)
private Locale locale;
@Field(def = "0")
private boolean manualLocale;
@Getter
@Field(keys = {"discordId"}, nullable = true)
private Long discordId;
private Map<Punishment.PunishmentType, Punishment> punishments = null;
private Set<UserPerm> permissions = null;
private UserPerm.Prefix prefix = null;
public SteamwarUser(int id, UUID uuid, String userName, String password, int team, boolean leader, Locale locale, boolean manualLocale, Long discordId) {
this.id = id;
this.uuid = uuid;
this.userName = userName;
this.password = password;
this.team = team;
this.leader = leader;
this.locale = locale;
this.manualLocale = manualLocale;
this.discordId = discordId != null && discordId != 0 ? discordId : null;
usersById.put(id, this);
usersByName.put(userName.toLowerCase(), this);
usersByUUID.put(uuid, this);
if (this.discordId != null) {
usersByDiscord.put(discordId, this);
}
}
public UUID getUUID() {
return uuid;
}
public Locale getLocale() {
if(locale != null)
return locale;
return Locale.getDefault();
}
public Punishment getPunishment(Punishment.PunishmentType type) {
initPunishments();
return punishments.getOrDefault(type, null);
}
public boolean isPunished(Punishment.PunishmentType punishment) {
initPunishments();
if (!punishments.containsKey(punishment)) {
return false;
}
if (!punishments.get(punishment).isCurrent()) {
if (punishment == Punishment.PunishmentType.Ban) {
BannedUserIPs.unbanIPs(id);
}
punishments.remove(punishment);
return false;
}
return true;
}
public boolean hasPerm(UserPerm perm) {
initPerms();
return permissions.contains(perm);
}
public Set<UserPerm> perms() {
initPerms();
return permissions;
}
public UserPerm.Prefix prefix() {
initPerms();
return prefix;
}
public double getOnlinetime() {
return getPlaytime.select(rs -> {
if (rs.next() && rs.getBigDecimal("Playtime") != null)
return rs.getBigDecimal("Playtime").doubleValue();
return 0.0;
}, id);
}
public Timestamp getFirstjoin() {
return getFirstjoin.select(rs -> {
if (rs.next())
return rs.getTimestamp("FirstJoin");
return null;
}, id);
}
public void punish(Punishment.PunishmentType punishment, Timestamp time, String banReason, int from, boolean perma) {
initPunishments();
punishments.remove(punishment);
punishments.put(punishment, Punishment.createPunishment(id, from, punishment, banReason, time, perma));
}
public void setTeam(int team) {
this.team = team;
updateTeam.update(team, id);
setLeader(false);
}
public void setLeader(boolean leader) {
this.leader = leader;
updateLeader.update(leader, id);
}
public void setLocale(Locale locale, boolean manualLocale) {
if (locale == null || (this.manualLocale && !manualLocale))
return;
this.locale = locale;
this.manualLocale = manualLocale;
updateLocale.update(locale.toLanguageTag(), manualLocale, id);
}
public void setDiscordId(Long discordId) {
usersByDiscord.remove(this.discordId);
this.discordId = discordId;
updateDiscord.update(discordId, id);
if (discordId != null) {
usersByDiscord.put(discordId, this);
}
}
public void setPassword(String password) {
try {
byte[] salt = new byte[16];
random.nextBytes(salt);
String saltString = Base64.getEncoder().encodeToString(salt);
byte[] hash = generateHash(password, salt);
String hashString = Base64.getEncoder().encodeToString(hash);
this.password = hashString + ":" + saltString;
updatePassword.update(this.password, id);
} catch (Exception e) {
throw new SecurityException(e);
}
}
public boolean verifyPassword(String password) {
try {
if (this.password == null) {
return false;
}
String[] parts = this.password.split(":");
if (parts.length != 2) {
SQLConfig.impl.getLogger().log(Level.SEVERE ,"Invalid password hash for user {0} ({1})", new Object[]{userName, id});
return false;
}
String hashString = parts[0];
byte[] realHash = Base64.getDecoder().decode(hashString);
String saltString = parts[1];
byte[] salt = Base64.getDecoder().decode(saltString);
byte[] hash = generateHash(password, salt);
return Arrays.equals(realHash, hash);
} catch (Exception e) {
SQLConfig.impl.getLogger().log(Level.SEVERE, "Error while verifying password for user " + userName + " (" + id + ")", e);
return false;
}
}
private byte[] generateHash(String password, byte[] salt)
throws InvalidKeySpecException {
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 512);
return factory.generateSecret(spec).getEncoded();
}
private void initPunishments() {
if(punishments != null)
return;
punishments = Punishment.getPunishmentsOfPlayer(id);
}
private void initPerms() {
if(permissions != null)
return;
permissions = UserPerm.getPerms(id);
prefix = permissions.stream().filter(UserPerm.prefixes::containsKey).findAny().map(UserPerm.prefixes::get).orElse(UserPerm.emptyPrefix);
}
} }

Datei anzeigen

@ -21,120 +21,41 @@ package de.steamwar.sql;
import de.steamwar.sql.internal.Field; import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement; import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table; import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@AllArgsConstructor @AllArgsConstructor
public class Team { public class Team {
private static final Map<Integer, Team> teamCache = new HashMap<>();
public static void clear() {
teamCache.clear();
}
private static final Table<Team> table = new Table<>(Team.class); private static final Table<Team> table = new Table<>(Team.class);
private static final SelectStatement<Team> byId = table.select(Table.PRIMARY); private static final SelectStatement<Team> select = table.select(Table.PRIMARY);
private static final SelectStatement<Team> byName = new SelectStatement<>(table, "SELECT * FROM Team WHERE (lower(TeamName) = ? OR lower(TeamKuerzel) = ?) AND NOT TeamDeleted");
private static final SelectStatement<Team> all = table.selectFields("TeamDeleted");
private static final Statement insert = table.insertFields("TeamKuerzel", "TeamName");
private static final Statement update = table.update(Table.PRIMARY, "TeamKuerzel", "TeamName", "TeamColor", "Address", "Port");
private static final Statement delete = table.update(Table.PRIMARY, "TeamDeleted");
private static final Statement getSize = new Statement("SELECT COUNT(id) FROM UserData WHERE Team = ?");
@Field(keys = {Table.PRIMARY}, autoincrement = true) @Field(keys = {Table.PRIMARY})
@Getter @Getter
private final int teamId; private final int teamId;
@Field @Field
@Getter @Getter
private String teamKuerzel; private final String teamKuerzel;
@Field @Field
@Getter @Getter
private String teamName; private final String teamName;
@Field(def = "'8'") @Field(def = "'8'")
@Getter @Getter
private String teamColor; private final String teamColor;
@Field(nullable = true)
@Getter
private String address;
@Field(def = "'25565'")
@Getter
private int port;
@Field(def = "0")
private boolean teamDeleted;
public static void create(String kuerzel, String name){ private static final Team pub = new Team(0, "PUB", "Öffentlich", "8");
insert.update(kuerzel, name);
}
public static Team get(int id) { public static Team get(int id) {
return teamCache.computeIfAbsent(id, byId::select); if(id == 0)
} return pub;
return select.select(id);
public static Team get(String name){
// No cache lookup due to low frequency use
name = name.toLowerCase();
return byName.select(name, name);
}
public static List<Team> getAll(){
clear();
List<Team> teams = all.listSelect(false);
teams.forEach(team -> teamCache.put(team.getTeamId(), team));
return teams;
} }
public List<Integer> getMembers(){ public List<Integer> getMembers(){
return SteamwarUser.getTeam(teamId).stream().map(SteamwarUser::getId).collect(Collectors.toList()); return SteamwarUser.getTeam(teamId).stream().map(SteamwarUser::getId).collect(Collectors.toList());
} }
public int size(){
return getSize.select(rs -> {
rs.next();
return rs.getInt("COUNT(id)");
}, teamId);
}
public void disband(SteamwarUser user){
user.setLeader(false);
delete.update(true, teamId);
teamCache.remove(teamId);
TeamTeilnahme.deleteFuture(teamId);
}
public void setTeamKuerzel(String teamKuerzel) {
this.teamKuerzel = teamKuerzel;
updateDB();
}
public void setTeamName(String teamName) {
this.teamName = teamName;
updateDB();
}
public void setTeamColor(String teamColor) {
this.teamColor = teamColor;
updateDB();
}
public void setAddress(String address) {
this.address = address;
updateDB();
}
public void setPort(int port) {
this.port = port;
updateDB();
}
private void updateDB(){
update.update(teamKuerzel, teamName, teamColor, address, port, teamId);
}
} }

Datei anzeigen

@ -21,7 +21,6 @@ package de.steamwar.sql;
import de.steamwar.sql.internal.Field; import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement; import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table; import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@ -35,9 +34,6 @@ public class TeamTeilnahme {
private static final SelectStatement<TeamTeilnahme> select = table.select(Table.PRIMARY); private static final SelectStatement<TeamTeilnahme> select = table.select(Table.PRIMARY);
private static final SelectStatement<TeamTeilnahme> selectTeams = table.selectFields("EventID"); private static final SelectStatement<TeamTeilnahme> selectTeams = table.selectFields("EventID");
private static final SelectStatement<TeamTeilnahme> selectEvents = table.selectFields("TeamID"); private static final SelectStatement<TeamTeilnahme> selectEvents = table.selectFields("TeamID");
private static final Statement insert = table.insert(Table.PRIMARY);
private static final Statement delete = table.delete(Table.PRIMARY);
private static final Statement deleteFuture = new Statement("DELETE t FROM TeamTeilnahme t INNER JOIN Event e ON t.EventID = e.EventID WHERE t.TeamID = ? AND e.Start > NOW()");
@Field(keys = {Table.PRIMARY}) @Field(keys = {Table.PRIMARY})
private final int teamId; private final int teamId;
@ -48,18 +44,6 @@ public class TeamTeilnahme {
return select.select(teamID, eventID) != null; return select.select(teamID, eventID) != null;
} }
public static void teilnehmen(int teamID, int eventID){
insert.update(teamID, eventID);
}
public static void notTeilnehmen(int teamID, int eventID){
delete.update(teamID, eventID);
}
public static void deleteFuture(int teamID) {
deleteFuture.update(teamID);
}
public static Set<Team> getTeams(int eventID){ public static Set<Team> getTeams(int eventID){
return selectTeams.listSelect(eventID).stream().map(tt -> Team.get(tt.teamId)).collect(Collectors.toSet()); // suboptimal performance (O(n) database queries) return selectTeams.listSelect(eventID).stream().map(tt -> Team.get(tt.teamId)).collect(Collectors.toSet()); // suboptimal performance (O(n) database queries)
} }

Datei anzeigen

@ -1,110 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 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.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.SneakyThrows;
import lombok.ToString;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.sql.Timestamp;
import java.util.Base64;
import java.util.List;
@AllArgsConstructor
@Getter
@ToString
public class Token {
private static final Table<Token> table = new Table<>(Token.class);
private static final Statement insert = table.insertFields(true, "Name", "Owner", "Hash");
private static final SelectStatement<Token> get = table.select(Table.PRIMARY);
private static final SelectStatement<Token> listUser = table.selectFields("owner");
private static final SelectStatement<Token> getHash = table.selectFields("hash");
private static final Statement delete = table.delete(Table.PRIMARY);
@SneakyThrows
private static String getHash(String code) {
return Base64.getEncoder().encodeToString(MessageDigest.getInstance("SHA-512").digest(code.getBytes()));
}
@SneakyThrows
public static String createToken(String name, SteamwarUser owner) {
SecureRandom random = new SecureRandom();
byte[] bytes = new byte[20];
random.nextBytes(bytes);
String code = Base64.getEncoder().encodeToString(bytes);
String hash = getHash(code);
create(name, owner, hash);
return code;
}
public static Token getTokenByCode(String code) {
String hash = getHash(code);
return get(hash);
}
public static Token create(String name, SteamwarUser owner, String hash) {
int id = insert.insertGetKey(name, owner, hash);
return get(id);
}
public static Token get(int id) {
return get.select(id);
}
public static List<Token> listUser(SteamwarUser owner) {
return listUser.listSelect(owner);
}
public static Token get(String hash) {
return getHash.select(hash);
}
public static void delete(Token id) {
delete.update(id.getId());
}
@Field(keys = Table.PRIMARY, autoincrement = true)
private final int id;
@Field(keys = "NameOwner")
private final String name;
@Field(keys = "NameOwner")
private final int owner;
@Field
private final Timestamp created;
@Field
private final String hash;
public void delete() {
delete(this);
}
public SteamwarUser getOwner() {
return SteamwarUser.get(owner);
}
}

Datei anzeigen

@ -1,94 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 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.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
import java.util.stream.Collectors;
@AllArgsConstructor
public class Tutorial {
private static final Table<Tutorial> table = new Table<>(Tutorial.class);
private static final SelectStatement<Tutorial> by_popularity = new SelectStatement<>(table, "SELECT t.*, AVG(r.Stars) AS Stars FROM Tutorial t LEFT OUTER JOIN TutorialRating r ON t.TutorialID = r.TutorialID WHERE t.Released = ? GROUP BY t.TutorialID ORDER BY SUM(r.Stars) DESC LIMIT ?, ?");
private static final SelectStatement<Tutorial> own = new SelectStatement<>(table, "SELECT t.*, AVG(r.Stars) AS Stars FROM Tutorial t LEFT OUTER JOIN TutorialRating r ON t.TutorialID = r.TutorialID WHERE t.Creator = ? GROUP BY t.TutorialID ORDER BY t.TutorialID ASC LIMIT ?, ?");
private static final SelectStatement<Tutorial> by_creator_name = new SelectStatement<>(table, "SELECT t.*, AVG(r.Stars) AS Stars FROM Tutorial t LEFT OUTER JOIN TutorialRating r ON t.TutorialID = r.TutorialID WHERE t.Creator = ? AND t.Name = ? GROUP BY t.TutorialID");
private static final SelectStatement<Tutorial> by_id = new SelectStatement<>(table, "SELECT t.*, AVG(r.Stars) AS Stars FROM Tutorial t LEFT OUTER JOIN TutorialRating r ON t.TutorialID = r.TutorialID WHERE t.TutorialID = ? GROUP BY t.TutorialID");
private static final Statement rate = new Statement("INSERT INTO TutorialRating (TutorialID, UserID, Stars) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE Stars = VALUES(Stars)");
private static final Statement create = new Statement("INSERT INTO Tutorial (Creator, Name, Item) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE Item = VALUES(Item), Released = 0");
private static final Statement release = table.update(Table.PRIMARY, "released");
private static final Statement delete = table.delete(Table.PRIMARY);
public static List<Tutorial> getPage(int page, int elementsPerPage, boolean released) {
List<Tutorial> tutorials = by_popularity.listSelect(released, page * elementsPerPage, elementsPerPage);
SteamwarUser.batchCache(tutorials.stream().map(tutorial -> tutorial.creator).collect(Collectors.toSet()));
return tutorials;
}
public static List<Tutorial> getOwn(int user, int page, int elementsPerPage) {
return own.listSelect(user, page * elementsPerPage, elementsPerPage);
}
public static Tutorial create(int creator, String name, String item) {
create.update(creator, name, item);
return by_creator_name.select(creator, name);
}
public static Tutorial get(int id) {
return by_id.select(id);
}
@Getter
@Field(keys = {Table.PRIMARY}, autoincrement = true)
private final int tutorialId;
@Getter
@Field(keys = {"CreatorName"})
private final int creator;
@Getter
@Field(keys = {"CreatorName"})
private final String name;
@Getter
@Field(def = "'BOOK'")
private final String item;
@Getter
@Field(def = "0")
private final boolean released;
@Getter
@Field(def = "0") // Not really a field, but necessary for select generation
private final double stars;
public void release() {
release.update(1, tutorialId);
}
public void delete() {
delete.update(tutorialId);
}
public void rate(int user, int rating) {
rate.update(tutorialId, user, rating);
}
}

Datei anzeigen

@ -47,8 +47,7 @@ public class UserConfig {
} }
public static String getConfig(int player, String config) { public static String getConfig(int player, String config) {
UserConfig value = select.select(player, config); return select.select(player, config).value;
return value != null ? value.value : null;
} }
public static void updatePlayerConfig(UUID uuid, String config, String value) { public static void updatePlayerConfig(UUID uuid, String config, String value) {

Datei anzeigen

@ -1,188 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 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.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@AllArgsConstructor
public class UserElo {
private static final int ELO_DEFAULT = 0;
private static final Map<String, Map<Integer, Optional<Integer>>> gameModeUserEloCache = new ConcurrentHashMap<>();
private static final Map<Integer, String> emblemCache = new ConcurrentHashMap<>();
public static void clear() {
gameModeUserEloCache.clear();
emblemCache.clear();
}
private static final Table<UserElo> table = new Table<>(UserElo.class);
private static final SelectStatement<UserElo> getElo = table.select(Table.PRIMARY);
private static final Statement setElo = table.insertAll();
private static final Statement place = new Statement("SELECT COUNT(*) AS Place FROM UserElo WHERE GameMode = ? AND Elo > ? AND Season = ?");
private static final Statement fightsOfSeason = new Statement("SELECT COUNT(*) AS Fights FROM FightPlayer INNER JOIN Fight F on FightPlayer.FightID = F.FightID WHERE UserID = ? AND GameMode = ? AND UNIX_TIMESTAMP(StartTime) + Duration >= UNIX_TIMESTAMP(?)");
@Field(keys = {Table.PRIMARY})
private final int season;
@Field(keys = {Table.PRIMARY})
private final String gameMode;
@Field(keys = {Table.PRIMARY})
private final int userId;
@Field
private final int elo;
public static int getEloOrDefault(int userID, String gameMode) {
return getElo(userID, gameMode).orElse(ELO_DEFAULT);
}
public static Optional<Integer> getElo(int userID, String gameMode) {
return gameModeUserEloCache.computeIfAbsent(gameMode, gm -> new HashMap<>()).computeIfAbsent(userID, uid -> Optional.ofNullable(getElo.select(Season.getSeason(), gameMode, userID)).map(userElo -> userElo.elo));
}
public static int getFightsOfSeason(int userID, String gameMode) {
return fightsOfSeason.select(rs -> {
if (rs.next())
return rs.getInt("Fights");
return 0;
}, userID, gameMode, Season.getSeasonStart());
}
public static void setElo(int userId, String gameMode, int elo) {
emblemCache.remove(userId);
int oldPlacement = getPlacement(getElo(userId, gameMode).orElse(0), gameMode);
int newPlacement = getPlacement(elo, gameMode);
gameModeUserEloCache.getOrDefault(gameMode, Collections.emptyMap()).remove(userId);
if (oldPlacement <= 3 || newPlacement <= 3) {
emblemCache.clear();
}
setElo.update(Season.getSeason(), gameMode, userId, elo);
}
public static int getPlacement(int elo, String gameMode) {
return place.select(rs -> {
if (rs.next())
return rs.getInt("Place") + 1;
return -1;
}, gameMode, elo, Season.getSeason());
}
public static String getEmblem(SteamwarUser user, List<String> rankedModes) {
return emblemCache.computeIfAbsent(user.getId(), userId -> {
int emblemProgression = -1;
for (String mode : rankedModes) {
if (UserElo.getFightsOfSeason(userId, mode) == 0) continue;
int progression = getProgression(userId, mode);
if (progression > emblemProgression) {
emblemProgression = progression;
}
}
return toEmblem(emblemProgression);
});
}
public static String getEmblemProgression(String gameMode, int userId) {
switch (getProgression(userId, gameMode)) {
case -1:
return "§f/ §8 ∧ ❂ III II I";
case 0:
return "§8/ §6 §8∧ ∧ ❂ III II I";
case 1:
return "§8/ §6∧ §8 ∧ ❂ III II I";
case 2:
return "§8/ ∧ §7 §8∧ ∧ ❂ III II I";
case 3:
return "§8/ §7∧ §8 ∧ ❂ III II I";
case 4:
return "§8/ ∧ §e §8∧ ❂ III II I";
case 5:
return "§8/ §e∧ §8❂ III II I";
case 6:
return "§8/ ∧ §5❂ §8III II I";
case 7:
return "§8/ ∧ ❂ §5III §8II I";
case 8:
return "§8/ ∧ ❂ III §5II §8I";
case 9:
return "§8/ ∧ ❂ III II §5I";
default:
throw new SecurityException("Progression is not in range");
}
}
public static int getProgression(int userId, String gameMode) {
int elo = getElo(userId, gameMode).orElse(-1);
if (elo < 0) return -1;
if (elo <= 100) return 0;
if (elo <= 200) return 1;
if (elo <= 400) return 2;
if (elo <= 600) return 3;
if (elo <= 900) return 4;
if (elo <= 1200) return 5;
int placement = getPlacement(elo, gameMode);
if (placement == 1) return 9;
if (placement == 2) return 8;
if (placement == 3) return 7;
return 6;
}
public static String toEmblem(int progression) {
switch(progression) {
case -1:
return "";
case 0:
return "§6 ";
case 1:
return "§6∧ ";
case 2:
return "§7 ";
case 3:
return "§7∧ ";
case 4:
return "§e ";
case 5:
return "§e∧ ";
case 6:
return "§5❂ ";
case 7:
return "§5III ";
case 8:
return "§5II ";
case 9:
return "§5I ";
default:
throw new SecurityException("Progression out of range");
}
}
}

Datei anzeigen

@ -0,0 +1,45 @@
/*
This file is a part of the SteamWar software.
Copyright (C) 2020 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 lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
public enum UserGroup {
Admin("§4", "§e", true, true, true),
Developer("§3", "§f", true, true, true),
Moderator("§c", "§f", true, true, true),
Supporter("§9", "§f", false, true, true),
Builder("§2", "§f", false, true, false),
YouTuber("§5", "§f", false, false, false),
Member("§7", "§7", false, false, false);
@Getter
private final String colorCode;
@Getter
private final String chatColorCode;
@Getter
private final boolean adminGroup;
@Getter
private final boolean teamGroup;
@Getter
private final boolean checkSchematics;
}

Datei anzeigen

@ -1,90 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 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.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.SqlTypeMapper;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.*;
import java.util.stream.Collectors;
public enum UserPerm {
PREFIX_NONE, // special value, not stored in database
PREFIX_YOUTUBER,
PREFIX_GUIDE,
PREFIX_BUILDER,
PREFIX_SUPPORTER,
PREFIX_MODERATOR,
PREFIX_DEVELOPER,
PREFIX_ADMIN,
RESTRICTED_MODS,
COLOR_CHAT,
TEAM,
TICKET_LOG,
BUILD,
CHECK,
MODERATION,
ADMINISTRATION;
public static final Map<UserPerm, Prefix> prefixes;
public static final Prefix emptyPrefix;
static {
SqlTypeMapper.nameEnumMapper(UserPerm.class);
Map<UserPerm, Prefix> p = new EnumMap<>(UserPerm.class);
emptyPrefix = new Prefix("§7", "");
p.put(PREFIX_NONE, emptyPrefix);
p.put(PREFIX_YOUTUBER, new Prefix("§7", "YT"));
p.put(PREFIX_GUIDE, new Prefix("§a", "Guide"));
p.put(PREFIX_SUPPORTER, new Prefix("§6", "Sup"));
p.put(PREFIX_MODERATOR, new Prefix("§6", "Mod"));
p.put(PREFIX_BUILDER, new Prefix("§e", "Arch"));
p.put(PREFIX_DEVELOPER, new Prefix("§e", "Dev"));
p.put(PREFIX_ADMIN, new Prefix("§e", "Admin"));
prefixes = Collections.unmodifiableMap(p);
}
private static final Table<UserPermTable> table = new Table<>(UserPermTable.class, "UserPerm");
private static final SelectStatement<UserPermTable> getPerms = table.selectFields("user");
public static Set<UserPerm> getPerms(int user) {
return getPerms.listSelect(user).stream().map(up -> up.perm).collect(Collectors.toSet());
}
@Getter
@AllArgsConstructor
public static class Prefix {
private final String colorCode;
private final String chatPrefix;
}
@AllArgsConstructor
public static class UserPermTable {
@Field(keys = {Table.PRIMARY})
private final int user;
@Field(keys = {Table.PRIMARY})
private final UserPerm perm;
}
}

Datei anzeigen

@ -31,7 +31,7 @@ public class SelectStatement<T> extends Statement {
private final Table<T> table; private final Table<T> table;
SelectStatement(Table<T> table, String... kfields) { SelectStatement(Table<T> table, String... kfields) {
this(table, "SELECT " + Arrays.stream(table.fields).map(f -> f.identifier).collect(Collectors.joining(", ")) + " FROM " + table.name + " WHERE " + Arrays.stream(kfields).map(f -> f + " = ?").collect(Collectors.joining(" AND "))); this(table, "SELECT " + Arrays.stream(table.fields).map(f -> f.identifier).collect(Collectors.joining(", ")) + " FROM " + table.name + " WHERE " + Arrays.stream(kfields).map(f -> f + " = ?").collect(Collectors.joining(", ")));
} }
public SelectStatement(Table<T> table, String sql) { public SelectStatement(Table<T> table, String sql) {

Datei anzeigen

@ -19,7 +19,6 @@
package de.steamwar.sql.internal; package de.steamwar.sql.internal;
import java.io.InputStream;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
@ -31,13 +30,8 @@ import java.util.Map;
public final class SqlTypeMapper<T> { public final class SqlTypeMapper<T> {
private static final Map<Class<?>, SqlTypeMapper<?>> mappers = new IdentityHashMap<>(); private static final Map<Class<?>, SqlTypeMapper<?>> mappers = new IdentityHashMap<>();
public static <T> SqlTypeMapper<T> getMapper(Class<?> clazz) { public static <T> SqlTypeMapper<T> getMapper(Class<T> clazz) {
SqlTypeMapper<T> result = (SqlTypeMapper<T>) mappers.get(clazz); return (SqlTypeMapper<T>) mappers.get(clazz);
if(result == null)
throw new SecurityException("Unregistered mapper requested: " + clazz.getName());
return result;
} }
public static <T extends Enum<T>> void ordinalEnumMapper(Class<T> type) { public static <T extends Enum<T>> void ordinalEnumMapper(Class<T> type) {
@ -53,7 +47,7 @@ public final class SqlTypeMapper<T> {
public static <T extends Enum<T>> void nameEnumMapper(Class<T> type) { public static <T extends Enum<T>> void nameEnumMapper(Class<T> type) {
new SqlTypeMapper<>( new SqlTypeMapper<>(
type, type,
"VARCHAR(" + Arrays.stream(type.getEnumConstants()).map(e -> e.name().length()).max(Integer::compareTo).orElse(0) + ")", "VARCHAR(" + Arrays.stream(type.getEnumConstants()).map(e -> e.name().length()).max(Integer::compareTo) + ")",
(rs, identifier) -> Enum.valueOf(type, rs.getString(identifier)), (rs, identifier) -> Enum.valueOf(type, rs.getString(identifier)),
(st, index, value) -> st.setString(index, value.name()) (st, index, value) -> st.setString(index, value.name())
); );
@ -63,13 +57,12 @@ public final class SqlTypeMapper<T> {
primitiveMapper(boolean.class, Boolean.class, "BOOLEAN", ResultSet::getBoolean, PreparedStatement::setBoolean); primitiveMapper(boolean.class, Boolean.class, "BOOLEAN", ResultSet::getBoolean, PreparedStatement::setBoolean);
primitiveMapper(byte.class, Byte.class, "INTEGER(1)", ResultSet::getByte, PreparedStatement::setByte); primitiveMapper(byte.class, Byte.class, "INTEGER(1)", ResultSet::getByte, PreparedStatement::setByte);
primitiveMapper(short.class, Short.class, "INTEGER(2)", ResultSet::getShort, PreparedStatement::setShort); primitiveMapper(short.class, Short.class, "INTEGER(2)", ResultSet::getShort, PreparedStatement::setShort);
primitiveMapper(int.class, Integer.class, "INTEGER", ResultSet::getInt, PreparedStatement::setInt); primitiveMapper(int.class, Integer.class, "INTEGER(4)", ResultSet::getInt, PreparedStatement::setInt);
primitiveMapper(long.class, Long.class, "INTEGER(8)", ResultSet::getLong, PreparedStatement::setLong); primitiveMapper(long.class, Long.class, "INTEGER(8)", ResultSet::getLong, PreparedStatement::setLong);
primitiveMapper(float.class, Float.class, "REAL", ResultSet::getFloat, PreparedStatement::setFloat); primitiveMapper(float.class, Float.class, "REAL", ResultSet::getFloat, PreparedStatement::setFloat);
primitiveMapper(double.class, Double.class, "REAL", ResultSet::getDouble, PreparedStatement::setDouble); primitiveMapper(double.class, Double.class, "REAL", ResultSet::getDouble, PreparedStatement::setDouble);
new SqlTypeMapper<>(String.class, "TEXT", ResultSet::getString, PreparedStatement::setString); new SqlTypeMapper<>(String.class, "TEXT", ResultSet::getString, PreparedStatement::setString);
new SqlTypeMapper<>(Timestamp.class, "TIMESTAMP", ResultSet::getTimestamp, PreparedStatement::setTimestamp); new SqlTypeMapper<>(Timestamp.class, "TIMESTAMP", ResultSet::getTimestamp, PreparedStatement::setTimestamp);
new SqlTypeMapper<>(InputStream.class, "BLOB", ResultSet::getBinaryStream, PreparedStatement::setBinaryStream);
} }
private static <T> void primitiveMapper(Class<T> primitive, Class<T> wrapped, String sqlType, SQLReader<T> reader, SQLWriter<T> writer) { private static <T> void primitiveMapper(Class<T> primitive, Class<T> wrapped, String sqlType, SQLReader<T> reader, SQLWriter<T> writer) {

Datei anzeigen

@ -26,7 +26,6 @@ import java.sql.*;
import java.util.*; import java.util.*;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -39,18 +38,15 @@ public class Statement implements AutoCloseable {
private static final int MAX_CONNECTIONS; private static final int MAX_CONNECTIONS;
private static final Supplier<Connection> conProvider; private static final Supplier<Connection> conProvider;
static final Consumer<Table<?>> schemaCreator; static final Consumer<Table<?>> schemaCreator;
static final String ON_DUPLICATE_KEY;
static final UnaryOperator<String> upsertWrapper;
public static final String NULL_SAFE_EQUALS;
private static final boolean MYSQL_MODE; private static final boolean mysqlMode;
private static final boolean PRODUCTION_DATABASE; private static final boolean productionDatabase;
static { static {
File file = new File(System.getProperty("user.home"), "mysql.properties"); File file = new File(System.getProperty("user.home"), "mysql.properties");
MYSQL_MODE = file.exists(); mysqlMode = file.exists();
if(MYSQL_MODE) { if(mysqlMode) {
Properties properties = new Properties(); Properties properties = new Properties();
try { try {
properties.load(new FileReader(file)); properties.load(new FileReader(file));
@ -62,7 +58,7 @@ public class Statement implements AutoCloseable {
String user = properties.getProperty("user"); String user = properties.getProperty("user");
String password = properties.getProperty("password"); String password = properties.getProperty("password");
PRODUCTION_DATABASE = "core".equals(properties.getProperty("database")); productionDatabase = "core".equals(properties.getProperty("database"));
MAX_CONNECTIONS = SQLConfig.impl.maxConnections(); MAX_CONNECTIONS = SQLConfig.impl.maxConnections();
conProvider = () -> { conProvider = () -> {
try { try {
@ -72,9 +68,6 @@ public class Statement implements AutoCloseable {
} }
}; };
schemaCreator = table -> {}; schemaCreator = table -> {};
ON_DUPLICATE_KEY = " ON DUPLICATE KEY UPDATE ";
upsertWrapper = f -> f + " = VALUES(" + f + ")";
NULL_SAFE_EQUALS = " <=> ";
} else { } else {
Connection connection; Connection connection;
@ -85,17 +78,14 @@ public class Statement implements AutoCloseable {
throw new SecurityException("Could not create sqlite connection", e); throw new SecurityException("Could not create sqlite connection", e);
} }
PRODUCTION_DATABASE = false; productionDatabase = false;
MAX_CONNECTIONS = 1; MAX_CONNECTIONS = 1;
conProvider = () -> connection; conProvider = () -> connection;
schemaCreator = Table::ensureExistanceInSqlite; schemaCreator = Table::ensureExistanceInSqlite;
ON_DUPLICATE_KEY = " ON CONFLICT DO UPDATE SET ";
upsertWrapper = f -> f + " = " + f;
NULL_SAFE_EQUALS = " IS ";
} }
} }
private static volatile int connectionBudget = MAX_CONNECTIONS; private static int connectionBudget = MAX_CONNECTIONS;
public static void closeAll() { public static void closeAll() {
synchronized (connections) { synchronized (connections) {
@ -109,11 +99,11 @@ public class Statement implements AutoCloseable {
} }
public static boolean mysqlMode() { public static boolean mysqlMode() {
return MYSQL_MODE; return mysqlMode;
} }
public static boolean productionDatabase() { public static boolean productionDatabase() {
return PRODUCTION_DATABASE; return productionDatabase;
} }
private final boolean returnGeneratedKeys; private final boolean returnGeneratedKeys;
@ -134,11 +124,7 @@ public class Statement implements AutoCloseable {
public <T> T select(ResultSetUser<T> user, Object... objects) { public <T> T select(ResultSetUser<T> user, Object... objects) {
return withConnection(st -> { return withConnection(st -> {
boolean res = st.execute(); ResultSet rs = st.executeQuery();
if(!res) {
throw new SecurityException("No result set for select statement");
}
ResultSet rs = st.getResultSet();
T result = user.use(rs); T result = user.use(rs);
rs.close(); rs.close();
return result; return result;
@ -164,24 +150,23 @@ public class Statement implements AutoCloseable {
private <T> T withConnection(SQLRunnable<T> runnable, Object... objects) { private <T> T withConnection(SQLRunnable<T> runnable, Object... objects) {
Connection connection = aquireConnection(); Connection connection = aquireConnection();
T result;
T result;
try { try {
result = tryWithConnection(connection, runnable, objects); result = tryWithConnection(connection, runnable, objects);
} catch (Throwable e) { } catch (SQLException e) {
if(connectionInvalid(connection)) { try {
if(connection.isClosed() || !connection.isValid(1)) {
closeConnection(connection); closeConnection(connection);
return withConnection(runnable, objects); return withConnection(runnable, objects);
} else { }
synchronized (connections) { } catch (SQLException ex) {
connections.push(connection); closeConnection(connection);
connections.notify(); throw new SecurityException("Could not test connection validity", ex);
} }
throw new SecurityException("Failing sql statement", e); throw new SecurityException("Failing sql statement", e);
} }
}
synchronized (connections) { synchronized (connections) {
connections.push(connection); connections.push(connection);
@ -191,15 +176,6 @@ public class Statement implements AutoCloseable {
return result; return result;
} }
private boolean connectionInvalid(Connection connection) {
try {
return connection.isClosed() || !connection.isValid(1);
} catch (SQLException e) {
logger.log(Level.INFO, "Could not check SQL connection status", e); // No database logging possible at this state
return true;
}
}
private <T> T tryWithConnection(Connection connection, SQLRunnable<T> runnable, Object... objects) throws SQLException { private <T> T tryWithConnection(Connection connection, SQLRunnable<T> runnable, Object... objects) throws SQLException {
PreparedStatement st = cachedStatements.get(connection); PreparedStatement st = cachedStatements.get(connection);
if(st == null) { if(st == null) {
@ -212,10 +188,7 @@ public class Statement implements AutoCloseable {
for (int i = 0; i < objects.length; i++) { for (int i = 0; i < objects.length; i++) {
Object o = objects[i]; Object o = objects[i];
if(o != null)
SqlTypeMapper.getMapper(o.getClass()).write(st, i+1, o); SqlTypeMapper.getMapper(o.getClass()).write(st, i+1, o);
else
st.setNull(i+1, Types.NULL);
} }
return runnable.run(st); return runnable.run(st);
@ -238,7 +211,7 @@ public class Statement implements AutoCloseable {
private static Connection aquireConnection() { private static Connection aquireConnection() {
synchronized (connections) { synchronized (connections) {
while(connections.isEmpty() && connectionBudget <= 0) if(connections.isEmpty() && connectionBudget == 0)
waitOnConnections(); waitOnConnections();
if(!connections.isEmpty()) { if(!connections.isEmpty()) {

Datei anzeigen

@ -53,7 +53,7 @@ public class Table<T> {
keys = Arrays.stream(fields).flatMap(field -> Arrays.stream(field.field.keys())).distinct().collect(Collectors.toMap(Function.identity(), key -> Arrays.stream(fields).filter(field -> Arrays.asList(field.field.keys()).contains(key)).toArray(TableField[]::new))); keys = Arrays.stream(fields).flatMap(field -> Arrays.stream(field.field.keys())).distinct().collect(Collectors.toMap(Function.identity(), key -> Arrays.stream(fields).filter(field -> Arrays.asList(field.field.keys()).contains(key)).toArray(TableField[]::new)));
for (TableField<?> field : fields) { for (TableField<?> field : fields) {
fieldsByIdentifier.put(field.identifier.toLowerCase(), field); fieldsByIdentifier.put(field.identifier, field);
} }
Statement.schemaCreator.accept(this); Statement.schemaCreator.accept(this);
@ -75,7 +75,7 @@ public class Table<T> {
} }
public Statement updateFields(String[] fields, String... kfields) { public Statement updateFields(String[] fields, String... kfields) {
return new Statement("UPDATE " + name + " SET " + Arrays.stream(fields).map(f -> f + " = ?").collect(Collectors.joining(", ")) + " WHERE " + Arrays.stream(kfields).map(f -> f + " = ?").collect(Collectors.joining(" AND "))); return new Statement("UPDATE " + name + " SET " + Arrays.stream(fields).map(f -> f + " = ?").collect(Collectors.joining(", ")) + " WHERE " + Arrays.stream(kfields).map(f -> f + " = ?").collect(Collectors.joining(", ")));
} }
public Statement insert(String name) { public Statement insert(String name) {
@ -91,8 +91,8 @@ public class Table<T> {
} }
public Statement insertFields(boolean returnGeneratedKeys, String... fields) { public Statement insertFields(boolean returnGeneratedKeys, String... fields) {
List<String> nonKeyFields = Arrays.stream(fields).filter(f -> fieldsByIdentifier.get(f.toLowerCase()).field.keys().length == 0).collect(Collectors.toList()); List<String> nonKeyFields = Arrays.stream(fields).filter(f -> fieldsByIdentifier.get(f).field.keys().length == 0).collect(Collectors.toList());
return new Statement("INSERT INTO " + name + " (" + String.join(", ", fields) + ") VALUES (" + Arrays.stream(fields).map(f -> "?").collect(Collectors.joining(", ")) + ")" + (nonKeyFields.isEmpty() ? "" : Statement.ON_DUPLICATE_KEY + nonKeyFields.stream().map(Statement.upsertWrapper).collect(Collectors.joining(", "))), returnGeneratedKeys); return new Statement("INSERT INTO " + name + " (" + String.join(", ", fields) + ") VALUES (" + Arrays.stream(fields).map(f -> "?").collect(Collectors.joining(", ")) + ")" + (nonKeyFields.isEmpty() ? "" : " ON DUPLICATE KEY UPDATE " + nonKeyFields.stream().map(f -> f + " = VALUES(" + f + ")").collect(Collectors.joining(", "))), returnGeneratedKeys);
} }
public Statement delete(String name) { public Statement delete(String name) {
@ -100,15 +100,16 @@ public class Table<T> {
} }
public Statement deleteFields(String... kfields) { public Statement deleteFields(String... kfields) {
return new Statement("DELETE FROM " + name + " WHERE " + Arrays.stream(kfields).map(f -> f + " = ?").collect(Collectors.joining(" AND "))); return new Statement("DELETE FROM " + name + " WHERE " + Arrays.stream(kfields).map(f -> f + " = ?").collect(Collectors.joining(", ")));
} }
void ensureExistanceInSqlite() { void ensureExistanceInSqlite() {
List<TableField<?>> primaryKey = keys.containsKey(PRIMARY) ? Arrays.asList(keys.get(PRIMARY)) : Collections.emptyList();
try (Statement statement = new Statement( try (Statement statement = new Statement(
"CREATE TABLE IF NOT EXISTS " + name + "(" + "CREATE TABLE IF NOT EXISTS " + name + "(" +
Arrays.stream(fields).map(field -> field.identifier + " " + field.mapper.sqlType() + (field.field.nullable() ? " DEFAULT NULL" : " NOT NULL") + (field.field.nullable() || field.field.def().equals("") ? "" : " DEFAULT " + field.field.def())).collect(Collectors.joining(", ")) + Arrays.stream(fields).map(field -> field.identifier + " " + field.mapper.sqlType() + (field.field.nullable() ? " DEFAULT NULL" : " NOT NULL") + (field.field.nullable() || field.field.def().equals("") ? "" : " DEFAULT " + field.field.def()) + (primaryKey.contains(field) ? " PRIMARY KEY" : "") + (field.field.autoincrement() ? " AUTOINCREMENT" : "")).collect(Collectors.joining(", ")) +
keys.entrySet().stream().map(key -> (key.getKey().equals(PRIMARY) ? ", PRIMARY KEY(" : ", UNIQUE (") + Arrays.stream(key.getValue()).map(field -> field.identifier).collect(Collectors.joining(", ")) + ")").collect(Collectors.joining(" ")) + keys.entrySet().stream().filter(entry -> !entry.getKey().equals(PRIMARY)).map(key -> ", UNIQUE (" + Arrays.stream(key.getValue()).map(field -> field.identifier).collect(Collectors.joining(", ")) + ")").collect(Collectors.joining(" ")) +
")")) { ") STRICT, WITHOUT ROWID")) {
statement.update(); statement.update();
} }
} }
@ -126,7 +127,7 @@ public class Table<T> {
private TableField(java.lang.reflect.Field field) { private TableField(java.lang.reflect.Field field) {
this.identifier = field.getName(); this.identifier = field.getName();
this.mapper = SqlTypeMapper.getMapper(field.getType()); this.mapper = (SqlTypeMapper<T>) SqlTypeMapper.getMapper(field.getDeclaringClass());
this.field = field.getAnnotation(Field.class); this.field = field.getAnnotation(Field.class);
} }

Datei anzeigen

@ -0,0 +1,482 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.v2;
import de.steamwar.sql.*;
import de.steamwar.sql.internal.*;
import lombok.AccessLevel;
import lombok.Setter;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class SchematicNode {
static {
new SqlTypeMapper<>(SchematicNode.class, null, (rs, identifier) -> { throw new SecurityException("SchematicNode cannot be used as type (recursive select)"); }, (st, index, value) -> st.setInt(index, value.nodeId));
}
private static final Map<Integer, Map<String, List<String>>> TAB_CACHE = new HashMap<>();
public static void clear() {
TAB_CACHE.clear();
}
private static final String[] fields = {"NodeId", "NodeOwner", "NodeName", "ParentNode", "LastUpdate", "NodeItem", "NodeType", "NodeRank", "ReplaceColor", "AllowReplay", "NodeFormat"};
private static String nodeSelectCreator(String itemPrefix) {
return "SELECT " + Arrays.stream(fields).map(s -> itemPrefix + s).collect(Collectors.joining(", ")) + " FROM SchematicNode ";
}
public static final SchematicNode ROOT = new SchematicNode(0, 0, "ROOT", 0, Timestamp.from(Instant.now()), "", SchematicType.Normal, 0, false, false, false);
private static final Table<SchematicNode> table = new Table<>(SchematicNode.class);
private static final Statement create = table.insertFields(true, "NodeOwner", "NodeName", "ParentNode", "NodeItem", "NodeType");
private static final Statement update = table.insertAll();
private static final Statement delete = table.delete(Table.PRIMARY);
private static final SelectStatement<SchematicNode> byId = table.select(Table.PRIMARY);
private static final SelectStatement<SchematicNode> byOwnerNameParent = table.select("OwnerNameParent");
private static final SelectStatement<SchematicNode> byParent = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode = ? ORDER BY NodeName");
private static final SelectStatement<SchematicNode> dirsByParent = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE ParentNode = ? AND NodeType is NULL ORDER BY NodeName");
private static final SelectStatement<SchematicNode> byParentName = table.selectFields("NodeName", "ParentNode");
private static final SelectStatement<SchematicNode> accessibleByUserTypeParent = new SelectStatement<>(table, "WITH RECURSIVE RSNB AS (WITH RECURSIVE RSN as (" + nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) GROUP BY s.NodeId UNION " + nodeSelectCreator("SN.") + "AS SN, RSN WHERE SN.ParentNode = RSN.NodeId) SELECT * FROM RSN WHERE NodeType = ? UNION " + nodeSelectCreator("SN.") + "AS SN, RSNB WHERE SN.NodeId = RSNB.ParentNode)SELECT * FROM RSNB WHERE ParentNode = ? ORDER BY NodeName");
private static final SelectStatement<SchematicNode> accessibleByUserType = new SelectStatement<>(table, "WITH RECURSIVE RSN as (" + nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) GROUP BY s.NodeId UNION " + nodeSelectCreator("SN.") + "AS SN, RSN WHERE SN.ParentNode = RSN.NodeId) SELECT * FROM RSN WHERE NodeType = ? ORDER BY NodeName");
private static final SelectStatement<SchematicNode> byOwnerType = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName");
private static final SelectStatement<SchematicNode> byType = new SelectStatement<>(table, nodeSelectCreator("") + "WHERE NodeType = ? ORDER BY NodeName");
private static final SelectStatement<SchematicNode> accessibleByUser = new SelectStatement<>(table, nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) AND ((s.NodeOwner = ? AND s.ParentNode IS NULL) OR NOT s.NodeOwner = ?) GROUP BY s.NodeId ORDER BY s.NodeName");
private static final Statement schematicAccessibleForUser = new Statement("WITH RECURSIVE RSN AS (" + nodeSelectCreator("") + "WHERE NodeId = ? UNION " + nodeSelectCreator("SN.") + "SN, RSN WHERE RSN.ParentNode = SN.NodeId) SELECT COUNT(RSN.NodeId) AS `Accessible` FROM RSN LEFT Join NodeMember NM On NM.NodeId = RSN.NodeId WHERE NodeOwner = ? OR UserId = ? LIMIT 1");
private static final SelectStatement<SchematicNode> allAccessibleByUser = new SelectStatement<>(table, "WITH RECURSIVE RSN as (" + nodeSelectCreator("s.") + "s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) GROUP BY s.NodeId UNION " + nodeSelectCreator("SN.") + "AS SN, RSN WHERE SN.ParentNode = RSN.NodeId) SELECT * FROM RSN ORDER BY NodeName");
private static final SelectStatement<SchematicNode> allParentsOfNode = new SelectStatement<>(table, "WITH RECURSIVE RSN AS (" + nodeSelectCreator("") + "WHERE NodeId = ? UNION " + nodeSelectCreator("SN.") + "SN, RSN WHERE RSN.ParentNode = SN.NodeId) SELECT * FROM RSN ORDER BY NodeName");
@Field(keys = {Table.PRIMARY}, autoincrement = true)
private final int nodeId;
@Field(keys = {"OwnerNameParent"})
private final int nodeOwner;
@Field(keys = {"OwnerNameParent"})
private String nodeName;
@Field(keys = {"OwnerNameParent"})
private int parentNode;
@Field(def = "CURRENT_TIMESTAMP")
private Timestamp lastUpdate;
@Field(def = "''")
private String nodeItem;
@Field(def = "'normal'", nullable = true)
private SchematicType nodeType;
@Field(def = "0")
private int nodeRank;
@Field(def = "1")
private boolean replaceColor;
@Field(def = "1")
private boolean allowReplay;
@Setter(AccessLevel.PACKAGE)
@Field(def = "1")
private boolean nodeFormat;
private final Map<Integer, String> brCache = new HashMap<>();
public SchematicNode(
int nodeId,
int nodeOwner,
String nodeName,
int parentNode,
Timestamp lastUpdate,
String nodeItem,
SchematicType nodeType,
int nodeRank,
boolean replaceColor,
boolean allowReplay,
boolean nodeFormat
) {
this.nodeId = nodeId;
this.nodeOwner = nodeOwner;
this.nodeName = nodeName;
this.parentNode = parentNode;
this.nodeItem = nodeItem;
this.nodeType = nodeType;
this.lastUpdate = lastUpdate;
this.nodeRank = nodeRank;
this.replaceColor = replaceColor;
this.allowReplay = allowReplay;
this.nodeFormat = nodeFormat;
}
public static SchematicNode createSchematic(SteamwarUser owner, String name, int parent) {
return createSchematicNode(owner, name, parent, SchematicType.Normal.toDB(), "");
}
public static SchematicNode createSchematicDirectory(SteamwarUser owner, String name, int parent) {
return createSchematicNode(owner, name, parent, null, "");
}
public static SchematicNode createSchematicNode(SteamwarUser owner, String name, int parent, String type, String item) {
int nodeId = create.insertGetKey(owner, name, parent, type, item);
return getSchematicNode(nodeId);
}
public static Optional<SchematicNode> getSchematicNode(SteamwarUser owner, String name, SchematicNode parent) {
return getSchematicNode(owner, name, parent.getId());
}
public static Optional<SchematicNode> getSchematicNode(SteamwarUser owner, String name, int parent) {
return Optional.ofNullable(byOwnerNameParent.select(owner, name, parent));
}
public static List<SchematicNode> getSchematicNodeInNode(SchematicNode parent) {
if(parent.equals(ROOT)) {
rootWarning();
}
return byParent.listSelect(parent);
}
public static List<SchematicNode> getSchematicDirectoryInNode(SchematicNode parent) {
if(parent.equals(ROOT)) {
rootWarning();
}
return dirsByParent.listSelect(parent);
}
public static Optional<SchematicNode> getSchematicNode(String name, SchematicNode parent) {
if(parent.equals(ROOT)) {
rootWarning();
}
return Optional.ofNullable(byParentName.select(name, parent));
}
public static SchematicNode getSchematicNode(int id) {
if(id == 0) return ROOT;
return Optional.ofNullable(byId.select(id)).orElseThrow(() -> new IllegalStateException("Schematic node " + id + " does not exist"));
}
public static List<SchematicNode> getAccessibleSchematicsOfTypeInParent(SteamwarUser owner, String schemType, SchematicNode parent) {
return accessibleByUserTypeParent.listSelect(owner, owner, schemType, parent);
}
public static List<SchematicNode> getAllAccessibleSchematicsOfType(SteamwarUser user, SchematicType schemType) {
return accessibleByUserType.listSelect(user, user, schemType);
}
public static List<SchematicNode> getAllSchematicsOfType(SteamwarUser owner, SchematicType schemType) {
return byOwnerType.listSelect(owner, schemType);
}
public static List<SchematicNode> getAllSchematicsOfType(SchematicType schemType) {
return byType.listSelect(schemType);
}
public static List<SchematicNode> deepGet(SchematicNode parent, Predicate<SchematicNode> filter) {
List<SchematicNode> finalList = new ArrayList<>();
List<SchematicNode> nodes = SchematicNode.getSchematicNodeInNode(parent);
nodes.forEach(node -> {
if (node.isDir()) {
finalList.addAll(deepGet(node, filter));
} else {
if (filter.test(node))
finalList.add(node);
}
});
return finalList;
}
public static List<SchematicNode> getSchematicsAccessibleByUser(SteamwarUser user, SchematicNode parent) {
if (parent.equals(ROOT)) {
return accessibleByUser.listSelect(user, user, user, user);
}
if(Boolean.TRUE.equals(schematicAccessibleForUser.select(rs -> {
rs.next();
return rs.getBoolean("Accessible");
}, parent, user, user)))
return getSchematicNodeInNode(parent);
return Collections.emptyList();
}
public static List<SchematicNode> getAllSchematicsAccessibleByUser(SteamwarUser user) {
return allAccessibleByUser.listSelect(user, user);
}
public static List<SchematicNode> getAllParentsOfNode(SchematicNode node) {
return allParentsOfNode.listSelect(node);
}
public static Optional<SchematicNode> getNodeFromPath(SteamwarUser user, String s) {
if (s.startsWith("/")) {
s = s.substring(1);
}
if (s.isEmpty()) {
return Optional.empty();
}
if (s.contains("/")) {
String[] layers = s.split("/");
SchematicNode currentNode = null;
for (int i = 0; i < layers.length; i++) {
int finalI = i;
Optional<SchematicNode> node;
if (currentNode == null) {
node = SchematicNode.getSchematicsAccessibleByUser(user, ROOT).stream().filter(node1 -> node1.getName().equals(layers[finalI])).findAny();
} else {
node = SchematicNode.getSchematicNode(layers[i], currentNode);
}
if (!node.isPresent()) {
return Optional.empty();
} else {
currentNode = node.get();
if (!currentNode.isDir() && i != layers.length - 1) {
return Optional.empty();
}
}
}
return Optional.ofNullable(currentNode);
} else {
String finalS = s;
return SchematicNode.getSchematicsAccessibleByUser(user, ROOT).stream().filter(node1 -> node1.getName().equals(finalS)).findAny();
}
}
public static List<SchematicNode> filterSchems(SteamwarUser user, Predicate<SchematicNode> filter) {
List<SchematicNode> finalList = new ArrayList<>();
List<SchematicNode> nodes = getSchematicsAccessibleByUser(user, ROOT);
nodes.forEach(node -> {
if (node.isDir()) {
finalList.addAll(deepGet(node, filter));
} else {
if (filter.test(node))
finalList.add(node);
}
});
return finalList;
}
public int getId() {
return nodeId;
}
public SteamwarUser getOwner() {
return SteamwarUser.get(nodeOwner);
}
public String getName() {
return nodeName;
}
public void setName(String name) {
this.nodeName = name;
updateDB();
}
public SchematicNode getParent() {
return SchematicNode.getSchematicNode(parentNode);
}
public void setParent(SchematicNode parent) {
this.parentNode = parent.nodeId;
updateDB();
}
public String getItem() {
if (nodeItem.isEmpty()) {
return isDir() ? "CHEST" : "CAULDRON_ITEM";
}
return nodeItem;
}
public void setItem(String item) {
this.nodeItem = item;
updateDB();
}
public boolean isDir() {
return nodeType == null;
}
public boolean getSchemFormat() {
throwIfDir();
return nodeFormat;
}
public int getRank() {
throwIfDir();
return nodeRank;
}
public void setRank(int rank) {
throwIfDir();
this.nodeRank = rank;
}
public SchematicType getSchemtype() {
throwIfDir();
return nodeType;
}
public void setSchemtype(SchematicType type) {
throwIfDir();
this.nodeType = type;
updateDB();
}
public boolean replaceColor() {
return replaceColor;
}
public void setReplaceColor(boolean replaceColor) {
throwIfDir();
this.replaceColor = replaceColor;
updateDB();
}
public boolean allowReplay() {
return allowReplay;
}
public void setAllowReplay(boolean allowReplay) {
throwIfDir();
this.allowReplay = allowReplay;
updateDB();
}
public int getElo(int season) {
return SchemElo.getElo(this, season);
}
public boolean accessibleByUser(int user) {
return NodeMember.getNodeMember(nodeId, user) != null;
}
public Set<NodeMember> getMembers() {
return NodeMember.getNodeMembers(nodeId);
}
public Timestamp getLastUpdate() {
return lastUpdate;
}
private void updateDB() {
this.lastUpdate = Timestamp.from(Instant.now());
update.update(nodeId, nodeOwner, nodeName, parentNode, nodeItem, nodeType, lastUpdate, nodeRank, replaceColor, allowReplay, nodeFormat);
this.brCache.clear();
TAB_CACHE.clear();
}
public void delete() {
delete.update(nodeId);
}
@Override
public int hashCode() {
return nodeId;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof SchematicNode))
return false;
return ((SchematicNode) obj).getId() == nodeId;
}
public String generateBreadcrumbs(SteamwarUser user) {
return brCache.computeIfAbsent(user.getId(), integer -> generateBreadcrumbs("/", user));
}
public String generateBreadcrumbs(String split, SteamwarUser user) {
StringBuilder builder = new StringBuilder(getName());
SchematicNode currentNode = this;
if (currentNode.isDir()) builder.append("/");
final Set<NodeMember> nodeMembers = NodeMember.getSchematics(user.getId());
AtomicInteger i = new AtomicInteger();
i.set(currentNode.getId());
while (!currentNode.getParent().equals(ROOT) && nodeMembers.stream().noneMatch(nodeMember -> nodeMember.getNode() == i.get())) {
currentNode = currentNode.getParent();
i.set(currentNode.getId());
builder.insert(0, split)
.insert(0, currentNode.getName());
}
return builder.toString();
}
private static final List<String> FORBIDDEN_NAMES = Collections.unmodifiableList(Arrays.asList("public"));
public static boolean invalidSchemName(String[] layers) {
for (String layer : layers) {
if (layer.isEmpty()) {
return true;
}
if (layer.contains("/") ||
layer.contains("\\") ||
layer.contains("<") ||
layer.contains(">") ||
layer.contains("^") ||
layer.contains("°") ||
layer.contains("'") ||
layer.contains("\"") ||
layer.contains(" ")) {
return true;
}
if(FORBIDDEN_NAMES.contains(layer.toLowerCase())) {
return true;
}
}
return false;
}
public static List<String> getNodeTabcomplete(SteamwarUser user, String s) {
boolean sws = s.startsWith("/");
if (sws) {
s = s.substring(1);
}
int index = s.lastIndexOf("/");
String cacheKey = index == -1 ? "" : s.substring(0, index);
if(TAB_CACHE.containsKey(user.getId()) && TAB_CACHE.get(user.getId()).containsKey(cacheKey)) {
return new ArrayList<>(TAB_CACHE.get(user.getId()).get(cacheKey));
}
List<String> list = new ArrayList<>();
if (s.contains("/")) {
String preTab = s.substring(0, s.lastIndexOf("/") + 1);
Optional<SchematicNode> pa = SchematicNode.getNodeFromPath(user, preTab);
if (!pa.isPresent()) return Collections.emptyList();
List<SchematicNode> nodes = pa.map(SchematicNode::getSchematicNodeInNode).orElseGet(Collections::emptyList);
nodes.forEach(node -> list.add((sws ? "/" : "") + node.generateBreadcrumbs(user)));
} else {
List<SchematicNode> nodes = SchematicNode.getSchematicsAccessibleByUser(user, ROOT);
nodes.forEach(node -> list.add((sws ? "/" : "") + node.getName() + (node.isDir() ? "/" : "")));
}
list.remove("//copy");
TAB_CACHE.computeIfAbsent(user.getId(), integer -> new HashMap<>()).putIfAbsent(cacheKey, list);
return list;
}
private static void rootWarning() {
ByteArrayOutputStream stacktraceOutput = new ByteArrayOutputStream();
new Throwable().printStackTrace(new PrintStream(stacktraceOutput));
SWException.log("PERFORMANCE!!! Getting all/weird subset of schematic nodes with parent NULL", stacktraceOutput.toString());
}
private void throwIfDir() {
if (isDir()) {
throw new SecurityException("Is Directory");
}
}
}

Datei anzeigen

@ -1,6 +1,5 @@
setup:
- "ln -s /home/gitea/lib"
build: build:
- "ln -s /home/gitea/lib"
- "cp ~/gradle.properties ."
- "chmod u+x build.gradle"
- "./gradlew buildProject" - "./gradlew buildProject"
- "./gradlew --stop"

Datei anzeigen

@ -57,18 +57,4 @@ public class ArgumentCommand extends TestSWCommand {
public void argument(String sender, String arg) { public void argument(String sender, String arg) {
throw new ExecutionIdentifier("RunArgument with String"); throw new ExecutionIdentifier("RunArgument with String");
} }
@Register
public void minLengthArgument(String sender, @Length(min = 3) @StaticValue({"he", "hello"}) String arg) {
throw new ExecutionIdentifier("RunLengthArgument with String");
}
@Register
public void minAndMaxLengthArgument(String sender, @Length(min = 3, max = 3) @StaticValue({"wo", "world"}) String arg) {
throw new ExecutionIdentifier("RunLengthArgument with String");
}
public void arrayLengthArgument(String sender, @ArrayLength(max = 3) @StaticValue({"one", "two", "three"}) String... args) {
throw new ExecutionIdentifier("RunArrayLengthArgument with String");
}
} }

Datei anzeigen

@ -44,7 +44,6 @@ public class ArgumentCommandTest {
ArgumentCommand cmd = new ArgumentCommand(); ArgumentCommand cmd = new ArgumentCommand();
try { try {
cmd.execute("test", "", new String[]{"true", "false"}); cmd.execute("test", "", new String[]{"true", "false"});
assert false;
} catch (Exception e) { } catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with Boolean"); assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with Boolean");
} }
@ -55,7 +54,6 @@ public class ArgumentCommandTest {
ArgumentCommand cmd = new ArgumentCommand(); ArgumentCommand cmd = new ArgumentCommand();
try { try {
cmd.execute("test", "", new String[]{"0.0", "0.0", "0.0"}); cmd.execute("test", "", new String[]{"0.0", "0.0", "0.0"});
assert false;
} catch (Exception e) { } catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with Float"); assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with Float");
} }
@ -66,7 +64,6 @@ public class ArgumentCommandTest {
ArgumentCommand cmd = new ArgumentCommand(); ArgumentCommand cmd = new ArgumentCommand();
try { try {
cmd.execute("test", "", new String[]{"0.0", "0.0", "0.0", "0.0"}); cmd.execute("test", "", new String[]{"0.0", "0.0", "0.0", "0.0"});
assert false;
} catch (Exception e) { } catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with Double"); assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with Double");
} }
@ -77,7 +74,6 @@ public class ArgumentCommandTest {
ArgumentCommand cmd = new ArgumentCommand(); ArgumentCommand cmd = new ArgumentCommand();
try { try {
cmd.execute("test", "", new String[]{"0"}); cmd.execute("test", "", new String[]{"0"});
assert false;
} catch (Exception e) { } catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with Integer"); assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with Integer");
} }
@ -88,17 +84,26 @@ public class ArgumentCommandTest {
ArgumentCommand cmd = new ArgumentCommand(); ArgumentCommand cmd = new ArgumentCommand();
try { try {
cmd.execute("test", "", new String[]{"0", "0"}); cmd.execute("test", "", new String[]{"0", "0"});
assert false;
} catch (Exception e) { } catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with Long"); assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with Long");
} }
} }
@Test
public void testString() {
ArgumentCommand cmd = new ArgumentCommand();
try {
cmd.execute("test", "", new String[]{"Hello World"});
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with String");
}
}
@Test @Test
public void testTabComplete() { public void testTabComplete() {
ArgumentCommand cmd = new ArgumentCommand(); ArgumentCommand cmd = new ArgumentCommand();
List<String> strings = cmd.tabComplete("test", "", new String[]{""}); List<String> strings = cmd.tabComplete("test", "", new String[]{""});
assertTabCompletes(strings, "true", "false", "hello", "wor"); assertTabCompletes(strings, "true", "false");
} }
@Test @Test
@ -106,23 +111,5 @@ public class ArgumentCommandTest {
ArgumentCommand cmd = new ArgumentCommand(); ArgumentCommand cmd = new ArgumentCommand();
List<String> strings = cmd.tabComplete("test", "", new String[]{"t"}); List<String> strings = cmd.tabComplete("test", "", new String[]{"t"});
assertTabCompletes(strings, "true", "t"); assertTabCompletes(strings, "true", "t");
strings = cmd.tabComplete("test", "", new String[]{"h"});
assertTabCompletes(strings, "h", "hello");
strings = cmd.tabComplete("test", "", new String[]{"hel"});
assertTabCompletes(strings, "hel", "hello");
strings = cmd.tabComplete("test", "", new String[]{"w"});
assertTabCompletes(strings, "w", "wor");
strings = cmd.tabComplete("test", "", new String[]{"wor"});
assertTabCompletes(strings, "wor");
strings = cmd.tabComplete("test", "", new String[]{"worl"});
assertTabCompletes(strings, "wor", "worl");
strings = cmd.tabComplete("test", "", new String[]{"one", "two", "three", "one"});
assertTabCompletes(strings);
} }
} }

Datei anzeigen

@ -39,18 +39,17 @@ public class BetterExceptionCommand extends TestSWCommand {
public TestTypeMapper<String> tabCompleteException() { public TestTypeMapper<String> tabCompleteException() {
return new TestTypeMapper<String>() { return new TestTypeMapper<String>() {
@Override @Override
public String map(String sender, PreviousArguments previousArguments, String s) { public String map(String sender, String[] previousArguments, String s) {
return null; return null;
} }
@Override @Override
public boolean validate(String sender, String value, MessageSender messageSender) { public boolean validate(String sender, String value, MessageSender messageSender) {
System.out.println("Validate: " + value);
throw new SecurityException(); throw new SecurityException();
} }
@Override @Override
public Collection<String> tabCompletes(String sender, PreviousArguments previousArguments, String s) { public Collection<String> tabCompletes(String sender, String[] previousArguments, String s) {
throw new SecurityException(); throw new SecurityException();
} }
}; };

Datei anzeigen

@ -41,14 +41,15 @@ public class CacheCommand extends TestSWCommand {
@Cached @Cached
@Mapper(value = "int", local = true) @Mapper(value = "int", local = true)
public AbstractTypeMapper<String, Integer> typeMapper() { public AbstractTypeMapper<String, Integer> typeMapper() {
System.out.println("TypeMapper register");
return new TestTypeMapper<Integer>() { return new TestTypeMapper<Integer>() {
@Override @Override
public Integer map(String sender, PreviousArguments previousArguments, String s) { public Integer map(String sender, String[] previousArguments, String s) {
return Integer.parseInt(s); return Integer.parseInt(s);
} }
@Override @Override
public Collection<String> tabCompletes(String sender, PreviousArguments previousArguments, String s) { public Collection<String> tabCompletes(String sender, String[] previousArguments, String s) {
return Arrays.asList(count.getAndIncrement() + ""); return Arrays.asList(count.getAndIncrement() + "");
} }
}; };

Datei anzeigen

@ -36,14 +36,6 @@ public class CacheCommandTest {
assertThat(tabCompletions1, is(equalTo(tabCompletions2))); assertThat(tabCompletions1, is(equalTo(tabCompletions2)));
} }
@Test
public void testCachingWithDifferentMessages() {
CacheCommand cmd = new CacheCommand();
List<String> tabCompletions1 = cmd.tabComplete("test", "", new String[]{""});
List<String> tabCompletions2 = cmd.tabComplete("test", "", new String[]{"0"});
assertThat(tabCompletions1, is(equalTo(tabCompletions2)));
}
@Test @Test
public void testCachingWithDifferentSenders() { public void testCachingWithDifferentSenders() {
CacheCommand cmd = new CacheCommand(); CacheCommand cmd = new CacheCommand();

Datei anzeigen

@ -43,7 +43,7 @@ public class NullMapperCommand extends TestSWCommand {
public TestTypeMapper<String> typeMapper() { public TestTypeMapper<String> typeMapper() {
return new TestTypeMapper<String>() { return new TestTypeMapper<String>() {
@Override @Override
public String map(String sender, PreviousArguments previousArguments, String s) { public String map(String sender, String[] previousArguments, String s) {
if (s.equals("Hello World")) { if (s.equals("Hello World")) {
return null; return null;
} }
@ -51,7 +51,7 @@ public class NullMapperCommand extends TestSWCommand {
} }
@Override @Override
public Collection<String> tabCompletes(String sender, PreviousArguments previousArguments, String s) { public Collection<String> tabCompletes(String sender, String[] previousArguments, String s) {
return null; return null;
} }
}; };

Datei anzeigen

@ -1,35 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.command;
import de.steamwar.command.dto.ExecutionIdentifier;
import de.steamwar.command.dto.TestSWCommand;
public class NumberValidatorCommand extends TestSWCommand {
public NumberValidatorCommand() {
super("numberValidator");
}
@Register
public void test(String sender, @Min(intValue = 0) @Max(intValue = 10) int i) {
throw new ExecutionIdentifier("RunNumberValidator with int");
}
}

Datei anzeigen

@ -1,51 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.command;
import de.steamwar.command.dto.ExecutionIdentifier;
import org.junit.Test;
import static de.steamwar.AssertionUtils.assertCMDFramework;
public class NumberValidatorCommandTest {
@Test
public void testMinValue() {
NumberValidatorCommand command = new NumberValidatorCommand();
command.execute("sender", "", new String[]{"-1"});
}
@Test
public void testMaxValue() {
NumberValidatorCommand command = new NumberValidatorCommand();
command.execute("sender", "", new String[]{"11"});
}
@Test
public void testValidValue() {
try {
NumberValidatorCommand command = new NumberValidatorCommand();
command.execute("sender", "", new String[]{"2"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunNumberValidator with int");
}
}
}

Datei anzeigen

@ -1,70 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.command;
import de.steamwar.command.dto.ExecutionIdentifier;
import de.steamwar.command.dto.TestSWCommand;
import de.steamwar.command.dto.TestTypeMapper;
import java.util.Arrays;
import java.util.Collection;
public class PartOfCommand {
public static class ParentCommand extends TestSWCommand {
public ParentCommand() {
super("parent");
}
@Register
public void execute(String s, String... args) {
throw new ExecutionIdentifier("ParentCommand with String...");
}
@Mapper("test")
public TestTypeMapper<Integer> typeMapper() {
return new TestTypeMapper<Integer>() {
@Override
public Integer map(String sender, PreviousArguments previousArguments, String s) {
return -1;
}
@Override
public Collection<String> tabCompletes(String sender, PreviousArguments previousArguments, String s) {
return Arrays.asList(s);
}
};
}
}
@AbstractSWCommand.PartOf(ParentCommand.class)
public static class SubCommand extends TestSWCommand {
public SubCommand() {
super(null);
}
@Register
public void execute(String s, @Mapper("test") int i) {
throw new ExecutionIdentifier("SubCommand with int " + i);
}
}
}

Datei anzeigen

@ -1,42 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.command;
import de.steamwar.command.dto.ExecutionIdentifier;
import org.junit.Test;
import static de.steamwar.AssertionUtils.assertCMDFramework;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
public class PartOfCommandTest {
@Test
public void testMerging() {
PartOfCommand.ParentCommand command = new PartOfCommand.ParentCommand();
new PartOfCommand.SubCommand();
try {
command.execute("test", "", new String[]{"0"});
assertThat(true, is(false));
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "SubCommand with int -1");
}
}
}

Datei anzeigen

@ -1,54 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.command;
import de.steamwar.command.dto.ExecutionIdentifier;
import de.steamwar.command.dto.TestSWCommand;
import de.steamwar.command.dto.TestTypeMapper;
import java.util.Arrays;
import java.util.Collection;
public class PreviousArgumentCommand extends TestSWCommand {
public PreviousArgumentCommand() {
super("previous");
}
@Register
public void genericCommand(String s, int i, String l) {
throw new ExecutionIdentifier(l);
}
@ClassMapper(value = String.class, local = true)
public TestTypeMapper<String> typeMapper() {
return new TestTypeMapper<String>() {
@Override
public String map(String sender, PreviousArguments previousArguments, String s) {
return "RunTypeMapper_" + previousArguments.getMappedArg(0) + "_" + previousArguments.getMappedArg(0).getClass().getSimpleName();
}
@Override
public Collection<String> tabCompletes(String sender, PreviousArguments previousArguments, String s) {
return Arrays.asList(previousArguments.getMappedArg(0) + "");
}
};
}
}

Datei anzeigen

@ -1,56 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.command;
import de.steamwar.command.dto.ExecutionIdentifier;
import org.junit.Test;
import java.util.List;
import static de.steamwar.AssertionUtils.assertCMDFramework;
import static de.steamwar.AssertionUtils.assertTabCompletes;
public class PreviousArgumentCommandTest {
@Test
public void testPrevArg1() {
PreviousArgumentCommand command = new PreviousArgumentCommand();
List<String> strings = command.tabComplete("", "", new String[]{"1", ""});
assertTabCompletes(strings, "1");
}
@Test
public void testPrevArg2() {
PreviousArgumentCommand command = new PreviousArgumentCommand();
List<String> strings = command.tabComplete("", "", new String[]{"2", ""});
assertTabCompletes(strings, "2");
}
@Test
public void testPrevArgExecute() {
PreviousArgumentCommand command = new PreviousArgumentCommand();
try {
command.execute("", "", new String[]{"2", "2"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunTypeMapper_2_Integer");
}
}
}

Datei anzeigen

@ -19,6 +19,7 @@
package de.steamwar.command; package de.steamwar.command;
import de.steamwar.command.AbstractSWCommand.Register;
import de.steamwar.command.dto.ExecutionIdentifier; import de.steamwar.command.dto.ExecutionIdentifier;
import de.steamwar.command.dto.TestSWCommand; import de.steamwar.command.dto.TestSWCommand;
@ -28,7 +29,7 @@ public class SimpleCommand extends TestSWCommand {
super("simple"); super("simple");
} }
@Register(value = "a", noTabComplete = true) @Register(value = "a", help = true)
public void test(String s, String... varargs) { public void test(String s, String... varargs) {
throw new ExecutionIdentifier("RunSimple with Varargs"); throw new ExecutionIdentifier("RunSimple with Varargs");
} }

Datei anzeigen

@ -41,13 +41,11 @@ public class StaticValueCommandTest {
StaticValueCommand cmd = new StaticValueCommand(); StaticValueCommand cmd = new StaticValueCommand();
try { try {
cmd.execute("", "", new String[] {"hello"}); cmd.execute("", "", new String[] {"hello"});
assert false;
} catch (Exception e) { } catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with hello"); assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with hello");
} }
try { try {
cmd.execute("", "", new String[] {"world"}); cmd.execute("", "", new String[] {"world"});
assert false;
} catch (Exception e) { } catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with world"); assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with world");
} }
@ -58,19 +56,16 @@ public class StaticValueCommandTest {
StaticValueCommand cmd = new StaticValueCommand(); StaticValueCommand cmd = new StaticValueCommand();
try { try {
cmd.execute("", "", new String[] {"-a"}); cmd.execute("", "", new String[] {"-a"});
assert false;
} catch (Exception e) { } catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with false"); assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with false");
} }
try { try {
cmd.execute("", "", new String[] {"-b"}); cmd.execute("", "", new String[] {"-b"});
assert false;
} catch (Exception e) { } catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with true"); assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with true");
} }
try { try {
cmd.execute("", "", new String[] {"-c"}); cmd.execute("", "", new String[] {"-c"});
assert false;
} catch (Exception e) { } catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with true"); assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with true");
} }
@ -81,19 +76,16 @@ public class StaticValueCommandTest {
StaticValueCommand cmd = new StaticValueCommand(); StaticValueCommand cmd = new StaticValueCommand();
try { try {
cmd.execute("", "", new String[] {"-d"}); cmd.execute("", "", new String[] {"-d"});
assert false;
} catch (Exception e) { } catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with true"); assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with true");
} }
try { try {
cmd.execute("", "", new String[] {"-e"}); cmd.execute("", "", new String[] {"-e"});
assert false;
} catch (Exception e) { } catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with false"); assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with false");
} }
try { try {
cmd.execute("", "", new String[] {"-f"}); cmd.execute("", "", new String[] {"-f"});
assert false;
} catch (Exception e) { } catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with true"); assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with true");
} }
@ -104,19 +96,16 @@ public class StaticValueCommandTest {
StaticValueCommand cmd = new StaticValueCommand(); StaticValueCommand cmd = new StaticValueCommand();
try { try {
cmd.execute("", "", new String[] {"-g"}); cmd.execute("", "", new String[] {"-g"});
assert false;
} catch (Exception e) { } catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with int 0"); assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with int 0");
} }
try { try {
cmd.execute("", "", new String[] {"-h"}); cmd.execute("", "", new String[] {"-h"});
assert false;
} catch (Exception e) { } catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with int 1"); assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with int 1");
} }
try { try {
cmd.execute("", "", new String[] {"-i"}); cmd.execute("", "", new String[] {"-i"});
assert false;
} catch (Exception e) { } catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with int 2"); assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with int 2");
} }
@ -127,19 +116,16 @@ public class StaticValueCommandTest {
StaticValueCommand cmd = new StaticValueCommand(); StaticValueCommand cmd = new StaticValueCommand();
try { try {
cmd.execute("", "", new String[] {"-j"}); cmd.execute("", "", new String[] {"-j"});
assert false;
} catch (Exception e) { } catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with long 0"); assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with long 0");
} }
try { try {
cmd.execute("", "", new String[] {"-k"}); cmd.execute("", "", new String[] {"-k"});
assert false;
} catch (Exception e) { } catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with long 1"); assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with long 1");
} }
try { try {
cmd.execute("", "", new String[] {"-l"}); cmd.execute("", "", new String[] {"-l"});
assert false;
} catch (Exception e) { } catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with long 2"); assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with long 2");
} }

Datei anzeigen

@ -1,58 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.command;
import de.steamwar.command.dto.ExecutionIdentifier;
import de.steamwar.command.dto.TestSWCommand;
import de.steamwar.command.dto.TestTypeMapper;
import java.util.Collections;
public class SubCMDSortingCommand extends TestSWCommand {
public SubCMDSortingCommand() {
super("subcmdsorting");
}
@Register
public void test(String s) {
throw new ExecutionIdentifier("Command with 0 parameters");
}
@Register
public void test(String s, String args) {
throw new ExecutionIdentifier("Command with 1 parameter");
}
@Register
public void test(String s, String i1, String i2, String i3, String... args) {
throw new ExecutionIdentifier("Command with 3+n parameters");
}
@Register
public void test(String s, String... args) {
throw new ExecutionIdentifier("Command with n parameters");
}
@ClassMapper(value = String.class, local = true)
public AbstractTypeMapper<String, String> stringMapper() {
return SWCommandUtils.createMapper(s -> s, Collections::singletonList);
}
}

Datei anzeigen

@ -1,83 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.command;
import de.steamwar.command.dto.ExecutionIdentifier;
import org.junit.Test;
import static de.steamwar.AssertionUtils.assertCMDFramework;
public class SubCMDSortingCommandTest {
@Test
public void testNoArgs() {
SubCMDSortingCommand cmd = new SubCMDSortingCommand();
try {
cmd.execute("", "", new String[]{});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "Command with 0 parameters");
}
}
@Test
public void testOneArgs() {
SubCMDSortingCommand cmd = new SubCMDSortingCommand();
try {
cmd.execute("", "", new String[]{"Hello"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "Command with 1 parameter");
}
}
@Test
public void testOneArgsVarArg() {
SubCMDSortingCommand cmd = new SubCMDSortingCommand();
try {
cmd.execute("", "", new String[]{"Hello", "World"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "Command with n parameters");
}
}
@Test
public void testThreeArgsVarArg() {
SubCMDSortingCommand cmd = new SubCMDSortingCommand();
try {
cmd.execute("", "", new String[]{"Hello", "World", "YoyoNow", "Hugo"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "Command with 3+n parameters");
}
}
@Test
public void testThreeArgsVarArg2() {
SubCMDSortingCommand cmd = new SubCMDSortingCommand();
try {
cmd.execute("", "", new String[]{"Hello", "World", "YoyoNow"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "Command with 3+n parameters");
}
}
}

Datei anzeigen

@ -60,26 +60,4 @@ public class ValidatorCommandTest {
assertCMDFramework(e, ExecutionIdentifier.class, "RunOnErrorDouble"); assertCMDFramework(e, ExecutionIdentifier.class, "RunOnErrorDouble");
} }
} }
@Test
public void testInvert() {
ValidatorInvertCommand cmd = new ValidatorInvertCommand();
try {
cmd.execute("test", "", new String[]{"false", "true"});
assertThat(true, is(false));
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunTestInvert");
}
}
@Test
public void testInvertOther() {
ValidatorInvertCommand cmd = new ValidatorInvertCommand();
try {
cmd.execute("test", "", new String[]{"Hello", "0"});
assertThat(true, is(false));
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunTestInvert2");
}
}
} }

Datei anzeigen

@ -1,51 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.command;
import de.steamwar.command.dto.ExecutionIdentifier;
import de.steamwar.command.dto.TestSWCommand;
import de.steamwar.command.dto.TestValidator;
public class ValidatorInvertCommand extends TestSWCommand {
public ValidatorInvertCommand() {
super("testvalidator");
}
@Register
public void test(@Validator(value = "Text", invert = true) String sender, boolean b, boolean b2) {
System.out.println("test: " + sender + " " + b + " " + b2);
throw new ExecutionIdentifier("RunTestInvert");
}
@Register
public void test(String sender, @Validator(value = "Text", invert = true) String h, int i) {
System.out.println("test: " + sender + " " + h + " " + i);
throw new ExecutionIdentifier("RunTestInvert2");
}
@Validator(value = "Text", local = true)
public TestValidator<String> testValidator() {
return (sender, value, messageSender) -> {
System.out.println("testValidator: " + sender + " " + value + " " + messageSender);
return false;
};
}
}

Datei anzeigen

@ -51,11 +51,9 @@ public class TestSWCommand extends AbstractSWCommand<String> {
@Override @Override
protected void commandSystemError(String sender, CommandFrameworkException e) { protected void commandSystemError(String sender, CommandFrameworkException e) {
System.out.println("CommandSystemError: " + e.getMessage());
} }
@Override @Override
protected void commandSystemWarning(Supplier<String> message) { protected void commandSystemWarning(Supplier<String> message) {
System.out.println("CommandSystemWarning: " + message.get());
} }
} }