Skip to content

Storing Game Data

Xyranaut edited this page Jun 1, 2026 · 2 revisions

Storing game data (the right column types)

Beginners often store the wrong type for game data — text where a number belongs, the wrong precision for coordinates, etc. This page shows how to store the common things an open.mp gamemode tracks.

Quick reference

Game thing MySQL column Why
money, score, ammo INT whole numbers
big counters (xp totals) BIGINT won't overflow at billions
position x/y/z, angle FLOAT decimals; FLOAT is plenty for SA coords
health, armour FLOAT 0.0–100.0
skin, interior, world, weapon id INT (often SMALLINT) small whole numbers
color INT UNSIGNED RGBA packed into one number
is_banned, is_vip TINYINT (0/1) booleans
player name VARCHAR(24) open.mp names ≤ 24 chars
IP address VARCHAR(45) fits IPv4 and IPv6
password hash VARCHAR(255) Argon2 hashes ~100 chars
a description / note TEXT long, variable text
created / last_login DATETIME or TIMESTAMP a moment in time

Positions (coordinates)

Store each component as a FLOAT:

x FLOAT, y FLOAT, z FLOAT, angle FLOAT
new Float:x, Float:y, Float:z, Float:a;
GetPlayerPos(playerid, x, y, z);
GetPlayerFacingAngle(playerid, a);
// bind with mysql_stmt_set_float(st, i, x); ...

Read back with mysql_rs_get_float_by(0, "x", x). FLOAT is accurate enough for San Andreas; use DOUBLE only if you truly need more precision.

Colors

open.mp colors are a single 32-bit RGBA value. Store as one integer:

color INT UNSIGNED
new color = GetPlayerColor(playerid);          // e.g. 0xRRGGBBAA
mysql_stmt_set_int(st, i, color);
// SetPlayerColor(playerid, savedColor);

Use INT UNSIGNED so the high bit doesn't make it negative.

Booleans (yes/no flags)

MySQL has no real boolean — use TINYINT with 0/1:

banned TINYINT NOT NULL DEFAULT 0,
vip    TINYINT NOT NULL DEFAULT 0
mysql_stmt_set_int(st, i, isBanned ? 1 : 0);
// reading:
new tmp; mysql_rs_get_int_by(0, "banned", tmp);
new bool:banned = (tmp != 0);

IP addresses

Store as text — VARCHAR(45) covers IPv6 too:

new ip[45];
GetPlayerIp(playerid, ip, sizeof ip);
mysql_stmt_set_string(st, i, ip);

Dates & times

Let MySQL stamp the time so it's consistent regardless of the server's clock code:

created    DATETIME DEFAULT CURRENT_TIMESTAMP,
last_login DATETIME
-- update last_login on each login:
UPDATE accounts SET last_login = NOW() WHERE id = ?;

Read a datetime back as a string ("2025-01-01 12:00:00"); see Date & time.

Weapons / inventory (many per player)

Don't make 13 weapon columns on accounts. Use a child table (one row per item):

CREATE TABLE account_weapons (
    account_id INT NOT NULL,
    slot       TINYINT NOT NULL,
    weapon     INT NOT NULL,
    ammo       INT NOT NULL,
    PRIMARY KEY (account_id, slot)
) CHARACTER SET utf8mb4;

This is a one-to-many relationship — see Designing your tables. The mysql-admin demo uses exactly this pattern.

Text & names (use utf8mb4!)

Always create tables with CHARACTER SET utf8mb4 so names/text in any language store without being corrupted or rejected by the database. Size VARCHAR to the real maximum (VARCHAR(24) for names).

Note: utf8mb4 is about safe storage, not display. SA:MP / open.mp chat uses the GTA SA bitmap font, which is single-byte (Windows-1252-style) — it cannot render emoji or most non-Latin characters in-game. So a name might store fine in MySQL but show as boxes/garbage in chat. Still use utf8mb4 (it never hurts and avoids data corruption); just don't expect emoji to appear in the game.

Don't

  • ❌ Store numbers as VARCHAR ("500") — you can't SUM/ORDER BY them properly.
  • ❌ Store a position as one "x,y,z" string — you can't query/compare it.
  • ❌ Use utf8 (without mb4) — it can't store all characters (4-byte ones break it).
  • ❌ Store booleans as "true"/"false" text — use TINYINT 0/1.

Next: Designing your tables · Date & time

Clone this wiki locally