F5 StudioF5 Studio
Skip to main content

Marketplace

A crosshair marketplace inside the menu. Players publish their own crosshairs, browse everyone else's, apply one in a click, like the good ones, and open an author's profile to see the rest of their work. Moderators get a tab of their own.

The whole thing is one switch:

config/features.lua
F5Cfg.EnableMarketplace = true

With it off, the tab disappears and every marketplace call refuses — including browsing, which answers "Marketplace is disabled" rather than an empty list. Existing listings stay in the database untouched.

What a Listing Holds

A listing carries the crosshair only: the crosshair itself, its custom image, and the player's per-weapon crosshairs. Nothing else from their HUD is published — no effects, no killstreak, no K/D.

StoredFrom
Title, description, tagsThe publisher, sanitised and length-capped
The crosshairRe-validated server-side before it is written
Owner nameThe framework character name at the moment of publishing, capped at 60 characters. It is never refreshed afterwards
Display name and avatarThe FiveM name and the Steam avatar, refreshed on every edit
Likes, appliesCounters, one per player per listing

Avatars need set steam_webApiKey "…" in server.cfg. Without it they stay blank everywhere — the console says so once at startup.

Publishing

RuleDefaultConfig
Listings per player10Marketplace.maxCrosshairListings
Titlerequired, 50 charactersMarketplace.titleMaxLength
Descriptionoptional, 160 charactersMarketplace.descriptionMaxLength
Tags2–3 of the 9 availableMarketplace.tags.minTagsPerListing / maxTagsPerListing
Cooldownone publish or edit every 3 seconds

The listing cap is enforced inside the insert itself, so two fast clicks cannot get around it. Title and description are stripped of control characters and collapsed whitespace, then truncated on a UTF-8 boundary. Tags that are not in tags.available are dropped silently.

A refused publish still costs the cooldown

The 3-second limit is applied before the listing is checked. A publish rejected for a missing title, too few tags or a blocked image has already used the player's turn — they have to wait before retrying with it fixed.

A player may edit or unpublish their own listings only — both queries are scoped to the owner's licence, so a crafted id changes nothing.

Applying a listing overwrites, immediately

Apply replaces the player's live crosshair, custom image and per-weapon crosshairs, resets their active profile to Default, and saves — with no confirmation step. Players who want their old crosshair back should save it as a profile first.

Custom-image listings

config/features.lua
F5Cfg.MarketplaceSections = {
publish = true,
publishCustomImage = true,
}

With publishCustomImage = false a listing whose crosshair carries an image is refused at publish and edit time — "Custom image crosshairs cannot be published on this server". The gate looks at whether the profile carries an image for its current source — an uploaded picture in upload mode, an address in URL mode — not at whether the image is switched on. A player with a leftover upload has to clear it before they can publish a plain shape crosshair.

It is a publish-time gate only: listings that went up while it was on keep working, and can still be browsed and applied afterwards.

URL-mode images are snapshotted, not linked. When a player publishes a crosshair whose image is an external URL, the server downloads it once and stores the bytes inside the listing. That means the listing keeps working after the original link dies — and it means the download has rules:

CheckBehaviour
Fetch failed, non-200, empty body"The image could not be downloaded from that URL" — the listing is not published
Larger than snapshotMaxBytes (384 KB)"That image is too large to publish"
Encoded result over imageDataMaxBytes (512 KB)Same message
Content type not PNG / JPEG / GIF / WEBP / SVG, or the bytes do not match the type"That link does not point to a supported image file"

Upload-mode images are already stored bytes and skip this path entirely.

Tags

Nine tags ship: competitive, fun, roleplay, minimal, colorful, animated, sniper, close-range, custom-image.

custom-image is reserved. Players cannot pick it; the server adds it when the published crosshair actually renders an image and strips it otherwise. It does not count toward the maximum a player may pick, and it does not count toward the minimum they must pick — but it does have to stay in tags.available to be filterable, and startup warns you if it is missing.

Browsing

