diff --git a/src/main/java/io/github/simplex/api/ExecutableService.java b/src/main/java/io/github/simplex/api/ExecutableService.java index 16d9a2a..5f39376 100644 --- a/src/main/java/io/github/simplex/api/ExecutableService.java +++ b/src/main/java/io/github/simplex/api/ExecutableService.java @@ -1,32 +1,70 @@ package io.github.simplex.api; +import io.github.simplex.simplexss.ServicePool; +import org.bukkit.NamespacedKey; import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; +import java.util.concurrent.Delayed; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public abstract class ExecutableService implements IService { - private final int serviceID; + private final NamespacedKey service_name; private final Plugin plugin; private final long delay; private final long period; private final boolean repeating; - public ExecutableService(Plugin plugin, int serviceID, long delay, long period, boolean repeating) { + private boolean cancelled = false; + + /** + * Creates a new instance of an executable service. + * The timings are measured in ticks (20 ticks per second). + * You do not need to explicitly define a delay or a period, + * however if you have flagged {@link #repeating} as true, and the period is null, + * then the period will automatically be set to 20 minutes. + * Each service is registered with a {@link NamespacedKey}, + * to allow for easy identification within the associated {@link ServicePool}. + * Each service also has a plugin parameter to allow for easy dependency injection. + * + * @param plugin Your plugin + * @param service_name A namespaced key which can be used to identify the service. + * @param delay A specified amount of time (in ticks) to wait before the service runs. + * @param period How long the service should wait between service executions (in ticks). + * @param repeating If the service should be scheduled for repeated executions or not. + */ + public ExecutableService(@NotNull Plugin plugin, @NotNull NamespacedKey service_name, @Nullable Long delay, @Nullable Long period, @NotNull Boolean repeating) { this.plugin = plugin; - this.serviceID = serviceID; + this.service_name = service_name; this.repeating = repeating; - this.delay = delay; - this.period = period; + this.delay = Objects.requireNonNullElse(delay, 0L); + this.period = Objects.requireNonNullElse(period, (20L * 60L) * 20L); } + /** + * @return The NamespacedKey of this service. + */ @Override - public int getServiceID() { - return serviceID; + public NamespacedKey getNamespacedKey() { + return service_name; } + /** + * @return The plugin which was defined in the constructor. + * This should be an instance of your main plugin class. + */ @Override public Plugin getProvidingPlugin() { return plugin; } + /** + * @return + */ @Override public long getDelay() { return delay; @@ -38,7 +76,22 @@ public abstract class ExecutableService implements IService { } @Override - public boolean isRepeating() { + public boolean isPeriodic() { return repeating; } + + @Override + public void setCancelled(boolean mayInterruptIfRunning) { + if (!mayInterruptIfRunning) { + cancelled = false; + } + + stop(); + cancelled = true; + } + + @Override + public boolean isCancelled() { + return this.cancelled; + } } diff --git a/src/main/java/io/github/simplex/api/IService.java b/src/main/java/io/github/simplex/api/IService.java index e79392d..960c03b 100644 --- a/src/main/java/io/github/simplex/api/IService.java +++ b/src/main/java/io/github/simplex/api/IService.java @@ -1,14 +1,21 @@ package io.github.simplex.api; +import org.bukkit.NamespacedKey; +import org.bukkit.event.Cancellable; import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; import reactor.core.publisher.Mono; -import java.util.concurrent.RunnableScheduledFuture; +public interface IService extends Runnable, Cancellable { + @Contract(" -> new") + static @NotNull NamespacedKey getDefaultNamespacedKey() { + return new NamespacedKey("simplex_ss", "default_service_name"); + } -public interface IService extends RunnableScheduledFuture { - int getServiceID(); + NamespacedKey getNamespacedKey(); - boolean isRepeating(); + boolean isPeriodic(); long getPeriod(); diff --git a/src/main/java/io/github/simplex/impl/Main.java b/src/main/java/io/github/simplex/impl/Main.java new file mode 100644 index 0000000..8be20ad --- /dev/null +++ b/src/main/java/io/github/simplex/impl/Main.java @@ -0,0 +1,20 @@ +package io.github.simplex.impl; + +import io.github.simplex.simplexss.SchedulingSystem; +import io.github.simplex.simplexss.ServiceManager; +import org.bukkit.plugin.java.JavaPlugin; + +public class Main extends JavaPlugin { + private SchedulingSystem scheduler; + private ServiceManager serviceManager; + + @Override + public void onEnable() { + this.serviceManager = new ServiceManager(this); + this.scheduler = new SchedulingSystem(serviceManager, this); + } + + @Override + public void onDisable() { + } +} diff --git a/src/main/java/io/github/simplex/impl/ServiceImpl.java b/src/main/java/io/github/simplex/impl/ServiceImpl.java new file mode 100644 index 0000000..e585141 --- /dev/null +++ b/src/main/java/io/github/simplex/impl/ServiceImpl.java @@ -0,0 +1,30 @@ +package io.github.simplex.impl; + +import io.github.simplex.api.ExecutableService; +import io.github.simplex.api.IService; +import org.bukkit.NamespacedKey; +import org.jetbrains.annotations.NotNull; +import reactor.core.publisher.Mono; + +import java.util.concurrent.TimeUnit; + +public class ServiceImpl extends ExecutableService { + public ServiceImpl(Main plugin) { + super(plugin, IService.getDefaultNamespacedKey(), 20L, 20L * 60L * 10L, true); + } + + @Override + public Mono start() { + return null; + } + + @Override + public Mono stop() { + return null; + } + + @Override + public void run() { + super.run(); + } +} diff --git a/src/main/java/io/github/simplex/simplexss/SchedulingSystem.java b/src/main/java/io/github/simplex/simplexss/SchedulingSystem.java index e07ef1b..16e6f39 100644 --- a/src/main/java/io/github/simplex/simplexss/SchedulingSystem.java +++ b/src/main/java/io/github/simplex/simplexss/SchedulingSystem.java @@ -3,12 +3,15 @@ package io.github.simplex.simplexss; import io.github.simplex.api.ISchedule; import io.github.simplex.api.IService; import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import reactor.core.publisher.Mono; +import java.util.Arrays; import java.util.HashSet; import java.util.Objects; import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; public final class SchedulingSystem implements ISchedule { private final ServiceManager serviceManager; @@ -41,7 +44,9 @@ public final class SchedulingSystem implements ISchedule { public Mono queue(@NotNull IService service) { return getServiceManager().flatMap(serviceManager -> { Mono pool = serviceManager.getAssociatedServicePool(service); - return pool.defaultIfEmpty(Objects.requireNonNull(serviceManager.createServicePool(service).block())); + return pool.defaultIfEmpty(Objects.requireNonNull(serviceManager + .createServicePool(ServicePool.getDefaultNamespacedKey(), service) + .block())); }); } diff --git a/src/main/java/io/github/simplex/simplexss/ServiceManager.java b/src/main/java/io/github/simplex/simplexss/ServiceManager.java index 29b7835..98c4b30 100644 --- a/src/main/java/io/github/simplex/simplexss/ServiceManager.java +++ b/src/main/java/io/github/simplex/simplexss/ServiceManager.java @@ -1,6 +1,7 @@ package io.github.simplex.simplexss; import io.github.simplex.api.IService; +import org.bukkit.NamespacedKey; import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @@ -10,7 +11,6 @@ import reactor.core.publisher.Mono; import java.util.Arrays; import java.util.HashSet; import java.util.Set; -import java.util.stream.Stream; public final class ServiceManager { private final Set servicePools; @@ -21,22 +21,27 @@ public final class ServiceManager { servicePools = new HashSet<>(); } - @Contract(pure = true, value = "_ -> new") - public @NotNull Mono createServicePool(IService... services) { - ServicePool pool = new ServicePool(false); - Stream.of(services).forEach(pool::addService); + @Contract(pure = true, value = "_, _ -> new") + public @NotNull Mono createServicePool(NamespacedKey poolName, IService... services) { + ServicePool pool = new ServicePool(poolName, false); + Flux.fromIterable(Arrays.asList(services)).doOnEach(s -> pool.addService(s.get())); return Mono.just(pool); } - public @NotNull Mono multithreadedServicePool(IService... services) { - ServicePool pool = new ServicePool(true); - Flux.fromIterable(Arrays.asList(services)).doOnEach(s -> { - pool.addService(s.get()); - }); + @Contract(pure = true, value = "_, _ -> new") + public @NotNull Mono multithreadedServicePool(NamespacedKey name, IService... services) { + ServicePool pool = new ServicePool(name, true); + Flux.fromIterable(Arrays.asList(services)).doOnEach(s -> pool.addService(s.get())); return Mono.just(pool); } - @Contract("_, _ -> param1") + @Contract(pure = true, value = "_, _ -> new") + public @NotNull Mono emptyServicePool(NamespacedKey poolName, boolean multithreaded) { + ServicePool pool = new ServicePool(poolName, multithreaded); + return Mono.just(pool); + } + + @Contract("_, _ -> new") public @NotNull Mono addToExistingPool(@NotNull ServicePool pool, IService... services) { Flux.fromIterable(Arrays.asList(services)).doOnEach(s -> { pool.addService(s.get()); @@ -44,7 +49,7 @@ public final class ServiceManager { return Mono.just(pool); } - @Contract("_, _ -> param1") + @Contract("_, _ -> new") public @NotNull Mono takeFromExistingPool(@NotNull ServicePool pool, IService... services) { Flux.fromIterable(Arrays.asList(services)).doOnEach(s -> { pool.removeService(s.get()); @@ -52,14 +57,17 @@ public final class ServiceManager { return Mono.just(pool); } + @Contract(" -> new") public @NotNull Flux getServicePools() { return Flux.fromIterable(servicePools); } + @Contract(pure = true) public boolean locateServiceWithinPools(IService service) { return servicePools.stream().map(p -> p.isValidService(service)).findFirst().orElseGet(() -> false); } + @Contract("_ -> new") public @NotNull Mono getAssociatedServicePool(IService service) { if (!locateServiceWithinPools(service)) return Mono.empty(); return getServicePools() @@ -67,6 +75,7 @@ public final class ServiceManager { .next(); } + @Contract("-> _") public Plugin getProvidingPlugin() { return plugin; } diff --git a/src/main/java/io/github/simplex/simplexss/ServicePool.java b/src/main/java/io/github/simplex/simplexss/ServicePool.java index 782f07d..7033f82 100644 --- a/src/main/java/io/github/simplex/simplexss/ServicePool.java +++ b/src/main/java/io/github/simplex/simplexss/ServicePool.java @@ -1,6 +1,7 @@ package io.github.simplex.simplexss; import io.github.simplex.api.IService; +import org.bukkit.NamespacedKey; import org.jetbrains.annotations.NotNull; import reactor.core.Disposable; import reactor.core.publisher.Flux; @@ -17,16 +18,23 @@ import java.util.concurrent.TimeUnit; public final class ServicePool { private final Set associatedServices; private final Scheduler scheduler; + private final NamespacedKey name; + private static final NamespacedKey DEFAULT = new NamespacedKey("simplex_ss", "default_service_pool"); - public ServicePool(boolean multithreaded) { + public ServicePool(NamespacedKey name, boolean multithreaded) { + this.name = name; this.associatedServices = new HashSet<>(); if (multithreaded) { - this.scheduler = Schedulers.fromExecutorService(Executors.newFixedThreadPool(4)); + this.scheduler = Schedulers.newBoundedElastic(4, 10, ""); } else { this.scheduler = Schedulers.fromExecutorService(Executors.newSingleThreadExecutor()); } } + static NamespacedKey getDefaultNamespacedKey() { + return DEFAULT; + } + void addService(IService service) { getAssociatedServices().add(service); } @@ -40,17 +48,17 @@ public final class ServicePool { return associatedServices; } - public Mono startService(int serviceID) { - Mono service = getService(serviceID); + public Mono startService(NamespacedKey service_name) { + Mono service = getService(service_name); return service.map(s -> { - if (s.isRepeating()) { + if (s.isPeriodic()) { return scheduler.schedulePeriodically(s, - s.getDelay() * 5, - s.getPeriod() * 5, + s.getDelay() * 50, + s.getPeriod() * 50, TimeUnit.MILLISECONDS); } return scheduler.schedule(s, - s.getDelay() * 5, + s.getDelay() * 50, TimeUnit.MILLISECONDS); }); @@ -60,16 +68,17 @@ public final class ServicePool { return Mono.just(getAssociatedServices()).flatMapMany(services -> { Set disposables = new HashSet<>(); for (IService service : services) { - if (service.isRepeating()) { + if (service.isPeriodic()) { disposables.add(scheduler.schedulePeriodically(service, - service.getDelay() * 5, - service.getPeriod() * 5, + service.getDelay() * 50, + service.getPeriod() * 50, TimeUnit.MILLISECONDS)); } else { - disposables.add(scheduler.schedule(service)); + disposables.add(scheduler.schedule(service, + service.getDelay() * 50, + TimeUnit.MILLISECONDS)); } } - ; return Flux.fromIterable(disposables); }); } @@ -78,13 +87,13 @@ public final class ServicePool { return disposableThread.doOnNext(Disposable::dispose).then(); } - public Mono stopService(int serviceID) { - return getService(serviceID).doOnNext(IService::stop).then(); + public Mono stopService(NamespacedKey service_name) { + return getService(service_name).doOnNext(IService::stop).then(); } - public Mono getService(int serviceID) { + public Mono getService(NamespacedKey service_name) { return Flux.fromIterable(getAssociatedServices()) - .filter(service -> service.getServiceID() == serviceID) + .filter(service -> service.getNamespacedKey().equals(service_name)) .next(); }