Skip to content

Commit

Permalink
feat: Move plugin annotation parsing to dedicated class
Browse files Browse the repository at this point in the history
  • Loading branch information
phinner committed Dec 16, 2023
1 parent 78cbd5f commit 55e14e5
Show file tree
Hide file tree
Showing 8 changed files with 187 additions and 10 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ plugins {
id("distributor.parent-conventions")
}

version = "3.2.2" + if (indraGit.headTag() == null) "-SNAPSHOT" else ""
version = "3.3.0" + if (indraGit.headTag() == null) "-SNAPSHOT" else ""
group = "fr.xpdustry"
description = "The Mindustry plugin of ur dreams..."
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ default <E extends Enum<E>> EventSubscription subscribe(
* @param plugin the plugin that owns the listener
* @param listener the listener to parse
* @return the subscription of the subscribed handlers
* @deprecated replace with {@link fr.xpdustry.distributor.api.plugin.PluginAnnotationParser}
*/
@Deprecated(forRemoval = true)
EventSubscription parse(final MindustryPlugin plugin, final Object listener);
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import arc.files.Fi;
import arc.struct.Seq;
import arc.util.CommandHandler;
import fr.xpdustry.distributor.api.DistributorProvider;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
Expand Down Expand Up @@ -69,6 +68,7 @@ public void dispose() {
private final PluginDescriptor descriptor = PluginDescriptor.from(this);
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final List<PluginListener> listeners = new ArrayList<>();
private final PluginAnnotationParser annotationParser = PluginAnnotationParser.simple(this);
private boolean canParseListeners = false;

@Override
Expand All @@ -81,6 +81,10 @@ public final Logger getLogger() {
return this.logger;
}

protected final PluginAnnotationParser getAnnotationParser() {
return this.annotationParser;
}

/**
* Returns an unmodifiable list of the listeners registered to this plugin.
*/
Expand All @@ -95,7 +99,7 @@ public void addListener(final PluginListener listener) {
}
this.listeners.add(listener);
if (this.canParseListeners) {
this.parseObject(listener);
this.annotationParser.parse(listener);
}
}

Expand Down Expand Up @@ -145,11 +149,6 @@ private void forEachListener(final Consumer<PluginListener> consumer) {
}
}

private void parseObject(final Object object) {
DistributorProvider.get().getEventBus().parse(AbstractMindustryPlugin.this, object);
DistributorProvider.get().getPluginScheduler().parse(AbstractMindustryPlugin.this, object);
}

