diff --git a/commons/src/main/java/ru/progrm_jarvis/minecraft/commons/control/AbstractPlayerControls.java b/commons/src/main/java/ru/progrm_jarvis/minecraft/commons/control/AbstractPlayerControls.java new file mode 100644 index 000000000..ebcba0dc7 --- /dev/null +++ b/commons/src/main/java/ru/progrm_jarvis/minecraft/commons/control/AbstractPlayerControls.java @@ -0,0 +1,139 @@ +package ru.progrm_jarvis.minecraft.commons.control; + +import lombok.*; +import lombok.experimental.FieldDefaults; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +import javax.annotation.Nullable; +import java.util.Collection; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +/** + * Abstract implementation of {@link PlayerControls} providing its common mechanisms. + */ +@ToString +@EqualsAndHashCode +@AllArgsConstructor +@FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true) +public abstract class AbstractPlayerControls
+ implements PlayerControls
{
+
+ /**
+ * Plugin whose player controls those are.
+ */
+ @NonNull P plugin;
+
+ /**
+ * Whether this control session is a global player container or not
+ */
+ @Getter boolean global;
+
+ /**
+ * Map of player's currently managed by this player controls and their current active sessions
+ */
+ @NonNull Map<@NonNull Player, @NonNull S> sessions;
+
+ /**
+ * Reference to the currently set event handler of this player controls
+ */
+ @NonNull AtomicReference type of plugin owning this player controls
+ * @param
+ extends BukkitPluginContainer , PlayerContainer {
+
+ /**
+ * Starts the new controls session for the player.
+ *
+ * @param player player for whom to start the controls session
+ * @return created controls session for the player (or the one he currently has in this player controls)
+ *
+ * @apiNote the session should be ended using {@link S#end()}
+ */
+ @NonNull S startSession(@NonNull Player player);
+
+ @Override
+ default void addPlayer(@NonNull final Player player) {
+ startSession(player);
+ }
+
+ /**
+ * Gets the current controls session of the specified player.
+ *
+ * @param player player for whom to get the controls sessions
+ * @return optional of the player's current controls session or empty if it doesn't have one
+ */
+ @NonNull Optional
+ extends AbstractPlayerControls {
+
+ public ScrollPlayerControls(@NonNull final P plugin, final boolean global,
+ @NonNull final Map<@NonNull Player, @NonNull ScrollPlayerControls.Session> sessions) {
+ super(plugin, global, sessions);
+ }
+
+ @Override
+ @NonNull protected Session createSession(final Player player) {
+ return new Session(player);
+ }
+
+ /**
+ * Handles the slot-change packet-event sending the event if needed.
+ *
+ * @param player player for whom to handle slot change
+ * @param slot slot now selected by the player
+ *
+ * @implNote initializes the event object only if needed (event handler is not null)
+ */
+ protected void handleSlotChanged(@NonNull final Player player, final int slot) {
+ val eventHandler = this.eventHandler.get();
+ if (eventHandler != null) eventHandler.accept(new Event(player, (byte) (slot - 4)));
+ }
+
+ /**
+ * Session of this scroll player controls
+ */
+ public class Session extends AbstractPlayerControls.Session {
+
+ public Session(final Player player) {
+ super(player);
+ }
+ }
+
+ /**
+ * An event to be passed to event handler whenever a player scrolls.
+ */
+ @Value
+ @FieldDefaults(level = AccessLevel.PROTECTED, makeFinal = true)
+ public static class Event {
+
+ /**
+ * Player whose event this one is
+ */
+ Player player;
+
+ /**
+ * Positive value for scroll to right, negative value for scroll to left, {@code 0} for no update.
+ */
+ byte delta;
+ }
+
+ /**
+ * Function used to fill the player's hotbar with items.
+ */
+ @FunctionalInterface
+ public interface HotbarFiller {
+
+ /**
+ * Gets the item to be set in player's inventory at the specified slot.
+ *
+ * @param player player for whom to update the item in inventory
+ * @param slot value between {@code 0} and {@code 8} (inclusive), an index of hotbar slot (from left to right)
+ * @return non-empty optional containing a non-null item to be set in player's hotbar at the specified slot
+ * or empty optional if the slot should not be updated for the player
+ */
+ Optional<@NonNull ItemStack> getItem(@Nonnull Player player, int slot);
+ }
+
+ /**
+ * Packet handler to be used to intercept incoming selected-slot-change packets.
+ */
+ protected class SlotChangePacketHandler extends PacketAdapter {
+
+ public SlotChangePacketHandler() {
+ super(ScrollPlayerControls.this.plugin, PacketType.Play.Client.HELD_ITEM_SLOT);
+ }
+
+ @Override
+ public void onPacketReceiving(final PacketEvent event) {
+ val player = event.getPlayer();
+ if (sessions.containsKey(player)) {
+ handleSlotChanged(player, new WrapperPlayClientHeldItemSlot(event.getPacket()).getSlot());
+ event.setCancelled(true);
+ }
+ }
+ }
+}
getSession(@NonNull final Player player) {
+ return Optional.ofNullable(sessions.get(player));
+ }
+
+ /**
+ * Creates the controls session for the player specified.
+ *
+ * @param player player for whom to initialize the controls session
+ * @return created controls session
+ *
+ * @implSpec implementations are not required to add the created session to {@link #sessions}
+ * as this is done by this method's caller
+ *
+ * @see Session#startSession(Player) this method's default caller
+ */
+ @NonNull protected abstract S createSession(Player player);
+
+ /**
+ * Finalizer called in {@link S#end()} in order to cleanup everything needed when the player end his session.
+ *
+ * @param session session to release
+ * @implSpec should not call to {@link S#end()} as this will (most definitely) lead to infinite recursion
+ *
+ * @implSpec implementations are not required to remove the session from {@link #sessions}
+ * as this is done by this method's caller
+ *
+ * @see Session#end() this method's default caller
+ */
+ protected void releaseControls(@NonNull final S session) {}
+
+ @Override
+ public void subscribe(@NonNull final Consumer type of session created for player whose controls are managed
+ * @param getSession(@NonNull Player player);
+
+ @Override
+ default void removePlayer(@NonNull final Player player) {
+ getSession(player).ifPresent(Session::end);
+ }
+
+ @Override
+ default boolean containsPlayer(@NonNull final Player player) {
+ return getSession(player).isPresent();
+ }
+
+ /**
+ * Subscribes the event handler on this player controls' events.
+ *
+ * @param eventHandler event handler to be used whenever an event is fired
+ */
+ void subscribe(@NonNull Consumer