Skip to main content

Rewards

Reward queue for offline players with claim menu, cron grants and claim events.

Privatev0.2.0Economy & crafting
RequiresTypeWriter0.9.0-beta-177 (BTC build)GUIMySQLMiniPlaceholdersoptional

Built for the BornToCraft network. This extension is documented for reference and is not distributed.

Overview​

Rewards keeps a per-player queue of rewards that have been earned but not yet collected. Instead of giving an item or running a command on the spot, you push a reward into the queue. The player collects it later from a menu, when they are online and have room in their inventory. Nothing is lost if the player is offline or their inventory is full when the reward is granted.

A reward is a template you define once (a reward definition: display item, lore, free slots required, actions to run on claim). Granting it adds a small record to the queue; claiming it removes the record and triggers the TypeWriter actions you linked to the definition. Because the claim effect is a list of TypeWriter actions, a reward can give items, money, a quest step or anything else TypeWriter can trigger.

The queue is stored in a single artifact. When the MySQL extension provides Redis, servers sharing the same storage are told to reload the artifact after each save, so a reward granted on one server can be claimed on another.

Features​

  • Deferred claiming — rewards wait in the queue until the player claims them.
  • Reward definitions — one reusable template per reward type, with display item, name, lore and a free-slot requirement.
  • Claim actions — a claim triggers any TypeWriter action list instead of a console command.
  • Standalone claim menu — a paginated inventory with reward slots, info slot, page buttons, bulk claim and back button.
  • Embeddable buttons — reward slots and a bulk-claim button can be placed on any GUI page through button types.
  • Cron grants — btc_give_reward can grant a reward at most once per cron period, per player (daily, weekly rewards).
  • Claim event — an event entry fires on each claim with the reward id, custom data and counters.
  • Pending-count fact and placeholders — use the queue in criteria, scoreboards and menus.
  • Anti-spam — a 500 ms cooldown between claims of the same player.

Installation​

  1. Install the GUI extension (its plugin name in the build is GuiAndDialogs) and the MySQL extension, both declared as dependencies of Rewards. Redis is only used for cross-server refresh and is not needed on a single server.
  2. Put the Rewards JAR in plugins/Typewriter/extensions/ and restart the server.
  3. Optionally install MiniPlaceholders to use the rewards placeholders in MiniMessage text.

Getting started​

Give a daily login reward that players collect from a menu.

  1. In the TypeWriter web panel, open a static page and add a Rewards Artifact. Add only one: the placeholders and the embedded buttons use the first one they find.
  2. Add a Rewards Config entry (also only one) to adjust messages and the reward lore layout.
  3. Add a Reward Definition. Set rewardId to daily_login, source to daily_login, pick a displayItem, and link the actions that give the reward in onClaimActions.
  4. Create a Give Reward action. Link the artifact and the reward, enable useCron and set cronExpression to 0 0 0 * * * for once per day. Trigger it from a join event or an NPC.
  5. Create an Open Rewards Menu action with the same artifact, and trigger it from a command or an NPC.
  6. Publish. A player who triggers the grant gets one pending reward; clicking it in the menu claims it.
Counting rewards

Use Has Pending Rewards in a criteria to show a notification only while the player has something to collect.

How it works​

The queue stores only the rewardId, the grant time and the custom data. The item shown in the menu and the actions to run are always read from the definition at the moment of display or claim. A queued reward whose definition has been deleted is not shown and cannot be claimed.

Rewards can be shown in two ways that share the same claim logic.

Standalone menu. Open Rewards Menu opens its own inventory with rows from 1 to 6. Pending rewards fill rewardSlotsConfig in order (a range, or an explicit slot list when useRange is off). When there are more rewards than slots, the menu is paginated with the optional previousPageButton and nextPageButton. The optional infoSlot, bulkClaimButton and backButton take slot numbers, an item, a MiniMessage name and lore. backButton.command runs as the player (without the leading slash); leave it empty to simply close the menu. Empty slots are filled with backgroundItem when it is set. Slot numbers go from 0 (top left) to 8 on the first row, then 9 to 17, and so on.

Buttons on any GUI page. In a GUI menu, set the buttonType of an item to one of the Rewards markers. The item you configure keeps its position and style; for reward slots it is replaced by the reward's own display item.

Button typeBehaviour
rewards_button:REWARD_SLOT#<n>Shows the viewer's n-th pending reward (counting from 0); left click claims it. Past the last reward the slot is empty.
rewards_button:BULK_CLAIMLeft click claims every pending reward the player has room for.

Claim rules, in both modes:

  • The reward needs requiredInventorySlots empty slots in the player inventory (0 means no check). If there is not enough room, the claim is refused and inventoryFullMessage is sent.
  • A claim triggers the definition's onClaimActions, then fires every Reward Claimed event entry. A bulk claim goes from the last queued reward to the first, and fires the event once with the reward id bulk_claim.
  • Success and bulk messages come from the Rewards Config entry. For embedded buttons, the first Rewards Config entry found is used.

The lore of a reward item is built from the definition's additionalLore, then a separator and the source and free-slot lines, then a closing tip; the labels come from the Rewards Config entry.

Placeholders​

MiniPlaceholders expansion rewards, usable in MiniMessage text as tags.

PlaceholderReturns
<rewards_pending>Number of pending rewards of the player.
<rewards_pending:rewardId>Number of pending rewards with this rewardId.
<rewards_source:source>Number of pending rewards whose definition has this source (case-insensitive). Rewards with a deleted definition are not counted.
<rewards_claimed_total>Number of rewards the player has claimed in total.

The reward id is an argument of the tag: write <rewards_pending:daily_login>, not <rewards_pending_daily_login>. All placeholders return nothing if no Rewards Artifact exists.

Entries​

7 entries, generated from the extension source.

Actions2
Events1
Facts1
Artifacts1
Manifests2

Troubleshooting​

The menu does not open and the log says the GUI Extension is not installed. btc_open_rewards_menu needs the GUI extension at runtime. Install it and restart. The action also does nothing, with a warning, when its artifact field is empty.

Give Reward does nothing. The log shows a warning when the action has no artifact, no reward, or when the linked definition has an empty rewardId. Fill the three fields.

A cron reward is granted only once. With useCron on, the last grant time is stored per player and per action entry. The next grant happens only after the next time the cron expression matches after that moment. Test with a short expression or use a different action entry per schedule.

A pending reward is invisible or cannot be claimed. The menu skips queued rewards whose definition cannot be found by rewardId. Do not change the rewardId of a definition after rewards have been granted.

Claim refused with the inventory-full message. The player has fewer empty slots than requiredInventorySlots. Note that the check counts every empty slot of the player inventory object, including armor and off-hand slots.

Toasts, sounds, noRewardsMessage and onClaimFailActions have no effect. These fields exist in Rewards Config and Reward Definition, but the code of this version never reads them. Only the claim, inventory-full and bulk messages and the lore layout are applied.

Rewards do not appear on another server. Cross-server refresh relies on Redis from the MySQL extension and on both servers reading the same artifact storage. Without Redis, each server only sees its own copy until it is restarted.

  • GUI — renders the page-driven buttons and the standalone menu.
  • MySQL — Redis cache invalidation between servers.