Skip to content

Commit

Permalink
chore(common): Some more modules
Browse files Browse the repository at this point in the history
  • Loading branch information
phinner committed Mar 27, 2024
1 parent 0794a38 commit 20440f8
Show file tree
Hide file tree
Showing 16 changed files with 392 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,23 @@
package com.xpdustry.distributor.common;

import com.xpdustry.distributor.common.command.CommandFacadeManager;
import com.xpdustry.distributor.common.event.EventManager;
import com.xpdustry.distributor.common.localization.LocalizationSourceManager;
import com.xpdustry.distributor.common.permission.PermissionManager;
import com.xpdustry.distributor.common.scheduler.PluginScheduler;
import com.xpdustry.distributor.common.service.ServiceManager;

public interface Distributor {

ServiceManager getServiceManager();

EventManager getEventManager();

CommandFacadeManager getCommandFacadeFactory();

PermissionManager getPermissionManager();

LocalizationSourceManager getLocalizationSourceManager();

PluginScheduler getPluginScheduler();
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@
package com.xpdustry.distributor.common;

import com.xpdustry.distributor.common.command.CommandFacadeManager;
import com.xpdustry.distributor.common.event.EventManager;
import com.xpdustry.distributor.common.localization.LocalizationSourceManager;
import com.xpdustry.distributor.common.permission.PermissionManager;
import com.xpdustry.distributor.common.plugin.AbstractMindustryPlugin;
import com.xpdustry.distributor.common.scheduler.PluginScheduler;
import com.xpdustry.distributor.common.scheduler.PluginTimeSource;
import com.xpdustry.distributor.common.service.ServiceManager;
import com.xpdustry.distributor.common.util.Priority;
import java.util.Objects;
Expand All @@ -31,17 +34,24 @@ public final class DistributorCommonPlugin extends AbstractMindustryPlugin imple

private final ServiceManager services = ServiceManager.create();
private final LocalizationSourceManager source = LocalizationSourceManager.create();
private @Nullable EventManager events = null;
private @Nullable CommandFacadeManager factory = null;
private @Nullable PluginScheduler scheduler = null;
private @Nullable PermissionManager permissions = null;

@Override
public ServiceManager getServiceManager() {
return this.services;
}

@Override
public EventManager getEventManager() {
return ensureInitialized(this.events, "event");
}

@Override
public CommandFacadeManager getCommandFacadeFactory() {
return ensureInitialized(this.factory, "command-facade-manager");
return ensureInitialized(this.factory, "command-facade");
}

@Override
Expand All @@ -54,16 +64,28 @@ public LocalizationSourceManager getLocalizationSourceManager() {
return this.source;
}

@Override
public PluginScheduler getPluginScheduler() {
return ensureInitialized(this.scheduler, "scheduler");
}

@Override
public void onInit() {
DistributorProvider.set(this);
this.services.register(this, EventManager.class, Priority.LOW, EventManager::create);
this.services.register(this, CommandFacadeManager.class, Priority.LOW, CommandFacadeManager::create);
this.services.register(this, PluginTimeSource.class, Priority.LOW, PluginTimeSource::arc);
}

@Override
public void onLoad() {
this.permissions = services.provide(PermissionManager.class);
this.events = services.provide(EventManager.class);
this.factory = services.provide(CommandFacadeManager.class);
this.scheduler = PluginScheduler.create(
this,
this.services.provide(PluginTimeSource.class),
Runtime.getRuntime().availableProcessors());
}

private <T> T ensureInitialized(final @Nullable T instance, final String name) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Distributor, a feature-rich framework for Mindustry plugins.
*
* Copyright (C) 2024 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 com.xpdustry.distributor.common.annotation;

import com.xpdustry.distributor.common.util.Priority;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Marks a method as an event handler, meaning it will be called by a {@link com.xpdustry.distributor.common.event.EventManager} when its corresponding event is
* posted.
* <br>
* The annotated method must have exactly one parameter, which is the event class.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface EventHandler {

/**
* The priority of the event handler.
*/
Priority priority() default Priority.NORMAL;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
@NullMarked
package com.xpdustry.distributor.common.annotation;

import org.jspecify.annotations.NullMarked;
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* Distributor, a feature-rich framework for Mindustry plugins.
*
* Copyright (C) 2024 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 com.xpdustry.distributor.common.event;

import com.xpdustry.distributor.common.plugin.MindustryPlugin;
import com.xpdustry.distributor.common.util.Priority;
import java.util.function.Consumer;

/**
* The event bus of this server. A better alternative to {@link arc.Events}.
* <br>
* Event subscribers registered with this class will not crash the server if an exception is thrown but be logged instead.
* Subscribers also come with {@link EventSubscription} objects to dynamically unsubscribe them. And also
* {@link Priority} to make sure your subscribers are called in a specific order.
* <pre> {@code
* final EventBus bus = DistributorProvider.get().getEventBus();
* final MindustryPlugin plugin = ...;
* final EventSubscription subscription = bus.subscribe(EventType.PlayerJoin.class, plugin, event -> {
* event.player.sendMessage("Hello " + event.player.name() + "!");
* });
* // When no longer needed, you can unsubscribe the listener
* subscription.unsubscribe();
* } </pre>
* <br>
*/
public interface EventManager {

static EventManager create() {
return new EventManagerImpl();
}

/**
* Subscribe to an event.
*
* @param event the event class to subscribe to
* @param priority the priority of the listener
* @param plugin the plugin that owns the listener
* @param listener the listener to subscribe
* @param <E> the type of the event
* @return the subscription of the subscribed listener
*/
<E> EventSubscription subscribe(
final Class<E> event, final Priority priority, final MindustryPlugin plugin, final Consumer<E> listener);

/**
* Subscribe to an event.
*
* @param event the event class to subscribe to
* @param plugin the plugin that owns the listener
* @param listener the listener to subscribe
* @param <E> the type of the event
* @return the subscription of the subscribed listener
*/
default <E> EventSubscription subscribe(
final Class<E> event, final MindustryPlugin plugin, final Consumer<E> listener) {
return this.subscribe(event, Priority.NORMAL, plugin, listener);
}

/**
* Subscribe to an event.
*
* @param event the event enum to subscribe to
* @param priority the priority of the listener
* @param plugin the plugin that owns the listener
* @param listener the listener to subscribe
* @param <E> the type of the enum event
* @return the subscription of the subscribed listener
*/
<E extends Enum<E>> EventSubscription subscribe(
final E event, final Priority priority, final MindustryPlugin plugin, final Runnable listener);

/**
* Subscribe to an event.
*
* @param event the event enum to subscribe to
* @param plugin the plugin that owns the listener
* @param listener the listener to subscribe
* @param <E> the type of the enum event
* @return the subscription of the subscribed listener
*/
default <E extends Enum<E>> EventSubscription subscribe(
final E event, final MindustryPlugin plugin, final Runnable listener) {
return this.subscribe(event, Priority.NORMAL, plugin, listener);
}

/**
* Posts the event to the arc event bus.
*
* @param event the event to post
* @param <E> the type of the event
*/
<E> void post(final E event);

/**
* Posts the event to the arc event bus to the listeners of the given super class.
*
* @param clazz the class of the event
* @param event the event to post
* @param <E> the type of the event
*/
<E> void post(final Class<? super E> clazz, final E event);

/**
* Posts the enum event to the arc event bus.
*
* @param event the enum event to post
* @param <E> the type of the enum event
*/
<E extends Enum<E>> void post(final E event);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* Distributor, a feature-rich framework for Mindustry plugins.
*
* Copyright (C) 2024 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 com.xpdustry.distributor.common.event;

import arc.Events;
import arc.func.Cons;
import arc.struct.ObjectMap;
import arc.struct.Seq;
import com.xpdustry.distributor.common.plugin.MindustryPlugin;
import com.xpdustry.distributor.common.plugin.PluginAware;
import com.xpdustry.distributor.common.util.Priority;
import java.util.Comparator;
import java.util.function.Consumer;

final class EventManagerImpl implements EventManager {

private static final Comparator<Cons<?>> COMPARATOR = (a, b) -> {
final var priorityA = a instanceof ConsumerCons<?> m ? m.priority : Priority.NORMAL;
final var priorityB = b instanceof ConsumerCons<?> m ? m.priority : Priority.NORMAL;
return priorityA.compareTo(priorityB);
};

final ObjectMap<Object, Seq<Cons<?>>> events;

@SuppressWarnings("unchecked")
EventManagerImpl() {
try {
final var field = Events.class.getDeclaredField("events");
field.setAccessible(true);
this.events = (ObjectMap<Object, Seq<Cons<?>>>) field.get(null);
} catch (final ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}

@Override
public <E> EventSubscription subscribe(
final Class<E> event, final Priority priority, final MindustryPlugin plugin, final Consumer<E> listener) {
return this.subscribe(event, new ConsumerCons<>(listener, priority, plugin));
}

@Override
public <E extends Enum<E>> EventSubscription subscribe(
final E event, final Priority priority, final MindustryPlugin plugin, final Runnable listener) {
return this.subscribe(event, new ConsumerCons<>(e -> listener.run(), priority, plugin));
}

@SuppressWarnings("ConstantValue") // <- intellij thinks ObjectMap.get never return null, cringe...
private <E> EventSubscription subscribe(final Object event, final ConsumerCons<E> subscriber) {
this.events.get(event, () -> new Seq<>(Cons.class)).add(subscriber).sort(COMPARATOR);
return () -> {
final var subscribers = this.events.get(event);
if (subscribers != null) {
subscribers.remove(subscriber);
if (subscribers.isEmpty()) {
this.events.remove(event);
}
}
};
}

@Override
public <E> void post(final E event) {
Events.fire(event.getClass(), event);
}

@Override
public <E> void post(final Class<? super E> clazz, final E event) {
Events.fire(clazz, event);
}

@Override
public <E extends Enum<E>> void post(final E event) {
Events.fire(event);
}

@SuppressWarnings("ClassCanBeRecord")
private static final class ConsumerCons<T> implements Cons<T>, PluginAware {

private final Consumer<T> consumer;
private final Priority priority;
private final MindustryPlugin plugin;

private ConsumerCons(final Consumer<T> consumer, final Priority priority, final MindustryPlugin plugin) {
this.consumer = consumer;
this.priority = priority;
this.plugin = plugin;
}

@Override
public void get(final T event) {
try {
this.consumer.accept(event);
} catch (final Throwable e) {
this.plugin
.getLogger()
.atError()
.setMessage("An error occurred while handling a {} event.")
.addArgument(event.getClass().getSimpleName())
.setCause(e)
.log();
}
}

@Override
public MindustryPlugin getPlugin() {
return this.plugin;
}
}
}
Loading

0 comments on commit 20440f8

Please sign in to comment.