Skip to content

Commit

Permalink
feat!: onUpdateContents
Browse files Browse the repository at this point in the history
  • Loading branch information
entrypointkr committed Dec 22, 2023
1 parent 5bb5862 commit 0cc06c8
Show file tree
Hide file tree
Showing 12 changed files with 167 additions and 65 deletions.
2 changes: 1 addition & 1 deletion core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ plugins {
}

group 'io.typecraft'
version '5.4.0'
version '6.0.0'

repositories {
mavenCentral()
Expand Down
104 changes: 69 additions & 35 deletions core/src/main/java/io/typecraft/bukkit/view/BukkitView.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.Plugin;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
Expand All @@ -35,48 +32,33 @@ public static void openView(ChestView view, Player player, Plugin plugin) {
holder.setView(view);
Inventory inv = Bukkit.createInventory(holder, view.getRow() * 9, view.getTitle());
holder.setInventory(inv);
OpenEvent event = new OpenEvent(view, player);
OpenEvent event = new OpenEvent(player, view);
updateInventory(view.getContents(), inv, event);
player.openInventory(inv);
}

/**
* Update the inventory contents to the given contents of view.
* Update the inventory to the given items of view.
* This won't cause InventoryCloseEvent and InventoryOpenEvent.
* If the new controls overwrite a player accessible slot, then the item will be returned back to player.
*
* @return false if the title and size of inventory player seeing is different from the given view; true if success.
* @return false if the view can't be updated -- the title and size of inventory player seeing is different from the given view; true if success.
*/
public static boolean updateView(ChestView view, Player player) {
public static boolean updateView(ChestView newView, Player player) {
Inventory topInv = player.getOpenInventory().getTopInventory();
InventoryHolder holder = topInv.getHolder();
String title = player.getOpenInventory().getTitle();
int size = topInv.getSize();
if (holder instanceof ViewHolder && title.equals(view.getTitle()) && size == (view.getRow() * 9)) {
if (holder instanceof ViewHolder && title.equals(newView.getTitle()) && size == (newView.getRow() * 9)) {
ViewHolder viewHolder = (ViewHolder) holder;
viewHolder.setView(view);
// TODO: Is this naming `OpenEvent` correct even for update?
updateInventory(view.getContents(), topInv, new OpenEvent(view, player));
// update contents
viewHolder.setView(newView);
updateInventory(newView.getContents(), topInv, new OpenEvent(player, newView));
return true;
}
return false;
}

private static Optional<ViewHolder> getOpenedViewHolder(UUID playerId) {
Player p = Bukkit.getPlayer(playerId);
InventoryHolder holder = p != null ? p.getOpenInventory().getTopInventory().getHolder() : null;
return holder instanceof ViewHolder
? Optional.of((ViewHolder) holder)
: Optional.empty();
}

/**
* Get an opened view from the given player id.
*/
public static Optional<ChestView> getOpenedView(UUID playerId) {
return getOpenedViewHolder(playerId)
.flatMap(holder -> Optional.ofNullable(holder.getView()));
}

private static void updateInventory(ViewContents contents, Inventory inv, OpenEvent event) {
inv.clear();
for (Map.Entry<Integer, ViewControl> pair : contents.getControls().entrySet()) {
Expand Down Expand Up @@ -108,6 +90,7 @@ public void onClick(InventoryClickEvent e) {
return;
}
Inventory topInv = e.getView().getTopInventory();
Inventory bottomInv = e.getView().getBottomInventory();
ViewHolder holder = topInv.getHolder() instanceof ViewHolder ? ((ViewHolder) topInv.getHolder()) : null;
if (holder == null || !holder.getPlugin().getName().equals(plugin.getName())) {
return;
Expand All @@ -128,6 +111,53 @@ public void onClick(InventoryClickEvent e) {
e.setCancelled(true);
}
break;
case SHIFT_LEFT:
Inventory clickedInv = e.getClickedInventory();
if (clickedInv == null) {
break;
}
if (viewControl != null) {
e.setCancelled(true);
}
Inventory targetInventory = e.getView().getTopInventory().equals(clickedInv)
? bottomInv
: e.getView().getTopInventory();
int targetSlot = targetInventory.firstEmpty();
if (clickedInv.equals(bottomInv) && view.getContents().getControls().containsKey(targetSlot)) {
e.setCancelled(true);
}
List<Integer> overwrittenSlots = view.getOverwriteMoveToOtherInventorySlots();
if (clickedInv.equals(bottomInv) && !overwrittenSlots.isEmpty()) {
ItemStack item = e.getCurrentItem();
if (item == null || item.getType() == Material.AIR) {
return;
}
int targetEmptySlot = view.findFirstSpace(overwrittenSlots, item);
if (targetEmptySlot >= 0 && targetSlot != targetEmptySlot) {
e.setCancelled(true);
runSync(() -> {
InventoryHolder theHolder = targetInventory.getHolder();
ViewHolder viewHolder = theHolder instanceof ViewHolder ? ((ViewHolder) theHolder) : null;
if (viewHolder == null) {
return;
}
ItemStack clickedItem = clickedInv.getItem(e.getSlot());
// check the item is equal after 1 tick
if (clickedItem == null || !clickedItem.equals(item)) {
return;
}
ItemStack targetItem = targetInventory.getItem(targetEmptySlot);
if (targetItem == null || targetItem.getType() == Material.AIR) {
targetInventory.setItem(targetEmptySlot, clickedItem);
} else if (targetItem.isSimilar(clickedItem) && targetItem.getAmount() + clickedItem.getAmount() <= targetItem.getType().getMaxStackSize()) {
targetItem.setAmount(targetItem.getAmount() + clickedItem.getAmount());
}
clickedInv.setItem(e.getSlot(), null);
viewHolder.updateViewContentsWithPlayer(p);
});
}
}
break;
default:
e.setCancelled(true);
break;
Expand All @@ -143,7 +173,7 @@ public void onClick(InventoryClickEvent e) {
handleAction(p, holder, action);
}
// update user input items
runSync(holder::updateViewContents);
runSync(() -> holder.updateViewContentsWithPlayer(p));
}

@EventHandler
Expand All @@ -159,6 +189,7 @@ public void onDrag(InventoryDragEvent e) {
return;
}
// update user input items
Player p = (Player) e.getWhoClicked();
ChestView newView = view.withContents(view.getContents().updated(topInv));
holder.setView(newView);
if (
Expand All @@ -167,7 +198,7 @@ public void onDrag(InventoryDragEvent e) {
) {
e.setCancelled(true);
}
runSync(holder::updateViewContents);
runSync(() -> holder.updateViewContentsWithPlayer(p));
}

@EventHandler
Expand All @@ -181,10 +212,11 @@ public void onClose(InventoryCloseEvent e) {
if (view == null) {
return;
}
// update user input items
Player p = (Player) e.getPlayer();
ViewAction action = ViewAction.NOTHING;
try {
action = view.getOnClose().apply(new CloseEvent(view, p));
action = view.getOnClose().apply(new CloseEvent(p, view));
} catch (Exception ex) {
plugin.getLogger().log(Level.WARNING, ex, () -> "Error on inventory close!");
}
Expand All @@ -208,8 +240,10 @@ private void handleAction(Player p, ViewHolder holder, ViewAction action) {
}
if (action instanceof ViewAction.Open) {
ViewAction.Open open = (ViewAction.Open) action;
holder.setView(null);
runSync(() -> openView(open.getView(), p, plugin));
runSync(() -> {
openView(open.getView(), p, plugin);
holder.setView(null);
});
} else if (action instanceof ViewAction.Reopen) {
runSync(() -> openView(currentView, p, plugin));
} else if (action instanceof ViewAction.Close) {
Expand All @@ -227,7 +261,7 @@ private void handleAction(Player p, ViewHolder holder, ViewAction action) {
} else if (action instanceof ViewAction.Update) {
ViewAction.Update update = (ViewAction.Update) action;
ChestView newView = currentView.withContents(update.getContents());
updateInventory(update.getContents(), holder.getInventory(), new OpenEvent(newView, p));
updateInventory(update.getContents(), holder.getInventory(), new OpenEvent(p, newView));
holder.setView(newView);
} else if (action instanceof ViewAction.UpdateAsync) {
ViewAction.UpdateAsync updateAsync = (ViewAction.UpdateAsync) action;
Expand All @@ -236,7 +270,7 @@ private void handleAction(Player p, ViewHolder holder, ViewAction action) {
ViewContents contents = updateAsync.getContentsFuture().get(30, TimeUnit.SECONDS);
runSync(() -> {
ChestView newView = currentView.withContents(contents);
updateInventory(contents, holder.getInventory(), new OpenEvent(newView, p));
updateInventory(contents, holder.getInventory(), new OpenEvent(p, newView));
holder.setView(newView);
});
} catch (Exception ex) {
Expand Down
56 changes: 40 additions & 16 deletions core/src/main/java/io/typecraft/bukkit/view/ChestView.java
Original file line number Diff line number Diff line change
@@ -1,27 +1,51 @@
package io.typecraft.bukkit.view;

import lombok.Data;
import lombok.Builder;
import lombok.Value;
import lombok.With;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;

import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;

@Data(staticConstructor = "of")
@Value(staticConstructor = "of")
@With
@Builder
public class ChestView {
private final String title;
private final int row;
private final ViewContents contents;
private final Function<CloseEvent, ViewAction> onClose;
@Builder.Default
String title = "";
@Builder.Default
int row = 6;
@Builder.Default
ViewContents contents = ViewContents.ofControls(Collections.emptyMap());
@Builder.Default
Function<CloseEvent, ViewAction> onClose = e -> ViewAction.NOTHING;
@Builder.Default
Consumer<UpdateEvent> onContentsUpdate = e -> {};
@Builder.Default
List<Integer> overwriteMoveToOtherInventorySlots = Collections.emptyList();

/**
* A simple constructor of ChestView
*
* @param title the inventory title
* @param row the inventory row
* @param contents the inventory contents
* @return ChestView
*/
public static ChestView just(String title, int row, ViewContents contents) {
return new ChestView(title, row, contents, e -> ViewAction.NOTHING);
int findFirstSpace(List<Integer> slots, @Nullable ItemStack item) {
int ret = -1;
for (Integer slot : slots) {
if (getContents().getControls().containsKey(slot)) {
continue;
}
ItemStack slotItem = getContents().getItems().get(slot);
if (slotItem == null || slotItem.getType() == Material.AIR) {
return slot;
}
if (item != null && item.isSimilar(slotItem)) {
int newAmount = slotItem.getAmount() + item.getAmount();
if (newAmount <= slotItem.getType().getMaxStackSize()) {
return slot;
}
}
}
return ret;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
@With
public class ClickEvent {
private final ChestView view;
private final Player clicker;
private final Player player;
// TODO: Can this normalize?
private final ClickType click;
private final InventoryAction action;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
@Data
@With
public class CloseEvent {
private final ChestView view;
private final Player player;
private final ChestView view;
}
2 changes: 1 addition & 1 deletion core/src/main/java/io/typecraft/bukkit/view/OpenEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
@Value
@With
public class OpenEvent {
Player player;
ChestView view;
Player viewer;
}
15 changes: 15 additions & 0 deletions core/src/main/java/io/typecraft/bukkit/view/UpdateEvent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.typecraft.bukkit.view;

import lombok.Value;
import lombok.With;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;

import java.util.Map;

@Value
@With
public class UpdateEvent {
Player player;
Map<Integer, ItemStack> items;
}
16 changes: 15 additions & 1 deletion core/src/main/java/io/typecraft/bukkit/view/ViewHolder.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.typecraft.bukkit.view;

import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.plugin.Plugin;
Expand All @@ -14,13 +15,26 @@ public ViewHolder(Plugin plugin) {
this.plugin = plugin;
}

/*
user input contents 업데이트 시점:
- onClick
- onDrag
*/
void updateViewContents() {
ChestView view = getView();
Inventory inv = getInventory();
if (view == null) {
return;
}
setView(this.view.withContents(this.view.getContents().updated(inv)));
setView(view.withContents(view.getContents().updated(inv)));
}

void updateViewContentsWithPlayer(Player player) {
updateViewContents();
ChestView view = getView();
if (view != null) {
view.getOnContentsUpdate().accept(new UpdateEvent(player, view.getContents().getItems()));
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,11 @@ public ChestView toView(int page) {
viewControls.put(pair.getKey(), control);
}
ViewContents contents = ViewContents.ofControls(viewControls);
return ChestView.just(getTitle(), getRow(), contents);
return ChestView.builder()
.title(title)
.row(row)
.contents(contents)
.build();
}

private static <T> List<T> pagingList(int elementSize, int page, List<T> list) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,26 @@ public static Optional<ChestView> loadViewConfig(ConfigurationSection section) {
}
if (slot >= 0) {
controls.put(slot, ViewControl.of(
e -> ViewPlugin.inst.replacePlaceholder(e.getViewer(), item.clone()),
e -> ViewPlugin.inst.replacePlaceholder(e.getPlayer(), item.clone()),
e -> {
List<String> cmds = commands.stream()
.map(a -> a.replace("%player%", e.getClicker().getName()))
.map(a -> a.replace("%player%", e.getPlayer().getName()))
.collect(Collectors.toList());
for (String cmd : cmds) {
StringCommandExecutor.execute(ViewPlugin.inst, e.getClicker(), cmd);
StringCommandExecutor.execute(ViewPlugin.inst, e.getPlayer(), cmd);
}
return ViewAction.NOTHING;
}
));
}
}
return Optional.of(ChestView.of(title, row, ViewContents.of(controls, new HashMap<>()), e -> ViewAction.NOTHING));
return Optional.of(ChestView.builder()
.title(title)
.row(row)
.contents(ViewContents.of(controls, new HashMap<>()))
.onClose(e -> ViewAction.NOTHING)
.onContentsUpdate(e -> {})
.build());
}

public static Optional<ItemStack> parseItem(ConfigurationSection xs) {
Expand Down
Loading

0 comments on commit 0cc06c8

Please sign in to comment.