Skip to main content

SIM Card System

With the SIM Card DLC enabled, a phone item is just hardware. The number lives on the SIM card, not on the phone — players buy SIMs, insert them, swap between two of them, and pay per call minute and per SMS from a prepaid credit balance.

Number follows the SIM

Move a SIM to another phone and the number moves with it. Losing the phone does not mean losing the number.

Two numbers, one phone

A phone holds up to SimCard.MaxSIMsPerPhone SIMs. Only the active one rings and sends.

Prepaid credits

Calls cost per minute, messages cost per message. An empty SIM means no calls and no SMS — emergency calls still work.

Burner phones

A SIM bought with cash, used for one job and thrown away, is a two-minute purchase at an NPC.
When the DLC is active, the SIM is the only source of a phone number. A phone without an inserted SIM has no number: it cannot call, cannot be called and cannot send messages. getNumberFromSource, getNumberFromIdentifier and every internal number lookup return the active SIM’s number.

Requirements

1

Enable metadata mode

config.lua
Config.SimCardDLC = true with Config.UseMetadata = false is refused at boot: the server prints an error and disables the DLC for that session.
2

Use an inventory with metadata support

SIM cards store their identity in item metadata. ox_inventory, one_inventory, jaksam and tgiann are the safe choices — see the Metadata System guide.
3

Add the SIM item

Create an item with the name from SimCard.SIMItem (default simcard).
ox_inventory/data/items.lua
stack = false is mandatory. Every SIM carries its own number in metadata — stacking would merge two different SIMs into one item.
4

Restart

ensure roadphone. The roadshop_simcards table is created automatically on boot, and the console prints SIM Card DLC: enabled.

Configuration

Everything lives in lua-code/addons/simcard/config.lua.

Shop NPCs

Each entry in SimCard.NPCs is an independent shop with its own ped, blip and distances. Add as many as you like:
config.lua
Peds spawn and despawn with the player’s distance. With Config.UseTarget = true and ox_target, the shop is registered as a target zone instead of an E prompt.

How numbers are issued

The sim_number column is UNIQUE, and the claim is a single statement — two players buying a SIM in the same tick can never end up with the same number. If handing out the item fails afterwards (inventory full), the reservation is released again. roadshop_simcards is the registry of every number ever issued, independent of who currently owns the card:
isPhoneNumberInUse checks online phones and the users table — it does not know about SIM numbers whose owner is offline. Use isSimNumberInUse when you want to test a number before handing it out.

Data model

A SIM exists in exactly one of two places, and it carries the same three fields in both:
Inserting removes the item and appends the entry; ejecting adds the item back and removes the entry. Both directions write the item first and the metadata second, so a full inventory can never destroy a SIM.
phone_sims, phone_active_sim and phone_battery are deliberately excluded from phone backups. They are hardware state, not user data — restoring them would duplicate SIM cards (back up → eject → restore leaves the same sim_id in the phone and in the inventory).

Converting to an eSIM

A player can convert an inserted SIM into an eSIM. This is one-way: an eSIM is welded to that phone and can no longer be ejected — it only leaves the phone when the SIM is deleted. Use it for phones that should never lose their number.

Player flow

1

Buy

Walk up to a shop NPC → Buy SIM Card → pays SimCard.SIMPrice from SimCard.PaymentMethod → a SIM item with a fresh number and starting_credits lands in the inventory.
2

Insert

Phone → Settings → SIM CardsInsert SIM lists every SIM item in the inventory. Picking one moves it into the phone.
3

Use

The active SIM’s number is the player’s number. Switching the active SIM changes the number immediately — while the phone is open, without reopening it.
4

Top up

At the NPC, Top up X credits charges the active SIM. The balance updates live in the phone UI.
5

Eject

Ejecting hands the SIM back as an item with its current balance intact. eSIMs cannot be ejected.

Credits and billing

Calls. Billing starts when the callee accepts, not when the phone rings. A full minute is deducted every 60 seconds; on hangup the started partial minute is billed pro rata. The meter stops no matter which side hangs up or disconnects. When the balance runs out mid-call, the call is ended for both parties and the caller gets a notification. Emergency numbers (anything registered as a dispatch number) require an inserted SIM but no credits, and are never billed. Messages. The credit check runs after every other validation, so a message that would be dropped anyway never costs anything. Without enough credits the message is not sent and the player is notified.

Admin

Hands a SIM with a specific number to a player. Requires the ace permission command.givesim (console is always allowed). The number must be digits only, must not be claimed already, and must not fall inside a number provider’s reserved space.
server.cfg
For everything scripted, use the SIM Card exports instead — they cover issuing, revoking, inserting, ejecting, switching and credits.

Integration with number providers

Resources that own a number range (phone boxes, Trap Phone, custom hotlines) register as a number provider and outrank SIMs in GetPlayerFromPhone. A SIM inside a reserved range would silently never receive calls, so both the NPC shop and /givesim refuse such numbers up front. The same check runs for every number issued through the exports.

Events

On phone open, the SIM list travels inside the applyPhoneMetadata payload (sims, activeSim, simCardDLC, simCostCall, simCostSMS) — no separate round trip. The UI-facing callbacks are listed under Callbacks.

Troubleshooting

No SIM inserted, or the inserted SIM has no credits. Check with exports['roadphone']:getSimCards(source) — an empty inserted list means the phone has no number at all.
Only the active SIM is reachable. A second SIM sitting in the same phone does not ring — that is intentional. Verify the number with getActiveSimNumber(source).
The item is defined with stack = true. Set stack = false and re-issue the affected SIMs — stacked SIMs share one metadata blob and lose their individual numbers.
The number is present in roadshop_simcards, even if nobody carries that card any more. Pick another number, or delete the stale row.
The number falls inside a number provider’s reserved range. Providers are asked first in GetPlayerFromPhone. Issue a number outside that range.
Switching rebuilds the number cache server-side and pushes the new number to the client. If a resource cached the number itself, refresh it on roadphone:simcard:creditsUpdate / roadphone:simcard:sync, or read it fresh with getActiveSimNumber.

SIM Card Exports

Issue, revoke, move and top up SIM cards from any resource

Metadata System

Item-based phone data — the foundation the SIM system builds on