private final class PluginApplicationListener implements ApplicationListener {

@Override
Expand All @@ -158,8 +157,8 @@ public void init() {
AbstractMindustryPlugin.this.forEachListener(PluginListener::onPluginLoad);
AbstractMindustryPlugin.this.canParseListeners = true;

AbstractMindustryPlugin.this.parseObject(AbstractMindustryPlugin.this);
AbstractMindustryPlugin.this.forEachListener(AbstractMindustryPlugin.this::parseObject);
AbstractMindustryPlugin.this.annotationParser.parse(AbstractMindustryPlugin.this);
AbstractMindustryPlugin.this.forEachListener(AbstractMindustryPlugin.this.annotationParser::parse);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Distributor, a feature-rich framework for Mindustry plugins.
*
* Copyright (C) 2023 Xpdustry
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package fr.xpdustry.distributor.api.plugin;

public interface PluginAnnotationParser {

static PluginAnnotationParser simple(final MindustryPlugin plugin) {
return new SimplePluginAnnotationParser(plugin);
}

void parse(final Object object);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* Distributor, a feature-rich framework for Mindustry plugins.
*
* Copyright (C) 2023 Xpdustry
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package fr.xpdustry.distributor.api.plugin;

import fr.xpdustry.distributor.api.DistributorProvider;
import fr.xpdustry.distributor.api.event.EventHandler;
import fr.xpdustry.distributor.api.scheduler.Cancellable;
import fr.xpdustry.distributor.api.scheduler.TaskHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.Consumer;

final class SimplePluginAnnotationParser implements PluginAnnotationParser {

private final MindustryPlugin plugin;

SimplePluginAnnotationParser(final MindustryPlugin plugin) {
this.plugin = plugin;
}

@Override
public void parse(final Object object) {
for (final var method : object.getClass().getDeclaredMethods()) {
parseEvents(object, method);
parseTasks(object, method);
}
}

private void parseEvents(final Object object, final Method method) {
final var annotation = method.getAnnotation(EventHandler.class);
if (annotation == null) {
return;
}
if (method.getParameterCount() != 1) {
throw new IllegalArgumentException("The event handler on " + method + " hasn't the right parameter count.");
} else if (!method.canAccess(object) || !method.trySetAccessible()) {
throw new RuntimeException("Unable to make " + method + " accessible.");
}

final var handler = new MethodEventHandler<>(object, method, plugin);
DistributorProvider.get()
.getEventBus()
.subscribe(handler.getEventType(), annotation.priority(), plugin, handler);
}

private void parseTasks(final Object object, final Method method) {
final var annotation = method.getAnnotation(TaskHandler.class);
if (annotation == null) {
return;
}
if (method.getParameterCount() > 1) {
throw new IllegalArgumentException("The event handler on " + method + " hasn't the right parameter count.");
} else if (!method.canAccess(object) || !method.trySetAccessible()) {
throw new RuntimeException("Unable to make " + method + " accessible.");
} else if (method.getParameterCount() == 1 && !Cancellable.class.equals(method.getParameterTypes()[0])) {
throw new IllegalArgumentException("The event handler on " + method + " hasn't the right parameter type.");
}

final var scheduler = DistributorProvider.get().getPluginScheduler();
final var builder = annotation.async() ? scheduler.scheduleAsync(plugin) : scheduler.scheduleSync(plugin);
if (annotation.interval() > -1) {
builder.repeat(annotation.interval(), annotation.unit());
}
if (annotation.delay() > -1) {
builder.delay(annotation.delay(), annotation.unit());
}
builder.execute(new MethodPluginTask(object, method));
}

private static final class MethodEventHandler<E> implements Consumer<E> {

private final Object target;
private final Method method;
private final MindustryPlugin plugin;

private MethodEventHandler(final Object target, final Method method, final MindustryPlugin plugin) {
this.target = target;
this.method = method;
this.plugin = plugin;
}

@Override
public void accept(final E event) {
try {
this.method.invoke(this.target, event);
} catch (final InvocationTargetException e) {
this.plugin
.getLogger()
.atError()
.setMessage("An error occurred while handling a {} event.")
.addArgument(event.getClass().getSimpleName())
.setCause(e.getTargetException())
.log();
} catch (final ReflectiveOperationException e) {
throw new RuntimeException("Failed to call " + this.method + " on " + this.target, e);
}
}

@SuppressWarnings("unchecked")
public Class<E> getEventType() {
return (Class<E>) this.method.getParameterTypes()[0];
}
}

private static final class MethodPluginTask implements Consumer<Cancellable> {

private final Object object;
private final Method method;

private MethodPluginTask(final Object object, final Method method) {
this.object = object;
this.method = method;
}

@Override
public void accept(final Cancellable cancellable) {
try {
if (this.method.getParameterCount() == 1) {
this.method.invoke(this.object, cancellable);
} else {
this.method.invoke(this.object);
}
} catch (final IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException("Unable to invoke " + this.method, e);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ public interface PluginScheduler {
* @param plugin the plugin that owns the listener
* @param object the object to parse
* @return a list of scheduled tasks
* @deprecated replace with {@link fr.xpdustry.distributor.api.plugin.PluginAnnotationParser}
*/
@Deprecated(forRemoval = true)
List<PluginTask<?>> parse(final MindustryPlugin plugin, final Object object);
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ public <E extends Enum<E>> void post(final E event) {
Events.fire(event);
}

@SuppressWarnings("removal")
@Override
public EventSubscription parse(final MindustryPlugin plugin, final Object listener) {
final List<EventSubscription> subscriptions = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public <V> PluginTaskRecipe<V> recipe(final MindustryPlugin plugin, final V valu
return new RecipePluginTask.Builder<>(this, plugin, value, new ArrayList<>());
}

@SuppressWarnings("removal")
@Override
public List<PluginTask<?>> parse(final MindustryPlugin plugin, final Object object) {
final List<PluginTask<?>> tasks = new ArrayList<>();
Expand Down

0 comments on commit 55e14e5

Please sign in to comment.