package com.github.ustc_zzzz.virtualchest.api.action; import com.google.common.collect.ClassToInstanceMap; import org.spongepowered.api.command.CommandResult; import org.spongepowered.api.entity.living.player.Player; import org.spongepowered.api.event.Event; import org.spongepowered.api.item.inventory.ItemStackSnapshot; import org.spongepowered.api.util.annotation.NonnullByDefault; import java.lang.ref.WeakReference; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.function.Predicate; /** * Representation of an action executor. * * @author ustc_zzzz * @see LoadEvent */ @NonnullByDefault @FunctionalInterface public interface VirtualChestActionExecutor { /** * Execute the submitted action and return a result. Asynchronous execution is allowed. * * @param parent parent result * @param command command suffix * @param context immutable context map * @return a completable future which will provide a {@link CommandResult} */ CompletableFuture<CommandResult> execute(CommandResult parent, String command, ClassToInstanceMap<Context> context); /** * An empty interface whose implementations represent the contexts of a submitted action. * <p> * Those implementations can be retrieved from the immutable {@link ClassToInstanceMap} passed * as the last parameter of {@link #execute} method. The keys of the {@link ClassToInstanceMap} * can be either constructed directly or got from the static fields declared in the interface. * <p> * For example, the expression {@code context.getInstance(Context.PLAYER).getPlayer()} returns * an {@link Optional} which may contains a reference of a player. Considering that the action * may be submitted by the server console, or the player may become offline since the execution * is continuing (asynchronous execution may continue for several ticks), the {@link Optional} * may be empty and contains nothing. * * @author ustc_zzzz */ @NonnullByDefault interface Context { Class<PlayerContext> PLAYER = PlayerContext.class; Class<ActionUUIDContext> ACTION_UUID = ActionUUIDContext.class; Class<HandheldItemContext> HANDHELD_ITEM = HandheldItemContext.class; } /** * @author ustc_zzzz */ @NonnullByDefault final class PlayerContext implements Context { private final WeakReference<Player> player; public PlayerContext() { this.player = new WeakReference<>(null); } public PlayerContext(Player player) { this.player = new WeakReference<>(player); } public Optional<Player> getPlayer() { return Optional.ofNullable(this.player.get()); } } /** * @author ustc_zzzz */ @NonnullByDefault final class ActionUUIDContext implements Context { private final UUID actionUniqueId; public ActionUUIDContext(UUID actionUUID) { this.actionUniqueId = actionUUID; } public UUID getActionUniqueId() { return this.actionUniqueId; } } /** * @author ustc_zzzz */ @NonnullByDefault final class HandheldItemContext implements Context { private final Predicate<ItemStackSnapshot> handheldItem; public HandheldItemContext(Predicate<ItemStackSnapshot> handheldItem) { this.handheldItem = handheldItem; } public boolean matchItem(ItemStackSnapshot item) { return this.handheldItem.test(item); } } /** * Representation of a event, fired while VirtualChest is being loaded at the stage the server * is starting. The event will be fired on the main thread. * <p> * The registration for all of the action executors provided by VirtualChest itself is ensured * to be finished completely before this event is fired. * * @author ustc_zzzz */ @NonnullByDefault interface LoadEvent extends Event { /** * Register a prefix and its corresponding action executor. Multiple action executors could * be registered to the same prefix and they will be executed orderly. * * @param prefix the prefix to be registered * @param actionExecutor the corresponding action executor */ void register(String prefix, VirtualChestActionExecutor actionExecutor); } }