Remove compile time linkage in favour of class index

Optimize build time when no change happened for the BauSystem
This commit is contained in:
2024-08-06 11:09:19 +02:00
parent b4ff8726af
commit 412971af4d
52 changed files with 165 additions and 1602 deletions
-37
View File
@@ -1,37 +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/>.
*/
plugins {
steamwar.java
}
tasks.register("buildResources") {
doLast {
val to = File("$projectDir/build/classes/java/main/META-INF/services/javax.annotation.processing.Processor")
to.parentFile.mkdirs()
if (!to.exists()) {
to.createNewFile()
to.writeText("de.steamwar.linkage.LinkageProcessor\n")
}
}
}
tasks.classes {
finalizedBy("buildResources")
}
@@ -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();
}
@@ -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
}
@@ -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;
}
}
@@ -1,356 +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.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 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 File projectDir;
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);
messager = processingEnv.getMessager();
className = "LinkageUtils";
projectDir = new File(processingEnv.getOptions().get("projectDir"));
mainClass(projectDir);
disabledFeatures();
String name = projectDir.getName();
if (name.contains("_")) {
name = name.substring(0, name.indexOf("_"));
}
this.name = name.replaceAll("[^a-zA-Z]", "").toLowerCase();
}
@SneakyThrows
private void mainClass(File projectDir) {
Optional<File> pluginYMLFile = Files.walk(projectDir.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;
}
}
@@ -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);
}
@@ -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 "";
}
@@ -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 {
}
@@ -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();
}
@@ -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();
}
@@ -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 {};
}
}
@@ -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();
}
@@ -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();
}
@@ -1,23 +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 Plain {
}
@@ -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");
}
}
@@ -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");
}
}
@@ -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");
}
}
@@ -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);
}
}
@@ -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();");
}
}
@@ -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();");
}
}
@@ -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 + ");");
}
}
@@ -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 + ");");
});
}
}
@@ -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();");
}
}
@@ -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 + ";");
}
}
@@ -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 + ";");
}
}
@@ -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);");
}
}