Add install-sqlite-module-amxx/en.md
Browse Source
1 changed file
with 125 additions and 0 deletions
install-sqlite-module-amxx/en.md
A
| @@ -0,0 +1,125 @@ | ||
| 1 | +--- | |
| 2 | +title: "How to Use the SQLite Module in AMX Mod X" | |
| 3 | +excerpt: "Enable the SQLite module in AMX Mod X, where the .sq3 file lives, when SQLite beats MySQL, and how to avoid the locking problems." | |
| 4 | +category: "installation" | |
| 5 | +tags: "amxmodx, sqlite" | |
| 6 | +image: "" | |
| 7 | +read_time: "8 min read" | |
| 8 | +--- | |
| 9 | +<p>The SQLite module gives AMX Mod X a real SQL database with no server, no credentials, and no network. It uses the same <code>sqlx</code> API as MySQL, so the same plugin code works against either backend — only the connection tuple changes. For a single game server that needs to persist points, ranks, shop purchases or settings, SQLite is often the correct choice and MySQL is over-engineering.</p> | |
| 10 | + | |
| 11 | +<h2>When SQLite is the right call</h2> | |
| 12 | +<table> | |
| 13 | +<thead><tr><th>Situation</th><th>Use</th></tr></thead> | |
| 14 | +<tbody> | |
| 15 | +<tr><td>One server, data local to it</td><td><strong>SQLite</strong></td></tr> | |
| 16 | +<tr><td>Several servers sharing ranks/bans</td><td>MySQL</td></tr> | |
| 17 | +<tr><td>A web panel reads the data live</td><td>MySQL</td></tr> | |
| 18 | +<tr><td>You want zero moving parts</td><td><strong>SQLite</strong></td></tr> | |
| 19 | +<tr><td>Heavy concurrent writes from many processes</td><td>MySQL</td></tr> | |
| 20 | +</tbody> | |
| 21 | +</table> | |
| 22 | +<p>SQLite is also a strictly better choice than <code>nvault</code> for anything structured. nVault is a flat key/value store with no queries and a well-earned reputation for corruption; see <a href="/tutorials/fix-nvault-corrupted">nVault corruption</a>.</p> | |
| 23 | + | |
| 24 | +<h2>1. Enable the module</h2> | |
| 25 | +<p><code>addons/amxmodx/configs/modules.ini</code>:</p> | |
| 26 | +<pre>fun | |
| 27 | +engine | |
| 28 | +fakemeta | |
| 29 | +cstrike | |
| 30 | +csx | |
| 31 | +hamsandwich | |
| 32 | +sqlite</pre> | |
| 33 | +<p>Check the file is there and is 32-bit:</p> | |
| 34 | +<pre>ls -la /home/steam/hlds/cstrike/addons/amxmodx/modules/sqlite_amxx_i386.so | |
| 35 | +file /home/steam/hlds/cstrike/addons/amxmodx/modules/sqlite_amxx_i386.so</pre> | |
| 36 | +<p>You do <strong>not</strong> need to install SQLite on the host. The module carries its own engine. Restart the server — modules load at startup, not on map change.</p> | |
| 37 | + | |
| 38 | +<h2>2. Where the database file lives</h2> | |
| 39 | +<p>The SQLite module puts databases under:</p> | |
| 40 | +<pre>cstrike/addons/amxmodx/data/sqlite/</pre> | |
| 41 | +<p>The directory must exist and must be writable by the user running HLDS. If it is not, every query fails and — depending on the plugin — you get silence, not an error.</p> | |
| 42 | +<pre>mkdir -p /home/steam/hlds/cstrike/addons/amxmodx/data/sqlite | |
| 43 | +chown -R steam:steam /home/steam/hlds/cstrike/addons/amxmodx/data | |
| 44 | +chmod 755 /home/steam/hlds/cstrike/addons/amxmodx/data/sqlite</pre> | |
| 45 | + | |
| 46 | +<h2>3. Connect from a plugin</h2> | |
| 47 | +<p>Same <code>sqlx</code> API as MySQL. The only difference is the tuple: for SQLite the "database" is the file name and the host/user/pass are ignored.</p> | |
| 48 | +<pre>#include <amxmodx> | |
| 49 | +#include <sqlx> | |
| 50 | + | |
| 51 | +new Handle:g_tuple | |
| 52 | + | |
| 53 | +public plugin_init() | |
| 54 | +{ | |
| 55 | + register_plugin("SQLite Example", "1.0", "you") | |
| 56 | + | |
| 57 | + // For SQLite the 4th argument is the database file (created if absent) | |
| 58 | + g_tuple = SQL_MakeDbTuple("", "", "", "server_stats") | |
| 59 | + | |
| 60 | + SQL_ThreadQuery(g_tuple, "OnTableReady", | |
| 61 | + "CREATE TABLE IF NOT EXISTS points ( \ | |
| 62 | + steamid TEXT PRIMARY KEY, \ | |
| 63 | + name TEXT, \ | |
| 64 | + points INTEGER DEFAULT 0 \ | |
| 65 | + )") | |
| 66 | +} | |
| 67 | + | |
| 68 | +public OnTableReady(failstate, Handle:query, error[], errnum, data[], size, Float:queuetime) | |
| 69 | +{ | |
| 70 | + if (failstate != TQUERY_SUCCESS) | |
| 71 | + log_amx("SQLite error %d: %s", errnum, error) | |
| 72 | +}</pre> | |
| 73 | +<p>Set <code>amx_sql_type</code> to <code>sqlite</code> in <code>configs/sql.cfg</code> if you use a plugin that reads the backend from there:</p> | |
| 74 | +<pre>amx_sql_type "sqlite" | |
| 75 | +amx_sql_db "server_stats"</pre> | |
| 76 | +<p>That produces <code>data/sqlite/server_stats.sq3</code>.</p> | |
| 77 | + | |
| 78 | +<h2>4. Writing rows safely</h2> | |
| 79 | +<p>Always escape values that come from a player. Names contain quotes, semicolons and worse, and SQLite is as injectable as MySQL:</p> | |
| 80 | +<pre>new name[64], escaped[128], steamid[35], q[256] | |
| 81 | +get_user_name(id, name, charsmax(name)) | |
| 82 | +get_user_authid(id, steamid, charsmax(steamid)) | |
| 83 | + | |
| 84 | +SQL_QuoteString(Empty_Handle, escaped, charsmax(escaped), name) | |
| 85 | + | |
| 86 | +formatex(q, charsmax(q), | |
| 87 | + "INSERT OR REPLACE INTO points (steamid, name, points) VALUES ('%s', '%s', %d)", | |
| 88 | + steamid, escaped, points) | |
| 89 | + | |
| 90 | +SQL_ThreadQuery(g_tuple, "OnSaved", q)</pre> | |
| 91 | +<p><code>INSERT OR REPLACE</code> is SQLite's upsert; MySQL uses <code>ON DUPLICATE KEY UPDATE</code>. This is the one place the two backends genuinely differ in a plugin, and it is why "SQLite and MySQL are drop-in interchangeable" is only 95% true.</p> | |
| 92 | + | |
| 93 | +<h2>5. Inspecting the database</h2> | |
| 94 | +<p>Install the <code>sqlite3</code> CLI on the host (this is for <em>you</em>, not for the module):</p> | |
| 95 | +<pre>sudo apt install -y sqlite3 | |
| 96 | +cd /home/steam/hlds/cstrike/addons/amxmodx/data/sqlite | |
| 97 | +sqlite3 server_stats.sq3 | |
| 98 | + | |
| 99 | +sqlite> .tables | |
| 100 | +sqlite> SELECT * FROM points ORDER BY points DESC LIMIT 10; | |
| 101 | +sqlite> .quit</pre> | |
| 102 | +<p>Do not run write queries against the file while the server is running. SQLite locks the whole database on write, and a long external transaction will make the game server's queries fail.</p> | |
| 103 | + | |
| 104 | +<h2>Common errors</h2> | |
| 105 | +<ul> | |
| 106 | +<li><strong><code>database is locked</code></strong> — two writers. Usually you left a <code>sqlite3</code> shell open with an uncommitted transaction, or two game servers point at the same <code>.sq3</code> file. One server, one file.</li> | |
| 107 | +<li><strong><code>unable to open database file</code></strong> — <code>data/sqlite/</code> does not exist, or is not writable by the HLDS user. Check with <code>sudo -u steam touch data/sqlite/test</code>.</li> | |
| 108 | +<li><strong><code>Module failed to load</code></strong> — the <code>.so</code> is missing or is not 32-bit. See <a href="/tutorials/fix-amxx-module-failed-to-load">the module load fix</a>.</li> | |
| 109 | +<li><strong><code>no such table: points</code></strong> — the plugin queried before its <code>CREATE TABLE</code> callback completed. Threaded queries are asynchronous; chain the first read off the create callback rather than firing both from <code>plugin_init()</code>.</li> | |
| 110 | +<li><strong>Data disappears after a restart</strong> — you are writing to a path inside a container without a volume, or the file is being recreated because the plugin's <code>CREATE TABLE</code> is missing <code>IF NOT EXISTS</code> and failing silently.</li> | |
| 111 | +</ul> | |
| 112 | + | |
| 113 | +<h2>Backups</h2> | |
| 114 | +<p>The whole database is one file, which is the best thing about SQLite:</p> | |
| 115 | +<pre>sqlite3 server_stats.sq3 ".backup '/home/steam/backups/stats-$(date +%F).sq3'"</pre> | |
| 116 | +<p>Use <code>.backup</code>, not <code>cp</code> — copying a file that is being written produces a corrupt database. Put that in cron.</p> | |
| 117 | + | |
| 118 | +<h2>Verification</h2> | |
| 119 | +<ol> | |
| 120 | +<li><code>amxx modules</code> lists SQLite as <code>running</code>.</li> | |
| 121 | +<li>The <code>.sq3</code> file appears in <code>data/sqlite/</code> after the first map.</li> | |
| 122 | +<li><code>sqlite3 file.sq3 ".tables"</code> shows your plugin's tables.</li> | |
| 123 | +<li>A value written in-game survives a map change and a full restart.</li> | |
| 124 | +</ol> | |
| 125 | +<p>If you later outgrow one server, the same plugin code moves to <a href="/tutorials/install-mysql-module-amxx">the MySQL module</a> with a different tuple and an upsert rewrite.</p> | |
| 0 | 126 |









