Input Menu — kéo vật phẩm vào GUI
Tính năng đặc trưng của ShinnUI: người chơi shift-click kéo vật phẩm từ
inventory của mình vào GUI, menu "góp" chúng vào các slot khai báo sẵn, và chỉ
trừ đồ thật khi bạn chạy action input (commit).
Model "buffer thuần": đồ trong inventory thật không bao giờ bị trừ khi nạp vào —
đóng menu = mất gì đâu. Trừ đồ chỉ xảy ra đúng một lần, đồng bộ, tại action input.
Cách hoạt động (luồng người chơi)#2>
- Shift-click vật phẩm trong inventory dưới (kể cả hotbar) → đồ được "nạp" vào slot Input trong GUI chấp nhận nó. GUI hiển thị số đã nạp và mục tiêu dạng
{current}/{goal} trong lore của slot.
- Shift-click tiếp vào cùng loại đồ → gộp tiếp, tới khi slot đầy (
max-amount).
- Overflow: nếu slot đang nhận đã đầy mà còn slot khác cùng nhận, số dư tự tràn sang slot tiếp theo.
- Withdraw: shift-click vào một slot Input đang có buffer → rút toàn bộ buffer của slot đó (đồ "quay về" — thực ra chưa bao giờ rời inventory thật, chỉ là bỏ hiển thị buffer).
- Click nút Confirm → chạy action
input → trừ đúng số lượng khỏi inventory thật → grant phần thưởng theo logic menu của bạn.
Vì buffer chỉ là hiển thị (không di chuyển đồ thật), người chơi không thể dup:
một stack thật chỉ được buffer đúng một lần (plugin theo dõi sourceRemainder từng slot gốc),
và lúc commit plugin quét lại inventory thật theo matcher/isSimilar để trừ — số trừ là số
thật có trên người, không phải con số client báo.
Khai báo slot Input#2>
{current}/{goal} trong lore của slot.max-amount).input → trừ đúng số lượng khỏi inventory thật → grant phần thưởng theo logic menu của bạn.Vì buffer chỉ là hiển thị (không di chuyển đồ thật), người chơi không thể dup:
một stack thật chỉ được buffer đúng một lần (plugin theo dõi sourceRemainder từng slot gốc),
và lúc commit plugin quét lại inventory thật theo matcher/isSimilar để trừ — số trừ là số
thật có trên người, không phải con số client báo.
Node Input: cấp menu, mỗi phần tử là một slot:
Input:
- slot: 10 # Slot tĩnh (0-53) trong GUI — bắt buộc
material: IRON_INGOT # (tùy chọn) chỉ nhận material này
required: 4 # (tùy chọn) phải đủ 4 mới được commit; 0 = tùy chọn
max-amount: 8 # (tùy chọn, mặc định 64) buffer tối đa
# --- hoặc lọc bằng matcher thay cho material:
# matcher: 'enchant:sharpness ; !lore:rusty'
# --- hoặc item của Nexo:
# nexo-id: ruby
| Key | Bắt buộc | Ý nghĩa |
|---|---|---|
slot | ✓ | Vị trí slot trong cửa sổ chest (0–53). Có thể dùng cú pháp slot tĩnh của TrMenu (range '10-14'…) — mỗi slot sinh một InputSlot riêng. |
material | — | Chỉ nhận đúng Material vanilla. Không đặt material/matcher/nexo-id → nhận mọi đồ. |
matcher | — | Item Matcher đầy đủ — conjunctive (mọi trait phải đúng). Ưu tiên dùng khi cần lọc enchant/PDC/NBT. |
nexo-id | — | Chỉ nhận Nexo item theo id. Nếu Nexo không cài → slot không nhận gì. |
required | — | Số lượng bắt buộc trước khi commit được phép. Commit sẽ trừ đúng required, phần dư buffer (và dư trong inventory) giữ nguyên. |
max-amount | — | Trần buffer của slot. required <= max-amount. |
Khi khai báo nhiều filter (ví dụ cả material và matcher), item phải
thỏa tất cả. Slot display (placeholder) trong layout nên set Hide-Player-Inventory: false
để người chơi thấy và kéo được đồ từ inventory dưới.
Action input — commit#2>
'D':
display:
material: Lime Dye
name: '&a&lConfirm'
actions:
all:
# input là EVAL action: chạy ĐỒNG BỘ ngay khi gặp, trước condition phía sau
- 'input'
- 'condition: js: funInt("meta:input_taken") >= 10'
actions:
- 'msg: &aĐã đủ nguyên liệu! Chế tạo thành công.'
- 'sound: ENTITY_PLAYER_LEVELUP-1-1'
- 'give-item: material:DIAMOND_PICKAXE'
- 'close'
deny:
- 'msg: &cChưa đủ đồ. Thiếu: ${formatMissing}'
- 'close'
Hành vi của action input (alias input-take):
- Yêu cầu quyền
shinnui.use— không có quyền thì không trừ gì cả. - Gate: nếu bất kỳ slot có
required > 0chưa đủ số → không trừ gì, buffer giữ nguyên, setinput_taken = 0vàinput_missing(chuỗi dạng"IRON_INGOT x4, GOLD_INGOT x1"), rồi không dừng chuỗi action — đểcondition/denyphía sau tự hiển thị phản hồi. - Commit thành công: trừ đúng
requiredmỗi slot (slot không required thì trừ toàn bộ buffer). Nếu inventory thật không còn đủ số, phần thiếu được re-buffer lại slot đó (safe-commit) — ý định của người chơi không bị âm thầm mất đi. - Set meta
input_taken= tổng số thực trừ được — dùng con số này để grant thưởng (đúng theo thực tế, không theo buffer client). - Là ActionEval nên nó chạy ngay lập tức trong danh sách action — việc này bảo đảm
conditionphía sau đọc được giá trịinput_takenmới (action thường bị hoãn chạy cuối chuỗi). - Nếu gọi từ thread async, action tự chuyển sang main thread (menu/bukkit bắt buộc main thread).
Meta & placeholder của Input#2>
| Meta / Placeholder | Giá trị |
|---|---|
%shinnui_meta_input% / {meta:input} | Tổng số đồ đang buffer (mọi slot). |
%shinnui_meta_input_<slot>% | Số đồ buffer trong slot cụ thể — %shinnui_meta_input_10%. |
%shinnui_meta_input_count% | Số slot Input đang có đồ. |
%shinnui_meta_input_taken% | Số đồ thực trừ ở lần commit gần nhất. |
%shinnui_meta_input_missing% | Danh sách thiếu dạng ITEM xN sau commit fail — màu hóa từng món qua JS helper (xem Functions). |
# Lore động hiển thị tiến độ trên chính slot Input / nút Confirm lore: - '&7Buffered: &b&l%shinnui_meta_input_10%&7 / &f4' - '&7Total: &b&l%shinnui_meta_input%' - '&7Taken: &a&l%shinnui_meta_input_taken%'
Mục tiêu hiển thị {current}/{goal} trên slot buffer: goal là required
nếu slot khai báo, ngược lại là max-amount. Icon trong layout hiển thị số bằng
%shinnui_meta_input_<slot>% được tự refresh ngay sau mỗi lần deposit/commit.
Ví dụ hoàn chỉnh — công thức craft#2>
File menus/Input-Demo.yml kèm plugin là ví dụ mẫu: 4 sắt + 4 vàng + 2 kim cương.
Tóm tắt cấu trúc:
Title: 'an Input Demo'
Options:
Hide-Player-Inventory: false # BẮT BUỘC false để kéo được đồ
Bindings:
Commands: [ '(?i)input(-)?(demo)?' ]
Input:
- { slot: 10, material: IRON_INGOT, required: 4, max-amount: 8 }
- { slot: 12, material: GOLD_INGOT, required: 4, max-amount: 8 }
- { slot: 14, material: DIAMOND, required: 2, max-amount: 4 }
Icons:
'A': # placeholder slot 10 — lore hiển thị %shinnui_meta_input_10% / 4
...
'D': # nút Confirm — actions: [input, condition..., deny: msg+close]
...
'X': # nút Close — actions: 'close' (buffer tự drop, không mất gì)
Lọc nâng cao bằng matcher#2>
File Input-Matcher-Demo.yml demo slot không giới hạn material mà lọc theo đặc trưng:
Input:
- slot: 10
material: DIAMOND_SWORD
matcher: 'enchant:sharpness ; !lore:rusty' # có Sharpness, KHÔNG có lore "rusty"
required: 1
- slot: 12
matcher: 'pdc:trmenu:quest:done' # item có tag PDC = "done"
required: 1
- slot: 14
matcher: 'nbt:custom:rarity:legendary' # NBT custom.rarity = legendary (cần NBTAPI)
required: 1
- slot: 16
nexo-id: ruby # Nexo item "ruby"
required: 1
Chi tiết cú pháp matcher ở trang Item Matcher.
FAQ#2>
Người chơi đóng menu giữa chừng thì sao?
Buffer bị drop, không trừ gì. Đó chính là ưu điểm buffer-pure: đóng = hủy an toàn.
Chỉ action input mới trừ đồ.
Đổi trang layout có mất buffer không?
Không — buffer được "vẽ lại" vào slot tương ứng khi đổi trang/refresh (những slot Input
không thuộc layout mới thì buffer vẫn nằm trong session).
Tại sao nút Confirm của tôi luôn báo thiếu đồ?
Nếu bạn dùng condition ngay sau input, hãy chắc chắn dùng cú pháp
reaction (condition nằm trong element riêng, ví dụ - 'input' rồi
- condition: ...; actions: ...; deny: ...). Vì input là eval action chạy
sync, input_taken luôn mới; nhưng nếu bạn đọc meta trước khi chạy input thì
chắc chắn là giá trị cũ.
Chặn spam click?
Mọi click (kể cả deposit Input) đi qua Options.Click-Limit trong settings.yml
(mặc định 40 click/s) và Min-Click-Delay của menu — client hack spam packet sẽ bị chặn.
Người chơi đóng menu giữa chừng thì sao?
Buffer bị drop, không trừ gì. Đó chính là ưu điểm buffer-pure: đóng = hủy an toàn.
Chỉ action input mới trừ đồ.
Đổi trang layout có mất buffer không?
Không — buffer được "vẽ lại" vào slot tương ứng khi đổi trang/refresh (những slot Input không thuộc layout mới thì buffer vẫn nằm trong session).
Tại sao nút Confirm của tôi luôn báo thiếu đồ?
Nếu bạn dùng condition ngay sau input, hãy chắc chắn dùng cú pháp
reaction (condition nằm trong element riêng, ví dụ - 'input' rồi
- condition: ...; actions: ...; deny: ...). Vì input là eval action chạy
sync, input_taken luôn mới; nhưng nếu bạn đọc meta trước khi chạy input thì
chắc chắn là giá trị cũ.
Chặn spam click?
Mọi click (kể cả deposit Input) đi qua Options.Click-Limit trong settings.yml
(mặc định 40 click/s) và Min-Click-Delay của menu — client hack spam packet sẽ bị chặn.