Shell Creator
aty_shellcreator
In-game grid interior builder: design shell interiors on a 2 m grid from a modular kit (floors, walls, openings, stairs, ceilings, decor, lights, anchor points), preview them by walking through, publish them, and let other resources such as aty_housing use them as interiors. The asset companion aty_shellcreator_kit supplies the 3D pieces the builder places.
Version 1.0.0. Live-tested on QBCore. ESX and Qbox are supported through
aty_libbut were not live-tested. Tested scope lists what was seen working in game and what was only checked with offline test harnesses.
Info
| Resource name | aty_shellcreator (companion asset: aty_shellcreator_kit) |
| Version | 1.0.0 for both resources (kit data version 7) |
| Frameworks | ESX / QBCore / Standalone (detected via aty_lib). Live-tested on QBCore only |
| Database | MySQL through aty_lib. Tables are created and migrated automatically on start, no SQL file to import |
| Lua version | Lua 5.4 (lua54 'yes'), fx_version 'cerulean', game 'gta5' |
| Sides | Client + server (shared config, grid model and locales) |
| NUI | Yes — Vue 3 + Pinia, pre-built into ui/ (shell library, editor panels, details, import and export dialogs) |
| Languages | English (en), Turkish (tr), German (de), French (fr), Spanish (es) and Brazilian Portuguese (pt-br), selected with Config.Language; a missing key falls back to English |
| Grid | Cell 2.0 m, storey height 3.2 m, wall and slab thickness 0.2 m. Default shell 20 x 20 cells (40 x 40 m), at most 32 x 32 cells (64 x 64 m), up to 6 levels including basements down to level -3 |
| Third-party notices | docs/LICENSES.md |
Tested scope
| Area | Status |
|---|---|
Resource start on QBCore with aty_lib |
Verified live: starts without errors, database migration runs, the kit is resolved and its hash is stored, ready is announced |
| Kit pieces in game | Verified live during kit development: own floor, ceiling and wall pieces have collision, the own stair works, door leaves fit their frames and swing. The kit's own notes record an in-game collision probe on 2026-09-30. The current kit data version 7 (67 materials) was not re-checked piece by piece in game for this page |
| Console import and publish | Verified live: sample interiors were created from JSON files with the console import command and published as revision 1 |
aty_housing registration |
Verified live: published shells were registered by aty_housing as sc_<id> interiors and cached in its aty_housing_built_shells table |
Opening the builder from the aty_housing creator |
A defect was found live (the player was moved into an invisible editor bucket with no screen) and fixed. Re-tested in game after the fix: works |
| Implemented and checked with offline test harnesses, not yet verified in game | Per-role permissions, multi-level builds and stairs, painting, undo and redo limits, prop budget, walk-through preview, drafts and autosave, revisions, members, import and export (share code and JSON), the 16 atmosphere presets, photo and plan thumbnails, coordinate-mode shells with teleporters and vehicle entry, the stay and restart watchdogs, shell visits for admins, and entering a property on a built shell end to end |
| ESX, Qbox | Not live-tested. Supported through aty_lib, but nothing more is claimed. The framework admin check only knows qb-core and es_extended; on any other framework only the ACE permission applies |
Dependencies
| Dependency | Required | Notes |
|---|---|---|
aty_lib |
Yes (declared in fxmanifest.lua) |
Framework, database, callback, notification, job and permission bridge. Must start first. Restarting aty_lib restarts every dependent resource, aty_shellcreator included. Requires aty_lib 1.3.9 or newer (the version released together with this one): it adds HasPermission (framework admins) and GetPlayerJob (job rules). With an older aty_lib only the ACE aty_shellcreator.admin and housing-ability rules match |
aty_shellcreator_kit |
Yes, for building | Not a declared dependency, so the resource starts without it, but then no kit is resolved: the builder is disabled and the console prints no usable shell kit - the builder stays disabled with the reason for every source tried. Players who open the manager get "No shell kit is installed. Start aty_shellcreator_kit." Start it before aty_shellcreator; it is also picked up when it starts later |
es_extended or qb-core |
One of them | Detected by aty_lib |
oxmysql (or the database resource aty_lib detects) |
One of them | Whatever aty_lib detects. Live-tested with oxmysql only |
aty_housing |
Optional | Uses published shells as property interiors. Its HasRealtorAbility export is only asked when a permission rule uses housingAbility. See aty_housing integration |
screenshot-basic |
Optional (not required) | Photo thumbnails of published shells. Without it (or with Config.Thumbnails.photo = 'off') no photo is taken; floor plan images do not need it. The state is checked when a thumbnail is requested, so start order does not matter |
aty_clothing |
Optional | This resource's code does not call it. Wardrobe anchors you place in a shell are handed to hosts such as aty_housing, which decide what a wardrobe opens |
ox_target, qb-target or aty_interactions |
Optional | Used through aty_lib for the optional target zone of coordinate-shell teleporters (Config.Teleporter.interact) |
| Inventory | Not needed | aty_shellcreator gives and takes no items. Every inventory that aty_lib supports works alongside it: aty_inventory, ox_inventory, qb-inventory, qs-inventory, ps-inventory, codem-inventory, tgiann-inventory, origen_inventory, gfx-inventory, esx_inventoryhud, ak47_inventory and ak47_qb_inventory. Live testing was done on aty_inventory only |
Installation
- Copy the
aty_shellcreatorandaty_shellcreator_kitfolders into your resources (for example[aty]/aty_shellcreatorand[aty]/aty_shellcreator_kit). - No SQL import. On every start the resource creates the tables it needs (
CREATE TABLE IF NOT EXISTS) and adds missing columns itself. It never touches your framework's tables. If the database is unreachable the migration is retried three times (5 s, then 10 s apart); after that the console prints an error and the resource stays idle until you restart it. See Database for the table list. - Add both resources to
server.cfgin the order below. - Give your administrators the permission (see Permissions).
- Check the job names and grades in
Config.Permissions(default: the jobrealestate, grade 1 and 2). - Edit
shared/config.luaif needed (see Configuration) and restart the resource. - In game, type
/shellcreatorand create your first shell.
On the first start with aty_shellcreator_kit, seven sample interiors are published as shells owned by the server: Bungalow - 2 bedrooms, Family house - 3 bedrooms, Empty Studio, Empty 1BR Apartment, Empty 2BR Apartment, Empty 3BR House and Empty Loft (Config.SampleShells, files in data/samples/). Each one is created once: a restart never duplicates it, and a sample you delete or rename is not created again. Set Config.SampleShells.enabled = false before the first start to skip them.
server.cfg
setr game_enableDynamicDoorCreation "true" # door leaves: FiveM refuses to create them without it
ensure oxmysql
ensure qb-core # or es_extended
ensure aty_lib # always before any aty_* resource
ensure screenshot-basic # optional, photo thumbnails
ensure aty_shellcreator_kit # the 3D kit, before the builder
ensure aty_shellcreator
ensure aty_housing # optional, after the builder
add_ace group.admin aty_shellcreator.admin allow
aty_shellcreator waits up to 10 seconds at start for aty_shellcreator_kit to begin starting, and resolves the kit again whenever the kit resource starts or stops. aty_housing notices aty_shellcreator starting or stopping at any time, so its position is not critical, but the order above is the tested one.
Admin permission
A player is a Shell Creator admin when either check says yes, framework first:
- The framework's own admin permission (
Config.AdminPermission): qb-core permission levels in.qb(default{ 'admin', 'god' }) or ESX groups in.esx(default{ 'admin', 'superadmin' }), asked throughaty_lib. - The ACE permission
Config.AdminAce(defaultaty_shellcreator.admin). It is also checked when the framework says the player is not an admin:
add_ace group.admin aty_shellcreator.admin allow
add_principal identifier.license:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx group.admin
On qb-core, group.admin is not a permission level: a player who is in group.admin but not in qbcore.admin or qbcore.god needs the ACE line above. The server console always counts as an admin. To use only ACE, set the lists in Config.AdminPermission to {}.
Kit resource
Start aty_shellcreator_kit before aty_shellcreator. The details are in Kit resource.
Database
All tables are created on start. Nothing here needs manual work; it is listed so you know what to include in backups.
| Table | Content |
|---|---|
aty_shellcreator_shells |
One row per shell: title, description, owner, visibility, status, current revision, edit lock, world placement and teleporter settings |
aty_shellcreator_revisions |
Published revisions of every shell (layout data) |
aty_shellcreator_drafts |
Working copies per character, written by autosave |
aty_shellcreator_members |
Per-shell members (editor or viewer role) |
aty_shellcreator_snippets |
Saved clipboard snippets per character |
aty_shellcreator_thumbs |
Photo and floor plan thumbnails |
aty_shellcreator_returns |
Return points for builders and visitors, so a player is put back where they started |
aty_shellcreator_log |
Audit log (opens, publishes, deletes, refused actions, unlocks, imports) |
aty_shellcreator_meta |
Schema version and the kit hash used at the last boot |
Notes:
- The owner of a shell is the framework character id. Shells created from the console belong to the server (no owner).
- Housekeeping runs at boot and every 6 hours: stale edit locks older than
Config.Editor.lockTimeoutSecondsare cleared, drafts older thanConfig.Drafts.keepDaysare removed, and audit rows older thanConfig.Log.retentionDaysare removed.
Files that stay open for editing
When the resource is distributed as an escrowed asset, these stay readable and editable (upload-config.json):
| Path | Why |
|---|---|
fxmanifest.lua, shared/config.lua |
Configuration |
server/api.lua, client/api.lua |
The open integration API (exports, events, hooks) |
locales/*.lua and locales/ui/*.lua |
Translate or reword any text, including the builder panels |
docs/*.md |
Documentation |
This list is the escrow edition. The open edition (a separate download of the same resource) is not protected: its Lua files are readable and editable as well.
data/*.json (the base decor catalogue and the starting templates in data/templates/*.json) are plain data files, not Lua. In the escrow edition all other Lua files are protected. The UI source code and the development tools are not part of the package; the pre-built panel is in ui/. For aty_shellcreator_kit the open files are fxmanifest.lua, kit.json and LICENSE.md.
Configuration
Everything is set in shared/config.lua. The file is read when the resource starts: restart aty_shellcreator after every change. Distances are metres unless stated, and durations say their unit in the option name. Every key is read on both sides unless noted; the server re-checks everything the client sends.
Core
| Option | Type | Default | Description |
|---|---|---|---|
Config.Debug |
boolean | false |
Verbose console output |
Config.Language |
string | 'en' |
'en', 'tr', 'de', 'fr', 'es' or 'pt-br'. Missing keys fall back to English |
Config.Command |
string | 'shellcreator' |
Chat command that opens the shell library. Also the name of the server console command (see Commands) |
Config.ExitCommand |
string | 'shellcreatorexit' |
Emergency command: closes the editor or preview, restores the character and releases focus |
Config.AdminPermission |
table | { qb = { 'admin', 'god' }, esx = { 'admin', 'superadmin' } } |
Framework admin permission, checked first. {} skips the framework check on that framework |
Config.AdminAce |
string | 'aty_shellcreator.admin' |
ACE permission, the last-resort admin check |
Config.HideOthers |
boolean | false |
true = non-admins never see other users' shells (owners and members still see theirs) |
Config.RequireDuty |
boolean | false |
true = job criteria in Config.Permissions only count while the player is on duty |
Config.Kit |
string | 'auto' |
'auto', 'aty_shellcreator_kit' or 'base_temp'. See Kit resource |
Config.SampleShells |
table | { enabled = true, list = { 'bungalow_2bed', 'family_3bed', 'empty_studio', 'empty_1br', 'empty_2br', 'empty_house_3bed', 'empty_loft' } } |
Sample interiors in data/samples/<key>.json, published once with the own kit (same checks as the console import). A published sample is remembered in aty_shellcreator_meta (sample:<key>), so it is never created twice; a shell that already has the sample's title is adopted instead. enabled = false = never; remove a key from list to skip that sample. Nothing is seeded with the temporary base_temp kit |
Config.KitExtensions |
table | {} |
RESERVED, not read by any code in this version. Leave it empty |
Config.CustomTextures |
table | { enabled = false, slots = { walls = {}, floors = {} } } |
RESERVED, not read by any code in this version. Leave it off |
Config.Doors.system |
boolean | true |
Register built door leaves with the game's door system |
Config.Doors.enableDynamicDoors |
boolean | true |
The server sets the replicated convar game_enableDynamicDoorCreation to "true" at start when server.cfg does not. Without it FiveM refuses to create door leaves. Keep the setr line in server.cfg anyway |
Config.Log.retentionDays |
number | 180 |
Audit log rows older than this are removed |
Permissions
Config.Permissions holds one rule per capability. The rule syntax and every default are described in Permissions.
Grid and budget
| Option | Type | Default | Description |
|---|---|---|---|
Config.Grid.defaultSize |
table | { w = 20, d = 20 } |
Size of a new shell in cells (2 m each), even numbers |
Config.Grid.minSize |
table | { w = 4, d = 4 } |
Smallest size in cells |
Config.Grid.maxSize |
table | { w = 32, d = 32 } |
Largest size in cells (64 x 64 m) |
Config.Grid.defaultLevels |
number | 1 |
Levels of a new shell |
Config.Grid.maxLevels |
number | 6 |
Total levels including basements |
Config.Grid.minLevel |
number | -3 |
Lowest basement level index |
Config.Budget.maxProps |
number | 800 |
Runtime props per shell (the config comment gives the range 50 to 1500) |
Config.Budget.warnAt |
number | 0.85 |
Share of the prop budget at which the editor warns |
Config.Budget.maxDecor |
number | 400 |
Decor items per shell |
Config.Budget.maxDecorPerCell |
number | 8 |
Decor items per cell |
Config.Budget.maxDecorPerFace |
number | 6 |
Decor items per wall face |
Config.Budget.maxLights |
number | 32 |
Lights per shell |
Config.Budget.maxAnchors |
number | 24 |
Anchor points per shell |
A shell that goes over the prop, light or decor caps cannot be published.
Editor
| Option | Type | Default | Description |
|---|---|---|---|
Config.Editor.bucketBase |
number | 400000 |
Routing bucket of an editing player = bucketBase + server id. Keep it clear of other scripts (the comment names aty_housing at 100000+ and 200000+, aty_garage_v2 at 700000+) |
Config.Editor.origin |
table | { x = -2800.0, y = -4600.0, z = 1100.0 } |
Where the builder works in the world. Not checked in game for map geometry: change it if the surroundings clip |
Config.Editor.keepInput |
boolean | false |
Keep game input while the editor panel has focus. Gizmo sessions always keep it |
Config.Editor.historySize |
number | 200 |
Undo steps (20 to 1000) |
Config.Editor.historyBytes |
number | 4194304 |
Memory cap of the undo history |
Config.Editor.autosaveSeconds |
number | 30 |
Draft write interval while there are unsaved changes (15 to 300) |
Config.Editor.heartbeatSeconds |
number | 20 |
Session heartbeat to the server |
Config.Editor.lockTimeoutSeconds |
number | 90 |
A shell lock without heartbeat expires after this long |
Config.Editor.opBatchMs |
number | 700 |
How long edits are batched before they go to the server |
Config.Editor.maxOpsPerBatch |
number | 64 |
Edits per batch |
Config.Editor.maxBatchBytes |
number | 16384 |
Bytes per batch |
Config.Editor.pasteMaxCells |
number | 256 |
Largest region the clipboard pastes |
Config.Editor.spawnPerFrame |
number | 32 |
Props spawned per frame while the editor builds the view |
Config.Editor.edgePick |
number | 0.6 |
Distance from an edge that selects it |
Config.Editor.previewKeyHoldMs |
number | 600 |
How long Backspace must be held to leave the preview |
Config.Editor.maxSessionMinutes |
number | 240 |
Session cap in minutes; 0 = none |
Config.Editor.idleMinutes |
number | 20 |
No edits and no camera input for this long: autosave and close |
Config.Revisions |
table | { keep = 20, pinnedMax = 5, noteMax = 128 } |
Revisions kept per shell, pinned revisions that survive pruning, note length |
Config.Drafts |
table | { keepDays = 30, maxPerCharacter = 10 } |
Draft retention and per-character cap |
Config.Snippets |
table | { maxPerCharacter = 30, maxCells = 256 } |
Saved snippets per character and the largest snippet |
Shortcuts
Config.Shortcuts holds the default editor shortcut per action; players can rebind them in the editor's settings. Config.ReservedKeys lists keys that other resources commonly map (1 to 9, F1, F2, F3, F5, F6, F8, F11, T, Tab, B, Y, X, G, K, M, L, U, N, H, I, Z, Alt, CapsLock); these are not offered for editor shortcuts. The defaults are listed under Key bindings.
Storage (coordinate shells)
| Option | Type | Default | Description |
|---|---|---|---|
Config.Storage.origin |
table | { x = -2000.0, y = -5200.0, z = 1000.0 } |
Origin of the slot grid where coordinate-mode shells without their own world position are placed. Not checked in game for map geometry |
Config.Storage.spacing |
number | 96.0 |
Distance between slots. Must be at least the largest shell extent (64 m) plus 32 m |
Config.Storage.columns |
number | 16 |
Slots per row; rows grow towards -Y |
Changing these after coordinate shells exist moves them; the server prints an error at boot when it detects it, and also when two coordinate shells overlap.
Runtime (spawning shells for players)
| Option | Type | Default | Description |
|---|---|---|---|
Config.Runtime.spawnRadius |
number | 40.0 |
Coordinate shells build when the player is this close |
Config.Runtime.despawnMargin |
number | 20.0 |
Extra distance before they are removed |
Config.Runtime.lingerSeconds |
number | 8 |
How long a coordinate shell stays built after the player has moved out of range |
Config.Runtime.checkMs |
number | 1000 |
How often the client checks its distance to coordinate shells |
Config.Runtime.jumpDistance |
number | 50.0 |
A move between two checks larger than this triggers an immediate check of coordinate shells (for example after a teleport) |
Config.Runtime.batchPerFrame |
number | 20 |
Props spawned per frame |
Config.Runtime.lodDist |
number | 250 |
Draw distance of built props |
Config.Runtime.modelTimeoutMs |
number | 10000 |
Wait for models to load |
Config.Runtime.payloadTimeoutMs |
number | 12000 |
Wait for the shell data from the server |
Config.Runtime.maxClientProps |
number | 2000 |
Props one client keeps built across shells |
Config.Runtime.cacheShells |
number | 12 |
Shells cached on the client |
Config.Runtime.serverCacheShells |
number | 50 |
Shells cached on the server |
Config.Runtime.kvpCache |
boolean | true |
Cache shell data in the client's key-value store |
Config.Runtime.kvpMaxBytes |
number | 262144 |
Size cap of that cache |
Config.Runtime.freezeVehicles |
boolean | true |
Freeze vehicles this client controls inside a shell that is about to be removed, so they do not fall |
Config.Runtime.fallbackSpawn |
table | { x = -1035.71, y = -2731.87, z = 12.86, w = 0.0 } |
Where a player is put when the shell they were in is gone |
A new published revision reaches players already inside a shell on their next entry; there is no live respawn.
Network and rate limits
| Option | Type | Default | Description |
|---|---|---|---|
Config.Net.latentBps |
number | 131072 |
Bandwidth of latent uploads (bytes per second) |
Config.Net.uploadChunk |
number | 16000 |
Upload chunk size |
Config.Net.thumbMaxBytes |
number | 180000 |
Largest photo thumbnail |
Config.Net.planMaxBytes |
number | 60000 |
Largest plan thumbnail |
Config.Net.importMaxBytes |
number | 524288 |
Largest import |
Config.Net.layoutMaxBytes |
number | 524288 |
Largest stored layout |
Config.RateLimits.defaultMs |
number | 300 |
Minimum gap between two calls of the same kind without its own rule |
Config.RateLimits.ops |
table | { perSecond = 6, burst = 12, perMinute = 2400 } |
Edit batches per player |
Config.RateLimits.query |
number | 2 |
Query rate |
Config.RateLimits.payloadPerMinute |
number | 20 |
Shell data requests per player and minute |
Config.RateLimits.payloadPerShellPerMinute |
number | 5 |
Shell data requests per player, shell and minute |
Config.RateLimits.openSeconds |
number | 3 |
Gap between two editor opens |
Config.RateLimits.saveSeconds |
number | 10 |
Gap between two saves |
Config.RateLimits.importPerMinute |
number | 3 |
Imports per minute |
Config.RateLimits.teleportSeconds |
number | 2 |
Gap between two teleporter uses |
Config.RateLimits.moveSeconds |
number | 5 |
Gap between two shell moves |
Thumbnails, atmosphere and lights
| Option | Type | Default | Description |
|---|---|---|---|
Config.Thumbnails.photo |
string | 'auto' |
'auto' takes a photo when screenshot-basic runs; 'off' never does |
Config.Thumbnails.width, .height |
number | 480, 270 |
Photo size |
Config.Thumbnails.quality |
number | 0.8 |
Photo compression quality |
Config.Thumbnails.planSize |
number | 512 |
Floor plan image size |
Config.Thumbnails.yaw, .pitch, .fov |
number | 45.0, 38.0, 40.0 |
Camera angle of the photo |
Config.Thumbnails.fill |
number | 0.8 |
How much of the frame the shell fills |
Config.Thumbnails.settleMs |
number | 600 |
Wait before the photo is taken |
Config.Atmosphere.presets |
table | 16 presets | Ids: neutral, warm, cool, dim, night, neon, clinical, industrial, luxury, vintage, noir, sunset, foggy, bright, cinematic, dark. Each has a label key, a timecycle modifier name, a strength and optionally an hour or weather override. Names the running game build does not know are skipped |
Config.Atmosphere.default |
string | 'neutral' |
Preset of a new shell |
Config.Atmosphere.weathers |
table | { 'EXTRASUNNY', 'CLEAR', 'CLOUDS', 'OVERCAST', 'FOGGY' } |
Weather locks a builder can choose |
Config.Atmosphere.defaultHour |
number | 12 |
Clock lock inside a shell |
Config.Atmosphere.defaultWeather |
string | 'EXTRASUNNY' |
Weather lock inside a shell |
Config.Atmosphere.noRain |
boolean | true |
No rain inside a shell |
Config.Atmosphere.pauseEvents |
table | { enter = {}, leave = {} } |
Client events to fire when a player enters or leaves a shell, to pause and resume a weather or time sync script, for example { { event = 'qb-weathersync:client:DisableSync' } } |
Config.Lights.maxDrawn |
number | 24 |
Lights drawn at once on one client |
Config.Lights.drawDistance |
number | 30.0 |
Light draw distance |
Config.Lights.defaultRange |
number | 6.0 |
Range of a new light |
Config.Lights.defaultIntensity |
number | 1.5 |
Intensity of a new light |
Teleporter (coordinate shells)
| Option | Type | Default | Description |
|---|---|---|---|
Config.Teleporter.enabled |
boolean | true |
false turns every shell teleporter off |
Config.Teleporter.interact |
string | 'both' |
'marker', 'target' or 'both'; a shell's own teleporter setting wins |
Config.Teleporter.key |
string | 'E' |
Default key of the aty_sc_use key mapping |
Config.Teleporter.drawDistance |
number | 20.0 |
The ring marker shows within this distance |
Config.Teleporter.interactDistance |
number | 1.8 |
The prompt works within this distance |
Config.Teleporter.standStillMs |
number | 500 |
The player must stand still this long before the prompt appears |
Config.Teleporter.fadeMs |
number | 400 |
Screen fade time |
Config.Teleporter.instanceBucketBase |
number | 500000 |
Routing bucket of a per-player instance = base + server id |
Config.Teleporter.marker |
table | { type = 25, size = { x = 1.1, y = 1.1, z = 1.1 }, color = { 105, 255, 236, 110 } } |
Ring marker style |
Config.Teleporter.watchdogSeconds |
number | 5 |
Interval of the server check for players standing inside a restricted coordinate shell without access |
Restricted teleporters. A teleporter whose access is not everyone is restricted. Its entry point is sent only to players who may use it (admins, players with a live grant, and players who pass the access rule: jobs, ACE, members or the canUseTeleporter hook, cached for 30 seconds). Everyone else receives the shell with entry = false and restricted = true: no ring, no prompt, no coordinates. The list is re-sent to a player whose right changes (job, ACE, grant, membership). The exit inside a shell always works.
The server also guards the shell itself. Every watchdogSeconds it checks the players in the default routing bucket: a player standing inside a restricted coordinate shell without a grant is moved back to the teleporter entry by the server (the vehicle they sit in is moved with them), so a modified client cannot stay inside. Admins, players with a grant (a teleporter entry grants the shell to the driver and every passenger of the vehicle) and players in a per-player instance are not moved. Each intrusion is written to the shell's audit log (at most once a minute per player and shell).
Commands
Every command is registered unrestricted and checked on the server; a player without the right gets a notification, nothing happens.
| Command | Where | Description |
|---|---|---|
/shellcreator |
Player | Opens the shell library (manager). Needs the open capability |
/shellcreator edit <id> |
Player | Opens shell <id> in the editor |
/shellcreator view <id> |
Player | Opens shell <id> read-only |
/shellcreator tp <id> |
Player, admins | Visits shell <id>. /shellcreator tp without an id returns from the visit |
/shellcreatorexit |
Player | Emergency exit: closes the editor or preview, restores the character, releases focus |
shellcreator kit |
Server console | Prints the active kit id, version, source, hash, grid, tint count, model count and decor count |
shellcreator rebake [id] |
Server console | Clears the shell data cache and rebakes the current revision of one shell or all published shells, with timings |
shellcreator validate [id] |
Server console | Prints the publish validation result (error and warning codes) of one shell or all |
shellcreator unlock <id> |
Server console | Clears the edit lock of a shell and closes the editor session holding it |
shellcreator purge <id> |
Server console | Permanently deletes a shell that is already in the deleted state |
shellcreator import <file> [title] |
Server console | Creates a shell owned by the server from a JSON file inside the aty_shellcreator resource folder (an exported layout, or an object with title, description, tags, visibility and layout) and publishes it. The same import and publish checks run as in the in-game dialog. A shell with the same title is never created twice |
shellcreator republish <id> <file> |
Server console | Publishes the layout in such a JSON file as a new revision of an existing shell; its id, title, owner, tags and revision history stay. Refused while the shell is open in the editor |
The command names follow Config.Command and Config.ExitCommand. The subcommands of the console version are ignored when typed by a player; a player typing /shellcreator kit simply opens the library.
Key bindings
Key bindings are registered with FiveM and can be rebound by every player under Settings, Key Bindings, FiveM.
| Binding | Default | What it does |
|---|---|---|
aty_sc_use |
E (Config.Teleporter.key) |
Use a shell entrance or exit. Acts only at a teleporter, never while the editor is open or a panel has focus |
+aty_sc_preview_exit |
BACKSPACE (hold) |
Leave the walk-through preview; hold for Config.Editor.previewKeyHoldMs |
+aty_sc_gizmo_toggle |
R |
Switch the move and rotate handles of the gizmo |
+aty_sc_gizmo_select |
MOUSE_LEFT |
Drag a gizmo handle |
Inside the editor the defaults of Config.Shortcuts apply:
| Action | Key | Action | Key |
|---|---|---|---|
| Select | Q |
Rotate | R |
| Floor | F |
Rotate left / right | [ / ] |
| Wall | C |
Undo / redo | Ctrl+Z / Ctrl+Y |
| Opening | O |
Copy / cut / paste | Ctrl+C / Ctrl+X / Ctrl+V |
| Stair | J |
Duplicate | Ctrl+D |
| Paint | P |
Mirror X / Y | Ctrl+M / Ctrl+Shift+M |
| Decor | V |
Level up / down | PageUp / PageDown |
| Light | ; | View mode | \ | | Anchor | , | Reset view | Home | | Room | . | Plan view | End | | Measure | / | Preview | Insert | | Demolish | Delete | Leave preview | Backspace | | Frame | Ctrl+F | | |
Mouse wheel while placing. When the active tool is placing something, the wheel turns it instead of zooming:
| Placing | Wheel | Shift + wheel |
|---|---|---|
| Floor or ceiling decor, anchors (spawn, exit and the other types) | 15° a notch, on the 15° grid | 5° a notch, on the 5° grid |
| Stairs, a pasted region | A quarter turn (90°) a notch | Same |
Wheel up turns the item left (counter-clockwise). R still turns 90° and Shift+R turns 15°. Ctrl + wheel zooms the camera while you place.
The wheel zooms the camera again in these cases: while a gizmo is editing an item, in demolish mode, when no item is armed for placement, for wall-only decor (it keeps the wall face heading), in the opening tool, in read-only sessions, in the walk-through preview, and during the thumbnail photo.
Outlines. Only decor items that are hovered or selected and currently visible get the engine outline. Nothing is outlined in the preview, during the photo, while the editor is loading or closing, or for items on a level the view hides. Lights and anchors are marked with wire boxes, not outlines.
Permissions
Config.Permissions has one rule per capability. A rule is 'admin', 'everyone', 'nobody', or a table. Admins (see Admin permission) pass every rule. A table rule passes when any listed criterion matches:
| Criterion | Meaning |
|---|---|
jobs = { [job] = minGrade } |
The player has that job at that grade or higher (aty_lib job lookup). With onDuty = true on the rule, or Config.RequireDuty = true, the player must also be on duty |
ace = 'name' |
The player holds that ACE permission |
housingAbility = 'name' |
aty_housing reports that realtor ability for the player (its HasRealtorAbility export). false when aty_housing is not started |
owner = true |
Counts only in checks on one specific shell: the player owns it |
Defaults (job realestate; grades are the minimum grade):
| Capability | Default | What it allows |
|---|---|---|
open |
realestate 1 |
Open the shell library |
create |
realestate 1 |
Create shells |
editOwn |
realestate 1 |
Edit own shells (as owner, or as a member with the editor role) |
editAny |
admin | Edit any shell |
publish |
realestate 2 |
Publish a revision |
deleteOwn |
realestate 2 |
Archive or delete own shells |
deleteAny |
admin | Archive, delete or restore any shell |
viewAll |
realestate 1 |
See public shells of other users |
moveCoords |
admin | Place or move a coordinate-mode shell in the world |
teleporters |
admin | Configure shell teleporters |
import |
admin | Import a shell from JSON or a share code |
export |
realestate 2 |
Export a shell |
revisions |
realestate 2 |
Browse, restore and pin revisions |
members |
realestate 2 |
Manage the member list of an own shell (the shell owner, or an admin) |
forceUnlock |
admin | Take over a shell locked by another editor |
purge |
admin | Permanently delete a deleted shell |
Shells are designed by admins and realtors; there are no player-owned shells by default. Other rule values are supported by the code but only the defaults are described as intended.
How a shell is seen by a player:
- Admins, the owner and members always see it.
- Otherwise, with
Config.HideOthers = truenobody else sees it. - With
Config.HideOthers = false, apublicshell is visible withviewAll, ateamshell is visible to players holdingopenwhose job equals the shell's team job, and aprivateshell is visible only to owner, members and admins. - A deleted shell is visible only to admins and its owner.
A player's capabilities are cached for 10 seconds, so a job change can take up to 10 seconds to apply. Every action is checked again on the server. Refused actions are written to the audit log, at most one row per player and action per minute.
Kit resource
aty_shellcreator_kit holds the 3D pieces the builder places. It has no scripts: its fxmanifest.lua streams the models and textures (stream/) and ships kit.json, the list the builder reads. Everything in it is original ATY work (see its LICENSE.md); kit.json only names three base-game models by name, which every GTA V client already has: two door leaves (door_house1, door_studio) and a pillar decor item. Stairs, railing, frames and window inserts are own models.
What kit.json (kit version 7) defines:
| Item | Count or value |
|---|---|
| Grid | Cell 2.0 m, storey 3.2 m, wall 0.2 m, slab 0.2 m |
| Materials | 67: 32 wall, 25 floor, 10 ceiling |
| Part groups | Floor and ceiling plates, walls in 8 shapes (full, half, low, arch, door, window, wide, hatch), 8 wall skins, 3 corner fillers, 13 door leaves (11 own, 1.30 x 2.30 m, plus the 2 base-game leaves above), 4 opening frames (wide, wide glass, arch glass, hatch glass), 8 window designs (each with a glass and an opaque variant), 3 stair styles (wood, dark, concrete), 1 stairwell railing, void |
| Own decor | 1 entry (a pillar). Base-game decor from data/decor_base.json is merged in (includeDecorBase), 111 built-in fit-out entries in the base catalogue (ceiling lights, bathroom fixtures, fitted kitchen units, shelving, office, wall art). 100 of the 111 show a picture icon in the editor (the same thumbnails aty_housing uses for its Decor Studio catalogue, shipped in data/thumbs/kit/); the other 11 show a category glyph. Living furniture is not shell decor: with aty_housing it comes as the property's furniture set (Decor Studio furniture) |
| Tints | None (tints.count = 0); anchor models are not part of this kit version |
Config.Kit chooses the source:
| Value | Behaviour |
|---|---|
'auto' (default) |
Use aty_shellcreator_kit when it is started and its kit.json validates; otherwise fall back to base_temp |
'aty_shellcreator_kit' |
Only the kit resource. If it is missing or invalid the builder stays disabled |
'base_temp' |
Only the temporary base-game kit. This is a development-only kit and is not shipped: data/kits/base_temp.json is excluded from the package, so on a purchased copy it does not exist and the builder stays disabled without the kit resource |
Config.KitExtensions is RESERVED for kits that extend the base kit; nothing reads it in this version.
A change of the kit while the server runs re-resolves the kit and fires the local event aty_shellcreator:kitChanged. Published shells are rebaked lazily on their next request, because the data hash includes the kit hash. A kit changed between restarts is detected at boot, and shell statistics are refreshed in the background.
Every player downloads the kit's stream files on connect. A client that is missing models answers models_missing when a shell is spawned and the shell does not build.
The kit's LICENSE.md states: copyright 2026 ATY, all rights reserved; every model, texture and archetype file is original, procedurally generated ATY work with no third-party or Rockstar textures or models included.
Exports
Server exports are called as exports.aty_shellcreator:<Name>(...). Every server export answers from memory unless marked "may yield"; before the resource is ready they return the value in brackets.
Server exports
| Export | Returns | Description |
|---|---|---|
ListShells(filter?) |
array [] |
Shells as items. filter = { status = 'published' (default) | 'archived' | 'draft' | 'any', owner, tag, coordMode }. Item: id, uid, title, status, rev, hash, tags, stats = { area, rooms, levels, props }, anchors, bounds = { min, max, radius }, hasPhoto, planLevels, updatedAt, deleted, broken. Deleted shells are never listed. broken means the current revision cannot be read |
GetShell(id) |
item or nil |
The item plus settings, owner_name, created_at, description. Works for deleted shells too, so a stored sc_<id> can be resolved |
GetShellPayload(id, rev?) |
table or nil |
Baked shell data for hosts that spawn shells themselves. May yield |
GetShellAnchors(id, rev?) |
anchor set or nil |
Anchors of the current or the given revision. Other revisions may yield |
GetShellPlan(id, level) |
table or nil |
Floor plan data of the current revision. May yield |
GetShellThumb(id, kind, level?) |
data URI or nil |
kind is 'photo' or 'plan'. May yield, cached for 5 minutes |
ContainsPoint(id, point, margin?) |
boolean | point ({ x, y, z } or array) is above an occupied cell of the current revision, within that level's clear height plus the margin. The vertical margin is capped at half a slab, so a roof counts as outside. false when unknown |
GetShellMask(id) |
table or nil |
The occupancy mask ContainsPoint reads, { levels = [{ l, minX, minY, w, d, bits }], cell, storey, clear, slab }, for hosts that mirror the check on the client |
GetWorldPlacement(id) |
{ x, y, z, h } or nil |
World placement of a coordinate-mode shell |
CanEdit(src, id) |
boolean | Whether the player may edit that shell |
GetPolicy(src, id) |
table or nil |
The player's policy for that shell: view, edit, publish, delete, restore, move, teleporter, export, revisions, members, unlock, purge, owner, member, admin |
OpenEditor(src, id?) |
true or false, key, text |
Opens the editor for that player. Without an id it opens the shell library. With an id the server checks the kit, the shell, the permission and the canOpen hook, then the player's client runs its normal open flow. text is the refusal in this resource's language |
IsEditing(src) |
shell id or nil |
The shell the player is editing |
GetKit() |
table or nil |
{ id, version, hash, capabilities, tints } of the active kit |
RegisterHook(name, fn) |
hook id or nil |
See Hooks |
RemoveHook(id) |
boolean | Removes a hook |
Anchor format: each anchor is { id, floor = { x, y, z, h }, ped = { x, y, z + 1.0, h }, label } in shell-local metres. ped is the standing-ped reading: place a ped with SetEntityCoords(ped, x, y, z - 1.0). An anchor set is { spawn, exit, stash = {...}, wardrobe = {...}, bed = {...}, kitchen = {...}, garage = {...}, custom = {...} }.
-- list published shells and read one
local shells = exports.aty_shellcreator:ListShells({ tag = 'apartment' })
for _, s in ipairs(shells) do
print(s.id, s.title, s.stats.area, s.stats.rooms)
end
local shell = exports.aty_shellcreator:GetShell(2)
local anchors = exports.aty_shellcreator:GetShellAnchors(2)
if anchors and anchors.spawn then
print('spawn at', anchors.spawn.floor.x, anchors.spawn.floor.y, anchors.spawn.floor.z)
end
-- open the builder for a player
local ok, key, text = exports.aty_shellcreator:OpenEditor(source, 2)
if not ok then print('refused:', key, text) end
Client exports
Hosted shells: the host resource owns the routing bucket and the teleport; aty_shellcreator builds the props.
| Export | Returns | Description |
|---|---|---|
SpawnShell(id, opts) |
handle, or false, reason[, missingModels] |
Builds a shell. opts = { origin = vector3 or { x, y, z }, heading = 0.0, rev = nil (current published), timeoutMs = 15000 (one deadline for data, models and collision, 1000 to 60000), hash = nil (a known data hash lets a cached copy skip the server round trip) }. Yields. Reasons: sc_invalid_input, sc_not_ready, sc_no_permission, sc_not_found, sc_shell_broken (from the server), sc_payload_timeout, models_missing, prop_cap, pool_full, models_timeout, create_refused, cancelled. Parts the client cannot create (slow, refused or missing models, door leaves without game_enableDynamicDoorCreation) do not stop the shell while the spawn point has a floor: they are printed once and retried in the background (see notSpawned and issue of GetHandleInfo) |
DespawnShell(handle) |
boolean | Removes the shell |
MoveShell(handle, origin, heading) |
boolean | Moves a built shell |
GetHandleAnchors(handle) |
table or nil |
Anchor set in world space. Each anchor is { floor = vector4, ped = vector4 (z + 1.0), label, id } |
GetHandleInfo(handle) |
table or nil |
{ handle, id, rev, hash, owner, state, origin, heading, props, doors = { doorKey... }, notSpawned, issue } |
IsInsideShell() |
handle, shellId, roomIndex or nil |
The shell the local player is in |
GetCurrentRoom() |
{ handle, id, room = { id, name, area } } or nil |
The room the local player is in |
SetDoorLocked(handle, doorKey, locked) |
boolean | Lock or unlock a door leaf |
SetRoomLights(handle, roomId | nil, on) |
boolean | Switch the lights of one room, or of the whole shell with nil |
OpenEditor(id?) |
boolean | Asks the server to open the editor (true = asked, permission is checked there); no id opens the shell library. false while an editor is already active or opening |
IsEditorActive() |
boolean | Whether the local player is in the editor |
A handle keeps the revision it was built with; a newer published revision is used by the next SpawnShell. Under the client prop cap, coordinate shells farther away are removed first. Handles are removed when the resource that created them stops.
-- client: build a shell at a spot and walk the ped to its spawn anchor
local handle, reason = exports.aty_shellcreator:SpawnShell(2, {
origin = vector3(-3500.0, -3500.0, 900.0), heading = 0.0,
})
if not handle then
print('could not build shell:', reason)
return
end
local a = exports.aty_shellcreator:GetHandleAnchors(handle)
if a and a.spawn then
SetEntityCoords(PlayerPedId(), a.spawn.ped.x, a.spawn.ped.y, a.spawn.ped.z - 1.0)
end
-- later
exports.aty_shellcreator:DespawnShell(handle)
Events
Server events are local events (TriggerEvent). Listen with AddEventHandler only, never RegisterNetEvent, so a client can never fake them.
Server events
| Event | Arguments | When |
|---|---|---|
aty_shellcreator:ready |
none | Boot finished. Re-query and register your hooks again |
aty_shellcreator:shellChanged |
id, info |
info.kind is published, archived, deleted, restored, renamed, moved, teleporter, purged or thumbnail; info.rev is the current revision |
aty_shellcreator:editorOpened |
src, id |
A player opened the editor on a shell |
aty_shellcreator:editorClosed |
src, id, mode |
The session ended |
aty_shellcreator:kitChanged |
kitHash |
The active kit changed while the server runs |
AddEventHandler('aty_shellcreator:shellChanged', function(id, info)
if info.kind == 'published' then
print(('shell %d now at revision %s'):format(id, tostring(info.rev)))
end
end)
Client events
| Event | Arguments | When |
|---|---|---|
aty_shellcreator:client:enteredShell |
handle, id |
The local player entered a built shell |
aty_shellcreator:client:leftShell |
handle, id |
The local player left it |
aty_shellcreator:client:handlesLost |
handles |
Fired synchronously while this resource stops, with every handle that is about to disappear. Set flags only and act in your own thread; the ped is frozen when inside |
aty_shellcreator:client:editorOpened |
id |
The local editor opened |
aty_shellcreator:client:editorClosed |
id |
The local editor closed |
Other aty_shellcreator:client:* and aty_shellcreator:server:* net events (openManager, openEditor, payload, index, indexDelta, forceExit, forceClose, editorLoad, upload) are internal to the resource. Use the exports instead of triggering them.
Hooks
Register a hook with exports.aty_shellcreator:RegisterHook(name, fn). Hooks are removed when the registering resource stops, so register them again on aty_shellcreator:ready. A hook that can veto returns false, message (the message is shown to the player, at most 128 characters); anything else allows.
| Hook | Data | Behaviour when your function errors |
|---|---|---|
canOpen |
{ source, shellId, charId } |
Allows (fails open) |
canPublish |
{ source, shellId, charId, stats } |
Allows (fails open) |
canArchive |
{ source, shellId, force } |
Refuses, unless force is set (admins only) |
canDelete |
{ source, shellId, force } |
Refuses, unless force is set (admins only) |
canUseTeleporter |
{ source, shellId, direction } |
Refuses (fails closed) |
usage |
{ shellId } |
Return { resource, count, label }; shown in the manager and the details view. A failing or malformed hook is skipped |
onPublished |
{ shellId, rev } |
Notification only, runs in its own thread |
onDeleted |
{ shellId, rev } |
Notification only, runs in its own thread |
-- refuse to archive or delete a shell your resource still uses
local function inUse(data)
if countMyUses(data.shellId) > 0 then
return false, 'Used by my resource'
end
end
local function register()
exports.aty_shellcreator:RegisterHook('canArchive', inUse)
exports.aty_shellcreator:RegisterHook('canDelete', inUse)
end
register()
AddEventHandler('aty_shellcreator:ready', register)
aty_housing integration
aty_housing is optional. When both run, every shell published in the Shell Creator becomes a housing shell interior named sc_<id>. This is handled by aty_housing (server/built_shells.lua) through the exports, events and hooks above; this resource needs nothing extra.
The interface follows the integration. The builder asks the server whether aty_housing is started and shows housing-specific parts only while it runs: the housing wording in the guide and the details tabs, the host export format, and the housingAbility chip of the permission rules. Without aty_housing the same places use generic wording about "host scripts" that read published shells through the exports, and the housing-only options are hidden. The state updates live when aty_housing starts or stops; nothing has to be configured.
What aty_housing takes from a published shell:
| From the shell | Used for |
|---|---|
spawn and exit anchors |
Where a player appears when entering, and the inside exit. A shell with no spawn anchor gets a fallback point at the grid origin and a warning in the housing health check |
stash anchors |
Property stashes (the first is the main stash; more become extra stashes, limited by Config.Stashes.max in aty_housing) |
wardrobe, bed, kitchen anchors |
Living points, mapped through Config.BuiltShells.fixtures (default wardrobe to wardrobe, bed to bed, kitchen to stove) |
Tags apartment, house, trailer |
The listing type (Config.BuiltShells.typeTag) |
| Stats and thumbnails | Area, rooms, levels, props and the photo and floor plan gallery of a listing |
| Bounds and occupancy mask | The inside check and the point check for captured points and furniture (ContainsPoint) |
The interior is built on the client at Config.ShellSpawn of aty_housing through SpawnShell, in the property's own routing bucket. That spawn point is marked in aty_housing as not verified in game: change it if a shell clips into anything.
The Config.BuiltShells block of aty_housing (shared/config.lua) controls the integration:
| Option | Default | Description |
|---|---|---|
enabled |
'auto' |
'auto' uses the Shell Creator when it runs and stays quiet otherwise; true does the same but a missing resource is an error in the console and the admin health check; false never |
resource |
'aty_shellcreator' |
Resource name of the Shell Creator |
planImages |
true |
Show the photo and floor plans in the listing gallery |
typeTag |
{ apartment = 'apartment', house = 'house', trailer = 'trailer' } |
Shell tag to listing type |
fixtures |
{ wardrobe = 'wardrobe', bed = 'bed', kitchen = 'stove' } |
Anchor type to living point type; false = anchors give no living points |
containsMargin |
0.5 |
Metres around the shell's floor cells that still count as inside |
radiusPad |
2.0 |
Metres added to the shell's bounding radius |
spawnTimeoutMs |
15000 |
How long a client waits for the shell to build (1000 to 60000) |
blockDelete |
true |
Refuse archiving or deleting a shell that properties use. An admin can force it |
blockEditorInside |
true |
Refuse opening the Shell Creator while the player is inside a property or on a creator visit |
notifyUpdate |
true |
Players inside hear once that a new version was published; it applies when the property is empty again |
notifyOwners |
true |
Owners and tenants are notified when their interior is archived, deleted or broken |
thumbCacheSeconds |
300 |
How long photo and floor plan images are cached on the server |
Behaviour to know:
aty_housingregisters thecanArchive,canDelete,usageandcanOpenhooks. The manager shows how many properties use a shell.- A shell that disappears (stopped, archived, deleted, purged, broken, unpublished) stays registered as unavailable, so its properties show "interior unavailable" instead of vanishing. Entering is refused, and such a property cannot be bought until the interior is back.
aty_housingkeeps the last known copy of every shell in its own tableaty_housing_built_shells, so labels and points survive whileaty_shellcreatoris not running.- A new revision does not change a property while someone is inside; it applies on the next entry.
- Publish a shell, then pick it under built shells in the
aty_housingproperty creator. The creator can also open the builder for a shell (OpenEditor).
Features
Builders (realtors and admins)
- Build on a 2 m grid with 3.2 m storeys: up to 6 levels including basements, shells of up to 64 x 64 m.
- Tools for floors, walls in 8 shapes, openings (doors, windows, wide openings), stairs, ceilings, painting (material and surface), decor, lights, anchor points, room naming and atmosphere, and a measure tool.
- Mouse-wheel rotation while placing decor, anchors, stairs and pasted regions (15° a notch,
Shift5°;Ctrl+ wheel zooms), see Key bindings. - Select, copy, cut, paste, duplicate and mirror regions; rotate; deep undo and redo; save regions as snippets and paste them into other shells.
- Start from a blank shell or one of seven templates: studio, one bedroom, two bedroom, office, shop, garage and warehouse.
- Prop, decor and light budgets with a live warning; publishing is blocked when a cap is exceeded.
- Publish validation with errors (no spawn point, spawn or exit not on a real floor or with less than 2.0 m headroom, over the caps, layout integrity, no real floor, empty title) and warnings (for example door swings over stairs or decor, perimeter openings or glass).
- Walk-through preview of the built shell before publishing.
- 16 atmosphere presets with clock and weather locks, per shell and per room.
- Orbit, plan and fly camera, level views, and an automatic photo and floor plan per level when you publish.
- Autosaved drafts, revision history with notes and pinned revisions, restore a revision as a draft.
- Share a shell as JSON or a share code; import by pasting one (the import checks size, grid version and kit keys, and reports what was replaced or dropped).
- Optional shell members (editor or viewer) so a colleague can work on your shell.
Admins
- Edit, publish, archive, delete, restore and permanently delete any shell.
- Visit any shell with
/shellcreator tp <id>and return with/shellcreator tp. - Take over a locked shell, or clear a stale lock from the console.
- Place coordinate-mode shells in the world with teleporter entrances (marker and optional target zone, per-player or shared instance, optional vehicle entry for the driver, restricted access with an intrusion watchdog).
- Import shells from files with the console
importcommand; runvalidate,rebakeandkitfor diagnostics. - Audit log of opens, publishes, deletions, unlocks, imports and refused actions.
Server owners
- No SQL import; tables are created and migrated on start.
- Permissions per capability by job and grade, ACE, duty state, or the housing realtor ability; hide other users' shells.
- Per-shell prop, decor, light and anchor caps and a global client prop cap to protect performance.
- Rate limits on every call, server-side re-validation of every edit, a server-authoritative editor lock and draft storage.
- Every value the client sends is checked on the server; the client can never send models or prop lists.
- Open integration API (exports, events, hooks) for your own scripts, and an interface in English, Turkish, German, French, Spanish and Brazilian Portuguese.
Languages
| Language | Config.Language |
Files |
|---|---|---|
| English (default, fallback) | 'en' |
locales/en.lua, locales/ui/en.lua |
| Turkish | 'tr' |
locales/tr.lua, locales/ui/tr.lua |
| German | 'de' |
locales/de.lua, locales/ui/de.lua |
| French | 'fr' |
locales/fr.lua, locales/ui/fr.lua |
| Spanish | 'es' |
locales/es.lua, locales/ui/es.lua |
| Brazilian Portuguese | 'pt-br' |
locales/pt-br.lua, locales/ui/pt-br.lua |
To switch, set Config.Language in shared/config.lua to the code in the second column and restart the resource. Server messages and the interface follow that setting. A key that is missing in the chosen language is shown in English.
Troubleshooting
| Problem | Solution |
|---|---|
Console: no usable shell kit - the builder stays disabled, or players get "No shell kit is installed. Start aty_shellcreator_kit." |
Add ensure aty_shellcreator_kit before ensure aty_shellcreator, check the folder name is exactly aty_shellcreator_kit, and run shellcreator kit in the console. The error line names the reason for every kit source that was tried |
Doorways without door leaves; F8 not spawned (doors_disabled): aty_sc_door_... |
FiveM refuses to create door leaves unless setr game_enableDynamicDoorCreation "true" is set. Config.Doors.enableDynamicDoors = true (default) sets it when the resource starts; add the line to server.cfg to make it permanent. The rest of the shell spawns without the leaves |
Config.Kit = ... is not a kit source in the console |
Use 'auto', 'aty_shellcreator_kit' or 'base_temp'. 'base_temp' does not exist in a purchased copy |
/shellcreator says "You are not allowed to do that." |
The player lacks the open capability. Give them the job and grade of Config.Permissions.open, or add the ACE line. Job changes can take up to 10 seconds to apply |
| Admin cannot do admin-only things | Check Config.AdminPermission for your framework and the ACE line add_ace group.admin aty_shellcreator.admin allow plus the add_principal. On qb-core group.admin is not a permission level. On a framework other than qb-core and es_extended only ACE works |
| A shell stays locked after a crash | The lock expires after Config.Editor.lockTimeoutSeconds (90 s) without a heartbeat. To clear it at once, run shellcreator unlock <id> in the console or use an admin account |
| Stuck in the editor, preview or with a panel on screen | Type /shellcreatorexit (or the name in Config.ExitCommand) |
A shell does not build for a player, models_missing |
The kit's stream files did not reach the client. Check aty_shellcreator_kit is started and not blocked in your stream setup, and have the player reconnect |
Shell does not build: prop_cap or pool_full |
The client hit Config.Runtime.maxClientProps or the game object pool limit. Lower Config.Budget.maxProps for new shells or reduce how many shells are built at once |
models_timeout or sc_payload_timeout |
Slow client or heavy server. Raise Config.Runtime.modelTimeoutMs / payloadTimeoutMs, or the host's own timeout (Config.BuiltShells.spawnTimeoutMs in aty_housing) |
| Publish is refused | Open the validation list in the editor. Common causes: no spawn anchor, spawn or exit not on a floor or without headroom, a cap exceeded, empty title. Run shellcreator validate <id> for the codes |
| A shell card is marked broken | Its current revision cannot be read. Restore an older revision as a draft and publish it; until then GetShellPayload returns nil and aty_housing treats the interior as unavailable |
| No photo thumbnails | Install and start screenshot-basic, and keep Config.Thumbnails.photo at 'auto'. Floor plan images do not need it |
A property in aty_housing says "interior unavailable" |
The shell is stopped, archived, deleted or broken. Start aty_shellcreator, restore or republish the shell, or check the reason in the aty_housing admin health check |
| Weather or time is wrong inside shells | Your weather sync script is fighting the shell's locks. Pause it with Config.Atmosphere.pauseEvents |
Console: Config.Storage changed ... coordinate shells moved or coordinate shells ... overlap |
Restore the previous Config.Storage values, or move one of the overlapping shells |
| Database errors at start | Check oxmysql (or your database resource) and the connection string; the migration retries three times, then the resource stays idle until restarted |
Known limitations
- Only the parts listed as verified in Tested scope were seen working in game. Everything else is implemented and checked with offline test harnesses only.
- Only QBCore was used for live tests. ESX and Qbox are untested; on a framework other than
qb-coreores_extendedthe framework admin check is skipped and only ACE applies. - No player-owned shells in this version: by default only admins and realtors design shells. The permission rules can be loosened, but that setup is not tested.
- A newly published revision reaches players already inside a shell on their next entry; there is no live respawn.
Config.Editor.originandConfig.Storage.originwere chosen far from the map and are not verified against map geometry in game. Move them if the surroundings clip or the area is not empty on your server.Config.KitExtensionsandConfig.CustomTexturesare RESERVED and have no effect in this version. The current kit has no colour tints and no anchor models.- Photo thumbnails need
screenshot-basic. - Each shell is limited by the budget caps and, per client, by
Config.Runtime.maxClientProps. - Shell names and descriptions are free text typed by builders; the interface texts are available in English, Turkish, German, French, Spanish and Brazilian Portuguese.
- Job-based rules need the job lookup of
aty_lib; without it only admin, ACE and housing-ability criteria can match. - The seven sample shells (
Config.SampleShells) are plain grid layouts made with the own kit; the two furnished houses get their furniture fromaty_housingfurniture sets (matched by title), not from the layout.
Third-party notices
Third-party components shipped with or required by these resources are listed in LICENSES.md: the Outfit typeface (SIL Open Font License 1.1, text in docs/OFL.txt), Vue and Pinia (MIT) bundled into the pre-built UI, and the separately installed resources aty_lib, aty_shellcreator_kit, aty_housing, aty_clothing and screenshot-basic. aty_shellcreator ships no Rockstar Games assets and no third-party shell, MLO or housing code. The kit's own licence statement is in aty_shellcreator_kit/LICENSE.md.