Skip to content

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.MaxSlots

Only 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

Nothing in this resource takes money. Tebex takes the payment; a grant is written when the purchase is delivered.

  1. Point Config.Store.url at your webstore and fill in Config.Store.packages. tebexId is the package id on Tebex — with it filled in, each card deep-links to that package.

  2. 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]. days of 0 never expires; a subscription should pass the days it renews on so the tier lapses by itself.

  3. {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.

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:

  1. A role id in a tier’s discord list — discord = { "000000000000000000" }
  2. A role name in the same list — discord = { "Bronze Member" }
  3. Nothing at all — with Config.DiscordAPI.autoMapByName on (the default), a role whose name matches a tier’s id, short or label grants 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.

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.