Membership and slots
Slots are worked out per account, every time a player reaches the character screen:
Config.Slots free slots, or the /setslots number if one was set + tier.slots the bonus on the highest tier the account holds + purchased slots the sum of every live `slots` grant (plus the largest Config.DiscordAPI.roleSlots match) ------------------ = clamped to Config.MaxSlotsOnly one tier applies: whichever source lands the account on the highest rank.
Set Config.Membership.stackPurchasedSlots = false to have the larger of the tier
bonus and bought slots win rather than adding up.
| Source | Granted by | Survives losing it? |
|---|---|---|
tebex |
a webstore package running mcperks tier ... |
yes — a database row, with an optional expiry |
discord |
holding a role in that tier’s discord list |
no — the role is read live |
ace |
Config.Membership.ace |
n/a |
staff |
an admin running mcperks |
yes |
Selling slots through Tebex
Section titled “Selling slots through Tebex”Nothing in this resource takes money. Tebex takes the payment; a grant is written when the purchase is delivered.
-
Point
Config.Store.urlat your webstore and fill inConfig.Store.packages.tebexIdis the package id on Tebex — with it filled in, each card deep-links to that package. -
Add a command to the Tebex package. Use the online command slot so
{id}resolves:mcperks slots {id} 1 0 {transaction}mcperks tier {id} gold 30 {transaction}Arguments are
<player> <amount|tier> [days] [reference].daysof0never expires; a subscription should pass the days it renews on so the tier lapses by itself. -
{transaction}is the reference, and that column is UNIQUE — a retried delivery pays out once, not twice.
Step 2 is optional. With Config.Store.tebex.autoDeliver on (the default),
recent payments are read and matched against each player as they reach the
character screen, so a purchase lands whether or not the webstore ever ran
anything — including purchases made before this resource was installed. Reading
payments does not drain the Tebex command queue, so it cannot double up with
the official tebex resource or txAdmin. Buyers are matched on every identifier
they have (licence, Cfx.re, Steam hex and decimal, Discord).
A purchased package can only be auto-delivered if its webstore id is the
tebexId of an entry in Config.Store.packages. Anything unmapped is named once
in the console; mcperks tebex lists every package on your store with its id. For
older payments, mcperks payments and mcperks claim <player> <transaction> do
the same job by hand.
Price sync. Turning on Config.Store.tebex.enabled with a secret key refreshes
package names and prices from the Tebex Plugin API, so a card cannot advertise a
price the checkout disagrees with. The key is read from nuggs_tebex_secret,
falling back to sv_tebexSecret.
Price permanent packs above the subscriptions. A permanent slot has to cost more than a month of the membership that includes the same slot, or the membership is dead on arrival. The shipped numbers pay for themselves after two to three months.
Selling nothing. Leave Config.Store.enabled = false and the STORE button
shows Config.InfoPanels.store instead. Tiers still work.
Discord roles
Section titled “Discord roles”Two convars, both required:
set nuggs_discord_token "your-bot-token"set nuggs_discord_server "your-server-id-or-invite-link"| The id | Discord Settings → Advanced → Developer Mode on, then right-click your server name → Copy Server ID |
| An invite link | Any permanent invite — discord.gg/yourserver. Resolved to the id at startup, and the console prints which server it matched |
(“Guild” and “server” are the same thing — Discord’s UI says server, its API says
guild. nuggs_discord_guild is still read, so an older server.cfg keeps
working.)
The bot must be in that server with the Server Members Intent enabled in the Discord developer portal, and the player must be running Discord. A bad token, a rate limit or Discord being down never blocks the character screen.
Which role grants what, in order of precedence:
- A role id in a tier’s
discordlist —discord = { "000000000000000000" } - A role name in the same list —
discord = { "Bronze Member" } - Nothing at all — with
Config.DiscordAPI.autoMapByNameon (the default), a role whose name matches a tier’sid,shortorlabelgrants that tier.
Config.DiscordAPI.roleSlots adds slots only — the sidebar still says STANDARD
MEMBER. It accepts names as well as ids.
The startup banner prints every role in your guild next to what it grants:
[INFO] discord roles 12 in server 000000000000000000[INFO] Bronze Member 000000000000000000 tier bronze[INFO] Server Booster 000000000000000001 +1 slot(s)[INFO] Verified 000000000000000002 -mcperks discord <playerId> does the same for one player, bypassing both caches.
When a membership lapses
Section titled “When a membership lapses”Characters above the new limit are not hidden and not deleted. They stay in the line-up marked ABOVE YOUR LIMIT: they cannot be played, they can still be deleted, and the card sends the player to the store.
