Merge pull request 'Linkage' (#20) from Linkage into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful

Reviewed-on: #20
Dieser Commit ist enthalten in:
Lixfel 2022-09-23 12:45:21 +02:00
Commit a249d66dcf
23 geänderte Dateien mit 1315 neuen und 0 gelöschten Zeilen

Datei anzeigen

@ -83,6 +83,18 @@ dependencies {
testImplementation 'org.hamcrest:hamcrest:2.2' testImplementation 'org.hamcrest:hamcrest:2.2'
} }
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

@ -0,0 +1,36 @@
/*
* 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.
*/
LinkageType.Context[] value();
}

Datei anzeigen

@ -0,0 +1,31 @@
/*
* 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(LinkageType.Context.BUNGEE)
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
public @interface DiscordMode {
}

Datei anzeigen

@ -0,0 +1,44 @@
/*
* 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(LinkageType.Context.BUNGEE)
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
public @interface EventMode {
Mode value();
@AllArgsConstructor
enum Mode {
EventOnly(""),
NonEvent("!");
@Getter
private String prefix;
}
}

Datei anzeigen

@ -0,0 +1,352 @@
/*
* 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.plan.ParameterBuilder;
import lombok.Getter;
import lombok.SneakyThrows;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import java.io.*;
import java.io.Writer;
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;
public class LinkageProcessor extends AbstractProcessor {
@Getter
private static LinkageType.Context context;
@Getter
private static String pluginMain;
private String name;
private String packageName;
private String className;
private Messager messager;
private boolean processed = false;
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public Set<String> getSupportedAnnotationTypes() {
return Stream.of(Linked.class, Linked.Linkages.class)
.map(Class::getCanonicalName)
.collect(Collectors.toSet());
}
@SneakyThrows
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
String name = new File(System.getProperty("user.dir")).getName();
if (name.contains(".")) name = name.substring(0, name.indexOf('.'));
name = name.toLowerCase();
messager = processingEnv.getMessager();
this.name = name;
packageName = "de.steamwar." + name + ".linkage";
className = "LinkageUtils";
mainClass();
}
@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") ? LinkageType.Context.BUNGEE : LinkageType.Context.SPIGOT;
Optional<String> mainName = getMainName(pluginYMLFile.get());
if (!mainName.isPresent()) {
messager.printMessage(Diagnostic.Kind.ERROR, "Could not find main class in plugin.yml or bungee.yml");
return;
}
pluginMain = mainName.get();
}
@SneakyThrows
private Optional<String> getMainName(File pluginYML) {
if (!pluginYML.exists()) return Optional.empty();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(pluginYML)))) {
return reader.lines()
.filter(line -> line.startsWith("main:"))
.map(line -> line.substring(line.indexOf(':') + 1).trim())
.findFirst();
}
}
@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(packageName, className);
buildPlan.addImport("java.util.Set");
buildPlan.addImport("java.lang.Class");
buildPlan.addImport("de.steamwar.linkage.LinkageType");
buildPlan.addImport("java.util.HashSet");
buildPlan.addField(new FieldBuilder("Set<Class<? extends LinkageType>>", "enabled", "new HashSet<>()"));
MethodBuilder runsMethod = new MethodBuilder("run", "void");
runsMethod.addParameter(new ParameterBuilder("Class<? extends LinkageType>...", "types"));
runsMethod.addLine("for (Class<? extends LinkageType> type : types) _run(type);");
buildPlan.addMethod(runsMethod);
MethodBuilder runMethod = new MethodBuilder("_run", "void");
runMethod.setPrivate(true);
buildPlan.addMethod(runMethod);
runMethod.addParameter(new ParameterBuilder("Class<? extends LinkageType>", "type"));
runMethod.addLine("if (!enabled.add(type)) return;");
Set<Class<? extends LinkageType>> alreadyGenerated = new HashSet<>();
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Linked.class);
elements.addAll((Set) roundEnv.getElementsAnnotatedWith(Linked.Linkages.class));
Map<String, TypeElement> neededFields = new HashMap<>();
for (Element element : elements) {
if (element.getKind() != ElementKind.CLASS) {
continue;
}
TypeElement typeElement = (TypeElement) element;
Linked[] linkeds = element.getAnnotationsByType(Linked.class);
if (linkeds.length == 0) {
continue;
}
if (linkeds.length > 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 -> {
return 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;
}
neededFields.put(typeElement.getQualifiedName().toString(), typeElement);
TypeElement fieldType = (TypeElement) ((DeclaredType) variableElement.asType()).asElement();
neededFields.put(fieldType.getQualifiedName().toString(), fieldType);
specialElements(typeElement, buildPlan, buildPlan::addStaticLine, () -> {
buildPlan.addStaticLine(getElement(typeElement, neededFields) + "." + variableElement.getSimpleName().toString() + " = " + getElement((TypeElement) ((DeclaredType) variableElement.asType()).asElement(), neededFields) + ";");
});
}
}
neededFields.forEach((s, typeElement) -> {
buildPlan.addImport(typeElement.getQualifiedName().toString());
buildPlan.addField(new FieldBuilder(typeElement.getSimpleName().toString(), typeElement.getSimpleName().toString()));
MethodBuilder initializer = new MethodBuilder(typeElement.getSimpleName().toString(), typeElement.getSimpleName().toString());
initializer.setPrivate(true);
specialElements(typeElement, buildPlan, initializer::addLine, () -> {
initializer.addLine("if (" + typeElement.getSimpleName().toString() + " == null) {");
initializer.addLine(" " + typeElement.getSimpleName().toString() + " = new " + typeElement.getSimpleName().toString() + "();");
initializer.addLine("}");
});
initializer.addLine("return " + typeElement.getSimpleName().toString() + ";");
buildPlan.addMethod(initializer);
});
Map<Class<?>, MethodBuilder> methods = new HashMap<>();
for (Element element : elements) {
if (element.getKind() != ElementKind.CLASS) {
continue;
}
TypeElement typeElement = (TypeElement) element;
System.out.println("Found element: " + typeElement.getQualifiedName().toString());
element.getAnnotationMirrors().stream()
.filter(annotationMirror -> {
String annotationNames = annotationMirror.getAnnotationType().asElement().getSimpleName().toString();
return annotationNames.equals("Linked") || annotationNames.equals("Linkages");
})
.flatMap(annotationMirror -> annotationMirror.getElementValues().values().stream())
.map(AnnotationValue::getValue)
.flatMap(o -> {
if (o instanceof List) {
return ((List<Object>) o).stream()
.map(AnnotationMirror.class::cast)
.map(AnnotationMirror::getElementValues)
.map(Map::values)
.flatMap(Collection::stream)
.map(AnnotationValue::getValue);
}
return Stream.of(o);
})
.map(Object::toString)
.map(s -> {
try {
return Class.forName(s);
} catch (Exception e) {
messager.printMessage(Diagnostic.Kind.ERROR, "Could not find class " + s, element);
return null;
}
})
.filter(Objects::nonNull)
.map(clazz -> {
try {
return (LinkageType) clazz.getDeclaredConstructor().newInstance();
} catch (Exception e) {
messager.printMessage(Diagnostic.Kind.ERROR, "Could not create instance of " + clazz.getName(), element);
return null;
}
})
.filter(Objects::nonNull)
.forEach(type -> {
Class<? extends LinkageType> clazz = type.getClass();
if (!type.requirements(typeElement.getSuperclass().toString(), typeElement.getInterfaces().stream().map(TypeMirror::toString).collect(Collectors.toSet()), typeElement)) {
return;
}
if (alreadyGenerated.add(clazz)) {
runMethod.addLine("if (type == " + clazz.getTypeName() + ".class) {");
runMethod.addLine(" run" + clazz.getSimpleName() + "();");
runMethod.addLine("}");
}
MethodBuilder method = methods.computeIfAbsent(clazz, t -> {
MethodBuilder methodBuilder = new MethodBuilder("run" + t.getSimpleName(), "void");
methodBuilder.setPrivate(true);
buildPlan.addMethod(methodBuilder);
return methodBuilder;
});
specialElements(typeElement, buildPlan, method::addLine, () -> {
type.generateCode(buildPlan, method, getElement(typeElement, neededFields), typeElement);
});
});
}
BufferedWriter bufferedWriter = new BufferedWriter(writer);
buildPlan.write(bufferedWriter);
bufferedWriter.close();
return true;
}
private String getElement(TypeElement typeElement, Map<String, TypeElement> neededFields) {
if (neededFields.containsKey(typeElement.getQualifiedName().toString())) {
return typeElement.getSimpleName().toString() + "()";
}
return "new " + typeElement.getQualifiedName().toString() + "()";
}
private void specialElements(TypeElement typeElement, BuildPlan buildPlan, Consumer<String> stringConsumer, Runnable inner) {
MinVersion minVersion = typeElement.getAnnotation(MinVersion.class);
MaxVersion maxVersion = typeElement.getAnnotation(MaxVersion.class);
EventMode eventMode = typeElement.getAnnotation(EventMode.class);
DiscordMode discordMode = typeElement.getAnnotation(DiscordMode.class);
PluginCheck[] pluginChecks = typeElement.getAnnotationsByType(PluginCheck.class);
if (context == LinkageType.Context.SPIGOT) {
errorOnNonNull(typeElement, eventMode, discordMode);
List<String> checks = new ArrayList<>();
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);
}
if (!checks.isEmpty()) stringConsumer.accept("if (" + String.join(" && ", checks) + ") {");
inner.run();
if (!checks.isEmpty()) stringConsumer.accept("}");
} else {
errorOnNonNull(typeElement, minVersion, maxVersion);
List<String> checks = new ArrayList<>();
if (eventMode != null) {
buildPlan.addImport("de.steamwar.bungeecore.BungeeCore");
checks.add(eventMode.value().getPrefix() + "BungeeCore.EVENT_MODE");
}
if (discordMode != null) {
buildPlan.addImport("de.steamwar.bungeecore.bot.config.SteamwarDiscordBotConfig");
checks.add("SteamwarDiscordBotConfig.loaded");
}
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);
}
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);
}
}
}
}

Datei anzeigen

@ -0,0 +1,58 @@
/*
* 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;
import java.util.Set;
public interface LinkageType {
Context context = LinkageProcessor.getContext();
default String getPluginMain() {
return LinkageProcessor.getPluginMain();
}
enum Context {
BUNGEE,
SPIGOT
}
default boolean requirements(String superClass, Set<String> interfaces, TypeElement typeElement) {
return true;
}
default void generateCode(BuildPlan buildPlan, MethodBuilder linkageTypeMethod, String instance, TypeElement typeElement) {
switch (context) {
case BUNGEE:
generateCodeBungee(buildPlan, linkageTypeMethod, instance, typeElement);
break;
case SPIGOT:
generateCodeSpigot(buildPlan, linkageTypeMethod, instance, typeElement);
break;
}
}
default void generateCodeBungee(BuildPlan buildPlan, MethodBuilder linkageTypeMethod, String instance, TypeElement typeElement) {
}
default void generateCodeSpigot(BuildPlan buildPlan, MethodBuilder linkageTypeMethod, String instance, TypeElement typeElement) {
}
}

Datei anzeigen

@ -0,0 +1,35 @@
/*
* 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})
@Repeatable(Linked.Linkages.class)
public @interface Linked {
Class<? extends LinkageType> value();
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
@interface Linkages {
@SuppressWarnings("unused") Linked[] value() default {};
}
}

Datei anzeigen

@ -0,0 +1,30 @@
/*
* 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

@ -0,0 +1,32 @@
/*
* 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(LinkageType.Context.SPIGOT)
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
public @interface MaxVersion {
int value();
}

Datei anzeigen

@ -0,0 +1,32 @@
/*
* 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(LinkageType.Context.SPIGOT)
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
public @interface MinVersion {
int value();
}

Datei anzeigen

@ -0,0 +1,42 @@
/*
* 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({LinkageType.Context.BUNGEE, LinkageType.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

@ -0,0 +1,24 @@
/*
* 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

@ -0,0 +1,24 @@
/*
* 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

@ -0,0 +1,89 @@
/*
* 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

@ -0,0 +1,44 @@
/*
* 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

@ -0,0 +1,70 @@
/*
* 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

@ -0,0 +1,35 @@
/*
* 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

@ -0,0 +1,45 @@
/*
* 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;
import java.util.Set;
public class Command implements LinkageType {
@Override
public boolean requirements(String superClass, Set<String> interfaces, TypeElement typeElement) {
return superClass.equals("de.steamwar.command.SWCommand");
}
@Override
public void generateCodeBungee(BuildPlan buildPlan, MethodBuilder linkageTypeMethod, String instance, TypeElement typeElement) {
linkageTypeMethod.addLine(instance + ";");
}
@Override
public void generateCodeSpigot(BuildPlan buildPlan, MethodBuilder linkageTypeMethod, String instance, TypeElement typeElement) {
linkageTypeMethod.addLine(instance + ".setMessage(" + getPluginMain() + ".MESSAGE);");
}
}

Datei anzeigen

@ -0,0 +1,40 @@
/*
* 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;
import java.util.Set;
public class DisableLink implements LinkageType {
@Override
public boolean requirements(String superClass, Set<String> interfaces, TypeElement typeElement) {
return interfaces.contains("de.steamwar.linkage.api.Disable");
}
@Override
public void generateCode(BuildPlan buildPlan, MethodBuilder linkageTypeMethod, String instance, TypeElement typeElement) {
linkageTypeMethod.addLine(instance + ".disable();");
}
}

Datei anzeigen

@ -0,0 +1,40 @@
/*
* 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;
import java.util.Set;
public class EnableLink implements LinkageType {
@Override
public boolean requirements(String superClass, Set<String> interfaces, TypeElement typeElement) {
return interfaces.contains("de.steamwar.linkage.api.Enable");
}
@Override
public void generateCode(BuildPlan buildPlan, MethodBuilder linkageTypeMethod, String instance, TypeElement typeElement) {
linkageTypeMethod.addLine(instance + ".enable();");
}
}

Datei anzeigen

@ -0,0 +1,113 @@
/*
* 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;
import java.util.Set;
public class ListenerLink implements LinkageType {
@Override
public boolean requirements(String superClass, Set<String> interfaces, TypeElement typeElement) {
if (context == Context.BUNGEE) {
return interfaces.contains("net.md_5.bungee.api.plugin.Listener");
} else {
return interfaces.contains("org.bukkit.event.Listener");
}
}
@Override
public void generateCodeBungee(BuildPlan buildPlan, MethodBuilder linkageTypeMethod, String instance, TypeElement typeElement) {
buildPlan.addImport("net.md_5.bungee.api.ProxyServer");
buildPlan.addImport("de.steamwar.bungeecore.BungeeCore");
linkageTypeMethod.addLine("ProxyServer.getInstance().getPluginManager().registerListener(BungeeCore.get(), " + instance + ");");
}
@Override
public void generateCodeSpigot(BuildPlan buildPlan, MethodBuilder linkageTypeMethod, 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(s)) 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);
linkageTypeMethod.addLine("handlerList" + eventType.getSimpleName() + " = " + eventType.getSimpleName() + ".getHandlerList();");
});
buildPlan.addImport(typeElement.getQualifiedName().toString());
String localInstance = "local" + typeElement.getSimpleName().toString();
linkageTypeMethod.addLine(typeElement.getSimpleName() + " " + localInstance + " = " + instance + ";");
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();
}
}
linkageTypeMethod.addLine(type.getSimpleName().toString() + "(" + localInstance + ", " + localInstance + "::" + executableElement.getSimpleName().toString() + ", EventPriority." + priority + ", " + ignoreCancelled + ");");
});
}
}

Datei anzeigen

@ -0,0 +1,34 @@
/*
* 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 Plain implements LinkageType {
@Override
public void generateCode(BuildPlan buildPlan, MethodBuilder linkageTypeMethod, String instance, TypeElement typeElement) {
linkageTypeMethod.addLine(instance + ";");
}
}

Datei anzeigen

@ -0,0 +1,53 @@
/*
* 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;
import java.lang.reflect.Type;
import java.util.Set;
public class UnlinkListener implements LinkageType {
@Override
public boolean requirements(String superClass, Set<String> interfaces, TypeElement typeElement) {
if (context == Context.BUNGEE) {
return interfaces.contains("net.md_5.bungee.api.plugin.Listener");
} else {
return interfaces.contains("org.bukkit.event.Listener");
}
}
@Override
public void generateCodeBungee(BuildPlan buildPlan, MethodBuilder linkageTypeMethod, String instance, TypeElement typeElement) {
buildPlan.addImport("net.md_5.bungee.api.ProxyServer");
buildPlan.addImport("de.steamwar.bungeecore.BungeeCore");
linkageTypeMethod.addLine("ProxyServer.getInstance().getPluginManager().registerListener(" + instance + ");");
}
@Override
public void generateCodeSpigot(BuildPlan buildPlan, MethodBuilder linkageTypeMethod, String instance, TypeElement typeElement) {
buildPlan.addImport("org.bukkit.event.HandlerList");
linkageTypeMethod.addLine("HandlerList.unregisterAll(" + instance + ");");
}
}