ControlBehaviourTurn off with
SearchMatches the title, or the author's name in author mode. Up to 50 characters; SQL wildcards are escapedMarketplaceSections.search
SortNewest, most applied, most likedMarketplaceSections.sort
Tag filterUp to 10 tags at once, matched as ORMarketplaceSections.tags
Author profileUp to 100 of that author's listingsMarketplaceSections.authorProfile
ApplyOne clickMarketplaceSections.apply
LikesOne per player per listing, no unlike, never your ownMarketplaceSections.likes

Turning search off does not just hide the box — the server discards any search text that arrives. Turning sort off pins every browse to defaultSort.

Hidden listings and listings by banned players are filtered out for everyone except a moderator who explicitly asks to see them.

Two invisible ceilings

Results past 5000 rows are unreachable — with the default page size of 12 that is 417 pages. And one page of results carries at most 2 MiB of inlined images: listings past that budget arrive without their picture and are marked as such, rather than being dropped.

Moderation

The Moderation tab appears for players who match F5Cfg.ModeratorAces — see Permissions. Every action is re-checked server-side, so the tab itself grants nothing.

ActionEffect
HideThe listing stays in the database but leaves circulation. A reason can be attached, up to 255 characters. It still counts against the owner's listing limit — they see it in their own profile marked as hidden, and can delete it themselves
UnhidePuts it back and clears the reason
RemoveDeletes the listing outright, along with its likes and applies
BanBlocks a player from the marketplace and deletes every listing they own
UnbanLifts the ban. Deleted listings are not restored

The panel loads one page of up to 100 listings — newest first, hidden ones included — and has no paging of its own. Moderation actions are limited to one per second per moderator.

Bans

Durations are a closed set: 1, 3, 7, 30, 90 days, or permanent. Anything else is refused before anything is written.

A banned player is blocked from publishing, editing, applying and liking. They can still browse. If they are online when the ban lands they are told immediately:

You are banned from the marketplace until 2026-09-01 18:30. Reason: stolen artwork

An empty reason reads "no reason given". Banning the same licence again updates the existing ban rather than stacking. An expired ban stops applying on its own — no restart needed.

With authorProfile off, a hidden listing is unreachable

An owner normally finds their hidden listing in their own profile view and can remove it. Switch MarketplaceSections.authorProfile off and that view is gone — the listing is invisible to everyone including its owner, while still occupying one of their slots. On a server with author profiles disabled, use Remove rather than Hide.

Unpublishing is not ban-checked

The publish, edit, apply and like calls all check the ban list. Unpublish does not — a banned player can still remove their own remaining listings. Since banning already deletes their listings, this rarely comes up.

The audit trail

Every moderation action writes a row into f5_combathud_admin_audit — actor, action, target, the old and new values as JSON — and fires a Discord webhook if one is configured. The database row is the authoritative record: it stores the licence, while the Discord embed shows the player name the moderator's client supplied.

Rate Limits

ActionLimit
Publish, edit1 per 3 s
Apply1 per 3 s
Like1 per 2 s
BrowseBurst of 10, refilling 3 per second
Author profileBurst of 5, refilling 1 per second
Every moderation action1 per s

Browsing past the limit answers with a short retry delay rather than an error message, so the menu simply waits.

Player-Facing Messages

All of these come from locales/, so they are translated:

SituationMessage
Marketplace offMarketplace is disabled
Database not ready yetDatabase is not ready — contact the server admin
No titleListing title is required
Too few tagsPick at least 2 tags before publishing
At the listing capYou can publish max 10 crosshairs
PublishedCrosshair published on marketplace
Editing someone else's listingListing not found or no permission
Listing hidden or deleted meanwhileListing not found
Liking your own listingYou cannot like your own listing
Liking twiceYou already liked this listing
Applying too fastPlease wait before applying again
Custom image blockedCustom image crosshairs cannot be published on this server
Image URL fetch failedThe image could not be downloaded from that URL
Image too largeThat image is too large to publish
Wrong image typeThat link does not point to a supported image file
Not a moderatorYou are not allowed to moderate the marketplace
BannedYou are permanently banned from the marketplace. Reason: …

See Also