Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ apply from: '../gradle/publish.gradle'
apply plugin: 'com.github.johnrengelman.shadow'

java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}

sourceSets {
Expand All @@ -19,11 +19,6 @@ sourceSets {
}
}

java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

dependencies {
api 'com.google.code.gson:gson:2.8.6'
api "com.google.guava:guava:${guavaVersion}"
Expand Down
18 changes: 18 additions & 0 deletions api/src/main/java/com/velocitypowered/api/event/Continuation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.velocitypowered.api.event;

/**
* Represents a continuation of a paused event handler. Any of the resume methods
* may only be called once otherwise an {@link IllegalStateException} is expected.
*/
public interface Continuation {

/**
* Resumes the continuation.
*/
void resume();

/**
* Resumes the continuation after the executed task failed.
*/
void resumeWithException(Throwable exception);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
@FunctionalInterface
public interface EventHandler<E> {

void execute(E event);
EventTask execute(E event);
}
181 changes: 181 additions & 0 deletions api/src/main/java/com/velocitypowered/api/event/EventTask.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package com.velocitypowered.api.event;

import static java.util.Objects.requireNonNull;

import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;

/**
* Represents a task that can be returned by a {@link EventHandler} which allows event handling to
* be suspended and resumed at a later time, and executing event handlers completely or partially
* asynchronously.
*
* <p>By default will all event handlers be executed on the thread the event was posted, using
* event tasks this behavior can be altered.</p>
*/
public abstract class EventTask {

EventTask() {
}

/**
* Whether this {@link EventTask} is required to be called asynchronously.
*
* <p>If this method returns {@code true}, the event task is guaranteed to be executed
* asynchronously from the current thread. Otherwise, the event task may be executed on the
* current thread or asynchronously.</p>
*
* @return Requires async
*/
public abstract boolean requiresAsync();

/**
* Represents a basic {@link EventTask}. The execution of the event handlers will resume after
* this basic task is executed using {@link #run()}.
*/
public abstract static class Basic extends EventTask {

/**
* Runs the task.
*/
public abstract void run();
}

/**
* Represents an {@link EventTask} which receives a {@link Continuation} through
* {@link #run(Continuation)}. The continuation must be notified when the task is
* completed, either with {@link Continuation#resume()} if the task was successful or
* {@link Continuation#resumeWithException(Throwable)} if an exception occurred.
*
* <p>The {@link Continuation} may only be resumed once, or an
* {@link IllegalStateException} is expected.</p>
*
* <p>The {@link Continuation} doesn't need to be notified during the execution of
* {@link #run(Continuation)}, this can happen at a later point in time and from another
* thread.</p>
*/
public abstract static class WithContinuation extends EventTask {

/**
* Runs this async task with the given continuation.
*
* @param continuation The continuation
*/
public abstract void run(Continuation continuation);
}

/**
* Creates a basic {@link EventTask} from the given {@link Runnable}. The task isn't guaranteed
* to be executed asynchronously ({@link #requiresAsync()} always returns {@code false}).
*
* @param task The task
* @return The event task
*/
public static EventTask.Basic of(final Runnable task) {
requireNonNull(task, "task");
return new Basic() {

@Override
public void run() {
task.run();
}

@Override
public boolean requiresAsync() {
return false;
}
};
}

/**
* Creates a basic async {@link EventTask} from the given {@link Runnable}. The task is guaranteed
* to be executed asynchronously ({@link #requiresAsync()} always returns {@code true}).
*
* @param task The task
* @return The async event task
*/
public static EventTask.Basic async(final Runnable task) {
requireNonNull(task, "task");
return new Basic() {

@Override
public void run() {
task.run();
}

@Override
public boolean requiresAsync() {
return true;
}
};
}

/**
* Creates an continuation based {@link EventTask} from the given {@link Consumer}. The task isn't
* guaranteed to be executed asynchronously ({@link #requiresAsync()} always returns
* {@code false}).
*
* @param task The task to execute
* @return The event task
*/
public static EventTask.WithContinuation withContinuation(
final Consumer<Continuation> task) {
requireNonNull(task, "task");
return new WithContinuation() {

@Override
public void run(final Continuation continuation) {
task.accept(continuation);
}

@Override
public boolean requiresAsync() {
return false;
}
};
}

/**
* Creates an async continuation based {@link EventTask} from the given {@link Consumer}. The task
* is guaranteed to be executed asynchronously ({@link #requiresAsync()} always returns
* {@code false}).
*
* @param task The task to execute
* @return The event task
*/
public static EventTask.WithContinuation asyncWithContinuation(
final Consumer<Continuation> task) {
requireNonNull(task, "task");
return new WithContinuation() {

@Override
public void run(final Continuation continuation) {
task.accept(continuation);
}

@Override
public boolean requiresAsync() {
return true;
}
};
}

/**
* Creates an continuation based {@link EventTask} for the given {@link CompletableFuture}. The
* continuation will be notified once the given future is completed.
*
* @param future The task to wait for
* @return The event task
*/
public static EventTask.WithContinuation resumeWhenComplete(
final CompletableFuture<?> future) {
requireNonNull(future, "future");
return withContinuation(continuation -> future.whenComplete((result, cause) -> {
if (cause != null) {
continuation.resumeWithException(cause);
} else {
continuation.resume();
}
}));
}
}
15 changes: 14 additions & 1 deletion api/src/main/java/com/velocitypowered/api/event/Subscribe.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,23 @@
public @interface Subscribe {

/**
* The order events will be posted to this listener.
* The order events will be posted to this handler.
*
* @return the order
*/
short order() default PostOrder.NORMAL;

/**
* Whether the handler is required to be called asynchronously.
*
* <p>If this method returns {@code true}, the method is guaranteed to be executed
* asynchronously from the current thread. Otherwise, the handler may be executed on the
* current thread or asynchronously.</p>
*
* <p>If any method handler targeting an event type is marked with {@code true}, then every
* handler targeting that event type will be executed asynchronously.</p>
*
* @return Requires async
*/
boolean async() default false;
}
13 changes: 10 additions & 3 deletions proxy/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ apply from: '../gradle/checkstyle.gradle'
apply plugin: 'com.github.johnrengelman.shadow'

java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}

test {
Expand Down Expand Up @@ -69,7 +69,6 @@ dependencies {
runtimeOnly 'com.lmax:disruptor:3.4.2' // Async loggers

implementation 'it.unimi.dsi:fastutil:8.4.1'
implementation 'net.kyori:event-method-asm:4.0.0-SNAPSHOT'
implementation 'net.kyori:adventure-nbt:4.0.0-SNAPSHOT'

implementation 'org.asynchttpclient:async-http-client:2.12.1'
Expand All @@ -78,6 +77,10 @@ dependencies {

implementation 'com.electronwill.night-config:toml:3.6.3'

implementation 'org.lanternpowered:lmbda:2.0.0-SNAPSHOT'

implementation 'com.github.ben-manes.caffeine:caffeine:2.8.8'

compileOnly 'com.github.spotbugs:spotbugs-annotations:4.1.2'

testImplementation "org.junit.jupiter:junit-jupiter-api:${junitVersion}"
Expand Down Expand Up @@ -129,3 +132,7 @@ shadowJar {
artifacts {
archives shadowJar
}

test {
useJUnitPlatform()
}
12 changes: 10 additions & 2 deletions proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.velocitypowered.api.event.EventManager;
import com.velocitypowered.api.event.lifecycle.ProxyInitializeEvent;
import com.velocitypowered.api.event.lifecycle.ProxyReloadEvent;
import com.velocitypowered.api.event.lifecycle.ProxyShutdownEvent;
import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.plugin.PluginContainer;
import com.velocitypowered.api.plugin.PluginManager;
Expand All @@ -26,11 +27,11 @@
import com.velocitypowered.proxy.config.VelocityConfiguration;
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
import com.velocitypowered.proxy.console.VelocityConsole;
import com.velocitypowered.proxy.event.VelocityEventManager;
import com.velocitypowered.proxy.network.ConnectionManager;
import com.velocitypowered.proxy.network.ProtocolUtils;
import com.velocitypowered.proxy.network.serialization.FaviconSerializer;
import com.velocitypowered.proxy.network.serialization.GameProfileSerializer;
import com.velocitypowered.proxy.plugin.VelocityEventManager;
import com.velocitypowered.proxy.plugin.VelocityPluginManager;
import com.velocitypowered.proxy.scheduler.VelocityScheduler;
import com.velocitypowered.proxy.server.ServerMap;
Expand Down Expand Up @@ -422,7 +423,14 @@ public void shutdown(boolean explicitExit, Component reason) {
logger.error("Exception while tearing down player connections", e);
}

eventManager.fireShutdownEvent();
try {
eventManager.fire(new ProxyShutdownEvent()).get(10, TimeUnit.SECONDS);
} catch (TimeoutException e) {
timedOut = true;
} catch (ExecutionException e) {
timedOut = true;
logger.error("Exception while firing the shutdown event", e);
}

timedOut = !eventManager.shutdown() || timedOut;
timedOut = !scheduler.shutdown() || timedOut;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import com.velocitypowered.api.command.SimpleCommand;
import com.velocitypowered.api.event.command.CommandExecuteEvent;
import com.velocitypowered.api.event.command.CommandExecuteEvent.CommandResult;
import com.velocitypowered.proxy.plugin.VelocityEventManager;
import com.velocitypowered.proxy.event.VelocityEventManager;
import com.velocitypowered.proxy.util.BrigadierUtils;
import java.util.Iterator;
import java.util.List;
Expand Down Expand Up @@ -147,7 +147,7 @@ public CompletableFuture<Boolean> execute(final CommandSource source, final Stri
return false;
}
return executeImmediately0(source, commandResult.getCommand().orElse(event.getCommand()));
}, eventManager.getService());
}, eventManager.getAsyncExecutor());
}

@Override
Expand All @@ -157,7 +157,7 @@ public CompletableFuture<Boolean> executeImmediately(
Preconditions.checkNotNull(cmdLine, "cmdLine");

return CompletableFuture.supplyAsync(
() -> executeImmediately0(source, cmdLine), eventManager.getService());
() -> executeImmediately0(source, cmdLine), eventManager.getAsyncExecutor());
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.velocitypowered.proxy.event;

import com.velocitypowered.api.event.EventTask;

public interface UntargetedEventHandler {

EventTask execute(Object targetInstance, Object event);

interface Void extends UntargetedEventHandler {

@Override
default EventTask execute(final Object targetInstance, final Object event) {
executeVoid(targetInstance, event);
return null;
}

void executeVoid(Object targetInstance, Object event);
}
}
Loading