Add 7 tutorials (7 files)
Browse Source
7 changed files
with 85 additions and 0 deletions
A
domain-name-for-server/en.md
+12 -0
A
fix-docker-hlds-exits-immediately/en.md
+23 -0
A
fix-meta-list-no-amxx/en.md
+10 -0
A
fix-reapi-native-not-found/en.md
+9 -0
A
fix-steam-id-pending-stuck/en.md
+10 -0
A
popular-mods-by-region/en.md
+9 -0
A
read-amxx-error-log/en.md
+12 -0
domain-name-for-server/en.md
A
| @@ -0,0 +1,12 @@ | ||
| 1 | +--- | |
| 2 | +title: "Using a Domain Name for Your Server (DNS + connect)" | |
| 3 | +excerpt: "How to point a domain at your CS 1.6 server so players connect by name instead of a raw IP, why GoldSrc resolves hostnames, the A-record setup, and the non-default-port caveat." | |
| 4 | +category: "hosting" | |
| 5 | +tags: "dns, hosting" | |
| 6 | +image: "" | |
| 7 | +read_time: "8 min read" | |
| 8 | +--- | |
| 9 | +<p>Handing players a raw <code>203.0.113.10:27015</code> is fragile: it is hard to remember, hard to type, and if your server ever changes IP every advert and forum post is suddenly dead. A domain fixes both problems — players connect to <code>play.yourserver.net</code>, and if the IP changes you update one DNS record instead of chasing every listing. GoldSrc resolves hostnames in the <code>connect</code> command, so this works with no client-side setup. Here is how to wire it up.</p><h2>1. Point an A record at your server IP</h2><p>In your domain registrar or DNS provider, create an <strong>A record</strong> mapping a subdomain to your server's public IPv4 address:</p><pre>Type: A | |
| 10 | +Name: play (becomes play.yourserver.net) | |
| 11 | +Value: 203.0.113.10 | |
| 12 | +TTL: 3600</pre><p>Use a subdomain like <code>play</code> or <code>cs</code> rather than the bare domain so you keep the root free for a website. Keep the TTL moderate (an hour); if you plan to move servers soon, drop it to 300 seconds a day ahead so the change propagates fast when you flip it.</p><h2>2. Connect by name from the console</h2><p>Once DNS has propagated, the game resolves the name exactly like an IP. In the client console:</p><pre>connect play.yourserver.net</pre><p>The engine looks up the A record, gets <code>203.0.113.10</code>, and connects to the default port 27015. This is the same <code>connect</code> flow as <a href="/tutorials/cs16-connect-via-console">connecting via console</a> — the only difference is a name in place of the number.</p><h2>3. Handle a non-default port</h2><p>Here is the trap. DNS resolves names to IP addresses only — it carries no port. If your server runs on anything other than 27015, players must still append the port after the resolved name:</p><pre>connect play.yourserver.net:27016</pre><p>A plain A record cannot hide a non-default port. If you run several servers on one box (see <a href="/tutorials/multiple-cs16-servers-one-vps">multiple servers on one VPS</a>), give each a memorable subdomain pointing at the same IP, and publish each with its port. Do not expect an SRV record to help here; the GoldSrc client does not consult SRV records for game connections, so the port must be in the connect string.</p><h2>4. Keep the hostname and domain consistent</h2><p>Set the in-game <code>hostname</code> and advertise the domain together so players associate the two:</p><pre>hostname "YourServer.net | Classic CS 1.6"</pre><p>Put the connect domain in your <a href="/tutorials/motd-txt-html-guide">MOTD</a>, GameTracker page, and Discord. When everything points at <code>play.yourserver.net</code>, a future IP move is a one-record edit rather than a re-advertising campaign.</p><h2>5. Home hosting with a changing IP</h2><p>If you host from a home connection, your ISP may hand you a dynamic IP that changes on reconnect — a static A record goes stale every time it moves. The fix is dynamic DNS: a small updater client on the host machine (or built into your router) watches the public IP and rewrites the A record automatically whenever it changes, so <code>play.yourserver.net</code> always tracks the current address. Many DNS providers support this on a subdomain. Home hosting has other trade-offs — upload bandwidth, NAT port-forwarding, and no DDoS filtering — but for a small server among friends, dynamic DNS keeps a memorable name working through IP churn without manual edits.</p><h2>6. If you use DDoS protection or a proxy</h2><p>Filtered hosting often gives you a protected IP or hostname that fronts your real server. In that case the A record should point at the <strong>protected</strong> address, not your origin, or you defeat the filtering. See <a href="/tutorials/ddos-protected-hosting">DDoS-protected hosting</a> for how the proxy address relates to your origin; the domain always advertises the front, never the box behind it.</p><h2>Common errors</h2><ul><li><strong><code>connect</code> hangs then fails on the name but works on the IP</strong> — DNS has not propagated yet, or the A record has a typo. Wait for the TTL, then re-check the record value.</li><li><strong>Name resolves but connection is refused</strong> — the port is wrong or blocked. A non-default port must be in the connect string, and the firewall must pass that UDP port; see <a href="/tutorials/cs16-server-firewall-ports">server firewall ports</a>.</li><li><strong>Works for you, not for others</strong> — you have a local hosts-file entry or cached DNS; others hit the real record. Verify the public record with a DNS lookup, not your own machine.</li><li><strong>Points at the wrong server after a move</strong> — you updated the server but not the A record. The domain is the source of truth; update it whenever the IP changes.</li><li><strong>Tried an SRV record for the port</strong> — GoldSrc ignores SRV for game connects. Publish the port explicitly.</li></ul><h2>Verification</h2><p>From a machine that is not yours, resolve the name and confirm it returns your server IP, then run <code>connect play.yourserver.net</code> (with the port if non-default) and confirm you land on the server. Change the in-game map and confirm the domain still connects — it should, since nothing about the map affects DNS. Finally, simulate a move on paper: if you had to change IP, confirm you know exactly which one A record to edit. If that record is the only thing you would touch, the domain is doing its job.</p> |
fix-docker-hlds-exits-immediately/en.md
A
| @@ -0,0 +1,23 @@ | ||
| 1 | +--- | |
| 2 | +title: "Fix: HLDS Docker Container Exits Immediately" | |
| 3 | +excerpt: "Why a containerized HLDS/ReHLDS server starts and stops within a second, the 32-bit library requirement, missing +map and console flags, tty/foreground issues, and reading the real crash from logs." | |
| 4 | +category: "errors" | |
| 5 | +tags: "docker, crash" | |
| 6 | +image: "" | |
| 7 | +read_time: "8 min read" | |
| 8 | +--- | |
| 9 | +<p>You run <code>docker compose up</code> for your CS 1.6 server and the container immediately exits — <code>docker ps</code> shows nothing, <code>docker ps -a</code> shows it <code>Exited (1)</code> or <code>Exited (0)</code> seconds after start. A container lives only as long as its foreground process, so an HLDS container that dies instantly means <code>hlds_run</code> either crashed on startup or ran, printed, and returned. Here is how to find which and fix it.</p><h2>1. Read the actual exit reason first</h2><p>Do not guess — get the container's own output:</p><pre>docker compose logs --tail=50 hlds | |
| 10 | +docker ps -a</pre><p>The last lines before exit are the real error. An <code>Exited (0)</code> usually means the process ran to completion (missing map, backgrounded); a non-zero code means a crash or a failed exec. Everything below is about interpreting those lines.</p><h2>2. Provide the 32-bit runtime</h2><p>HLDS and ReHLDS binaries are <strong>32-bit</strong>. On a 64-bit base image without i386 libraries, the loader cannot even start the binary and the container dies instantly with something like:</p><pre>./hlds_linux: No such file or directory</pre><p>— a misleading message that actually means the 32-bit loader is missing. In your Dockerfile (Debian/Ubuntu base) enable i386 and install the libs:</p><pre>RUN dpkg --add-architecture i386 \ | |
| 11 | + && apt-get update \ | |
| 12 | + && apt-get install -y libc6:i386 libstdc++6:i386 lib32gcc-s1 | |
| 13 | +</pre><p>If it then complains specifically about <code>libstdc++.so.6</code> or <code>libm.so.6</code>, see <a href="/tutorials/fix-libstdcpp-so-6-not-found">the shared-library fix</a> — same root cause, missing 32-bit dependency.</p><h2>3. Run in the foreground, not the launcher's background mode</h2><p>The default <code>hlds_run</code> wrapper monitors and restarts the real binary, which can confuse Docker's single-process model. Point the container's command at a start that stays in the foreground and does not detach. A working compose command line is explicit:</p><pre>command: >- | |
| 14 | + ./hlds_run -game cstrike -console -nomaster | |
| 15 | + +ip 0.0.0.0 +port 27015 | |
| 16 | + +map de_dust2 +maxplayers 20 | |
| 17 | +</pre><p>Two flags matter for containers: <code>-console</code> keeps output on stdout (so <code>docker logs</code> sees it and the process does not expect a tty), and a valid <code>+map</code> is mandatory. Without <code>+map</code>, HLDS prints <code>Host_Error: Couldn't spawn server</code> or exits cleanly having nothing to run.</p><h2>4. Give it a real map and maxplayers</h2><p>A container that exits <code>0</code> with no crash almost always lacks a startup map, or was told to run a launcher that forks and returns. The engine needs <code>+map <name></code> pointing at a BSP that exists in <code>cstrike/maps/</code>, plus a sane <code>+maxplayers</code>. Confirm the map is actually in the image:</p><pre>docker compose run --rm hlds ls cstrike/maps/de_dust2.bsp</pre><p>If that path is missing, your volume mount or COPY left the game files out — the binary starts, finds no map, and returns.</p><h2>5. Keep it attached with tty when needed</h2><p>Some builds of the launcher expect an interactive terminal and exit when they do not get one. In compose, grant a pseudo-TTY and keep stdin open:</p><pre>tty: true | |
| 18 | +stdin_open: true | |
| 19 | +restart: unless-stopped</pre><p><code>restart: unless-stopped</code> also stops a transient failure from leaving the container dead, but do not use it to paper over a real crash — fix the underlying error first or you get a container that restart-loops forever.</p><p>A useful debugging trick is to bypass the entrypoint entirely and get a shell inside the image, so you can run the binary by hand and read its raw output:</p><pre>docker compose run --rm --entrypoint bash hlds | |
| 20 | +# then, inside: | |
| 21 | +./hlds_run -game cstrike -console +map de_dust2 +maxplayers 20</pre><p>Running it interactively shows the crash line the daemon would otherwise swallow, which is far faster than reading exit codes and guessing.</p><h2>Common errors</h2><ul><li><strong><code>./hlds_linux: No such file or directory</code></strong> on a file that exists — missing 32-bit loader. Install <code>libc6:i386</code> and friends.</li><li><strong>Exited (0) immediately</strong> — no <code>+map</code>, or the map BSP is not in the image. Add a valid startup map.</li><li><strong><code>Host_Error: Couldn't spawn server</code></strong> — bad or missing map name.</li><li><strong>Container runs but nothing in logs</strong> — missing <code>-console</code>; output is going to a tty the daemon cannot read.</li><li><strong>Restart-loops forever</strong> — <code>restart: unless-stopped</code> masking a startup crash. Read <code>docker compose logs</code> and fix the real error.</li></ul><h2>Verification</h2><p>Start it and confirm the process stays up:</p><pre>docker compose up -d hlds | |
| 22 | +docker ps | |
| 23 | +docker compose logs --tail=20 hlds</pre><p><code>docker ps</code> should list the container as <code>Up</code>, and the logs should end with the server reaching a map and printing its ready banner rather than a <code>Host_Error</code>. Query the port from the host to confirm it answers, then connect a client. If it stays up but no one can join, that is a networking/port-forward issue, not a startup crash — a different problem from the one this fixes.</p> |
fix-meta-list-no-amxx/en.md
A
| @@ -0,0 +1,10 @@ | ||
| 1 | +--- | |
| 2 | +title: "Fix: 'meta list' Shows Metamod but Not AMX Mod X" | |
| 3 | +excerpt: "Metamod loads but AMX Mod X is absent from meta list. How the addons/metamod/plugins.ini line works, the Linux vs Windows and i386 filename traps, and reading the load status." | |
| 4 | +category: "errors" | |
| 5 | +tags: "metamod, amxmodx" | |
| 6 | +image: "" | |
| 7 | +read_time: "7 min read" | |
| 8 | +--- | |
| 9 | +<p>Metamod itself is working — <code>meta version</code> answers and <code>meta list</code> prints something — but AMX Mod X is not in that list, so none of your admin commands, stats, or plugins exist. This is almost always a registration problem: AMX Mod X is itself a Metamod plugin, and Metamod only loads it if it is correctly listed in Metamod's own plugin file with a valid path to the right binary. Metamod loading does not imply AMXX loading; they are two separate steps.</p><h2>1. Confirm what meta list actually shows</h2><p>From the server console:</p><pre>meta list</pre><p>Every Metamod plugin appears here with a status. AMX Mod X should show as a running plugin. If it is missing entirely, Metamod never tried to load it — the line is absent or wrong. If it appears but with a bad status, the file was found but failed to load (wrong architecture, corrupt, or missing dependency). Those are different fixes, so read the status before changing anything.</p><h2>2. Check the Metamod plugins.ini entry</h2><p>AMX Mod X is registered in Metamod's plugin list, not the engine's:</p><pre>addons/metamod/plugins.ini</pre><p>This file must contain a line pointing at the AMXX Metamod binary. The format is a platform tag followed by the path. On Linux:</p><pre>linux addons/amxmodx/dlls/amxmodx_mm_i386.so</pre><p>On Windows:</p><pre>win32 addons\amxmodx\dlls\amxmodx_mm.dll</pre><p>Do not confuse this with <code>addons/amxmodx/configs/plugins.ini</code>, which lists your <code>.amxx</code> scripts. That file is only ever read <em>after</em> AMXX loads. If AMXX is not in <code>meta list</code>, editing the AMXX plugins.ini does nothing — you are one layer too high.</p><h2>3. Fix the filename and platform traps</h2><p>Three things break this line constantly:</p><ul><li><strong>Wrong platform tag</strong> — a <code>win32</code> line on a Linux box (or vice versa) is silently ignored, so AMXX never loads and there is no error. Match the tag to the OS.</li><li><strong>Wrong architecture suffix</strong> — the Linux binary is typically the 32-bit <code>amxmodx_mm_i386.so</code>. If your line points at a name that does not exist on disk, nothing loads. List the <code>addons/amxmodx/dlls/</code> folder and use the exact filename that is there.</li><li><strong>Wrong relative path</strong> — the path is relative to the mod folder (<code>cstrike/</code>). A leading slash or an extra <code>cstrike/</code> breaks it.</li></ul><h2>4. Confirm Metamod is loaded via the game DLL, not the reverse</h2><p>Because <code>meta list</code> works, Metamod is loaded correctly (through <code>liblist.gam</code> or the plus-set gamedll override). That rules out the Metamod-side install and points the finger squarely at the AMXX line. If instead <code>meta version</code> failed too, you would have a Metamod problem, not an AMXX one — see the broader <a href="/tutorials/install-metamod-amxmodx">Metamod and AMX Mod X install guide</a> for that case.</p><h2>5. Match the AMXX build to the engine</h2><p>The classic GoldSrc engine is 32-bit, so the AMXX Metamod binary must be the 32-bit build. Mixing a 64-bit <code>.so</code> against a 32-bit engine gives a bad load rather than a missing entry, and the reverse (a 32-bit binary the OS cannot load because the 32-bit runtime libraries are absent on a 64-bit-only host) fails the same way. If <code>meta list</code> shows AMXX with a bad status rather than as running, this architecture mismatch is the usual reason — re-copy the AMXX release that matches your engine, and on a 64-bit Linux host make sure the i386 C runtime is installed so a 32-bit <code>.so</code> can load at all. The Windows build has no such split, but the <code>win32</code> tag must still match the OS.</p><h2>Troubleshooting</h2><ul><li><strong>AMXX absent from <code>meta list</code></strong> — the line in <code>addons/metamod/plugins.ini</code> is missing, commented, or has the wrong platform tag.</li><li><strong>Line present but status is bad load</strong> — the binary exists but is the wrong architecture (a 64-bit <code>.so</code> against a 32-bit engine) or corrupt. Re-copy the matching build.</li><li><strong>Edited the wrong plugins.ini</strong> — you added your scripts to <code>addons/amxmodx/configs/plugins.ini</code>, but AMXX is registered in <code>addons/metamod/plugins.ini</code>. Fix the Metamod one first.</li><li><strong>Nothing changed after editing</strong> — the file is only read at map load. Change the map or restart the server.</li><li><strong><code>amxx version</code> is an unknown command</strong> — AMXX genuinely did not load, confirming the <code>meta list</code> finding; keep working the Metamod line, not the AMXX config.</li></ul><h2>Verification</h2><p>Change the map so both plugin files are re-read, then run <code>meta list</code> again — AMX Mod X should now appear as running. Confirm from the other side with:</p><pre>amxx version | |
| 10 | +amxx plugins</pre><p><code>amxx version</code> should print the AMXX build, and <code>amxx plugins</code> should list your scripts loading. Once AMXX shows in <code>meta list</code> and answers <code>amxx version</code>, the two-layer chain — engine loads Metamod, Metamod loads AMXX, AMXX loads your plugins — is intact, and any remaining issue is with an individual script, which is a different problem covered in <a href="/tutorials/fix-amxx-module-failed-to-load">module failed to load</a>.</p> |
fix-reapi-native-not-found/en.md
A
| @@ -0,0 +1,9 @@ | ||
| 1 | +--- | |
| 2 | +title: "Fix: 'Native not found' After Installing ReAPI" | |
| 3 | +excerpt: "Why a plugin using ReAPI reports a missing native at load, how the reapi module, ReHLDS and ReGameDLL must line up, the modules.ini and version traps, and how to confirm the hook chain is available." | |
| 4 | +category: "errors" | |
| 5 | +tags: "reapi, natives" | |
| 6 | +image: "" | |
| 7 | +read_time: "8 min read" | |
| 8 | +--- | |
| 9 | +<p>You installed a plugin that uses ReAPI, and on map load AMX Mod X throws <code>Native not found</code> or the plugin shows as <code>bad load</code> in <code>amxx plugins</code>. ReAPI is not self-contained — it is a thin bridge that exposes ReHLDS and ReGameDLL hooks to Pawn, so a missing native almost always means a piece of that stack is absent or the wrong version, not that the plugin is broken. Here is how the layers fit and how to fix the gap.</p><h2>1. Understand what ReAPI actually needs</h2><p>ReAPI provides natives like <code>RegisterHookChain</code>, <code>set_member</code>, and <code>rg_*</code> functions. Those natives only exist if three things are present and compatible: <strong>ReHLDS</strong> (the engine), <strong>ReGameDLL_CS</strong> (the game logic that defines the hook chains), and the <strong>reapi</strong> AMX Mod X module that binds them. Remove or mismatch any one and the natives the plugin calls do not register, so AMXX reports them as not found. This is why the spec rule is firm: a plugin that <code>#include <reapi></code> requires ReGameDLL, full stop.</p><h2>2. Confirm the reapi module is loaded</h2><p>The module must be listed and running. Check <code>addons/amxmodx/configs/modules.ini</code> contains the line:</p><pre>reapi</pre><p>Then verify at runtime from the server console:</p><pre>amxx modules</pre><p>The <code>reapi</code> module must read <code>running</code>. If it is missing from the list, the <code>reapi_amxx.so</code> (or <code>.dll</code>) file is not in <code>addons/amxmodx/modules/</code>; if it is listed but <code>bad load</code>, it failed to bind to the game — which points at the ReGameDLL layer below.</p><h2>3. Verify ReGameDLL is actually the running game DLL</h2><p>ReAPI's game-side natives come from ReGameDLL_CS. If your server still runs the stock <code>cs.so</code> / <code>mp.dll</code>, the reapi module loads but the <code>rg_*</code> natives never appear. Confirm ReGameDLL is installed as covered in <a href="/tutorials/install-regamedll-cs16">the ReGameDLL install guide</a> — the game library in <code>cstrike/dlls/</code> must be the ReGameDLL build, and the server console at startup prints the ReGameDLL version banner. No banner means you are on stock game logic and every ReAPI game native will be missing.</p><h2>4. Line up the versions</h2><p>ReHLDS, ReGameDLL and the reapi module evolve together. A reapi module newer than your ReGameDLL can reference hook chains the older game DLL does not expose, and the specific native the plugin calls then fails to register. Update all three from the same release window rather than mixing a fresh reapi module onto a two-year-old ReGameDLL. If you cannot update the engine, use a reapi module contemporary with your ReGameDLL. This is the same compatibility discipline described in <a href="/tutorials/fix-regamedll-rehlds-incompatible">the ReGameDLL/ReHLDS incompatibility fix</a>.</p><h2>5. Load order in plugins.ini</h2><p>The reapi module loads from <code>modules.ini</code> before plugins load, so module order is not the issue — but a plugin that depends on another reapi plugin's registered natives can load first and fail. If your failing plugin calls natives that a companion plugin registers with <code>register_native</code>, that provider must appear earlier in <a href="/tutorials/plugins-ini-load-order">plugins.ini load order</a>. For a plugin that only uses ReAPI's own natives, this does not apply and the cause is one of the layers above.</p><h2>Troubleshooting</h2><ul><li><strong><code>Native not found</code> at load, reapi listed as running</strong> — ReGameDLL is not the active game DLL, so the game natives never registered. Install ReGameDLL (step 3).</li><li><strong>reapi shows <code>bad load</code> in <code>amxx modules</code></strong> — the module failed to bind to the game; version mismatch between reapi and ReGameDLL. Align versions (step 4).</li><li><strong>Module file missing entirely</strong> — <code>reapi_amxx.so</code> is not in the modules folder, or <code>reapi</code> is absent from <code>modules.ini</code>. Add both.</li><li><strong>Some rg_ natives work, one specific native does not</strong> — your ReGameDLL is older than the reapi module and lacks that hook. Update ReGameDLL.</li><li><strong>Plugin using a companion's native fails</strong> — provider plugin loads after the consumer. Reorder plugins.ini.</li><li><strong>Runs on ReHLDS box, breaks on another</strong> — the second box runs stock HLDS/game DLL. ReAPI cannot work there.</li></ul><h2>Verification</h2><p>After correcting the stack, run <code>amxx modules</code> and confirm <code>reapi</code> reads <code>running</code>, then <code>amxx plugins</code> and confirm your plugin reads <code>running</code> rather than <code>bad load</code>. Trigger the plugin's actual behaviour in game — a hook chain it registers, such as a spawn or damage modification — and confirm it fires. If the module runs and the plugin loads but a hook still does nothing, the plugin's registration is fine and the ReGameDLL feature it hooks may be disabled in ReGameDLL's own <code>game.cfg</code>. When building your own ReAPI plugin, remember to set <code>reapi_required</code> and <code>regamedll_required</code> so the directory installs the dependencies alongside it — see the <a href="/plugins/csb-admin-menu">CSB Admin Menu</a> for a maintained example that declares them correctly.</p> |
fix-steam-id-pending-stuck/en.md
A
| @@ -0,0 +1,10 @@ | ||
| 1 | +--- | |
| 2 | +title: "Fix: Players Stuck with STEAM_ID_PENDING" | |
| 3 | +excerpt: "Why players show as STEAM_ID_PENDING and never validate, how Steam auth works on a CS 1.6 server, and the sv_lan, connectivity and non-Steam fixes that let them in." | |
| 4 | +category: "errors" | |
| 5 | +tags: "steamid, validation" | |
| 6 | +image: "" | |
| 7 | +read_time: "8 min read" | |
| 8 | +--- | |
| 9 | +<p>A player connects, but in <code>status</code> their SteamID reads <code>STEAM_ID_PENDING</code> and never resolves to a real <code>STEAM_0:...</code>. Depending on your setup that player either plays with no valid identity (so stats, admin flags and bans keyed to SteamID do not apply) or gets dropped. Pending means the server asked Steam to validate the ticket and has not gotten an answer. This explains why that happens and how to clear it.</p><h2>1. Know what PENDING means</h2><p>When a Steam client connects, the server hands the auth ticket to Valve's Steam backend and waits for a verdict. Until the verdict arrives the player is <code>STEAM_ID_PENDING</code>; on success it becomes their real SteamID, on failure they are dropped with a validation error. So <em>persistent</em> pending is almost always the server being unable to reach Steam's auth servers, or the server not being configured to authenticate at all. Confirm the state:</p><pre>status</pre><p>Every player parked on <code>STEAM_ID_PENDING</code> points at a server-side auth problem, not a client one — one player pending is a fluke, everyone pending is your server.</p><h2>2. Check sv_lan</h2><p>The most common cause on a public server is that it is accidentally in LAN mode:</p><pre>sv_lan 0</pre><p>With <code>sv_lan 1</code> the server does not authenticate against Steam at all — every SteamID stays pending or blank, and the server never appears in the master list either. Set it to <code>0</code> in <a href="/tutorials/setup-server-cfg-cs16">server.cfg</a> for any internet-facing server. This one cvar fixes a surprising share of pending reports.</p><h2>3. Confirm the box can reach Steam</h2><p>If <code>sv_lan</code> is correct, the server may be firewalled off from Steam's auth backend. The server needs outbound access to Valve's Steam servers; a locked-down VPS that only opens the game port inbound but blocks outbound to Steam will leave every real Steam client pending. Verify outbound connectivity is not being blocked by the host firewall, and that the box's clock is roughly correct — a badly skewed system clock breaks TLS to Steam and stalls validation. On Linux, check the clock:</p><pre>timedatectl | |
| 10 | +# or: date</pre><p>A clock that is wrong by more than a few minutes will silently break Steam auth.</p><h2>4. For a non-Steam server, install an emulator</h2><p>If you run a <strong>non-Steam</strong> server, real validation against Valve will never complete for non-Steam clients — that is expected, and pending is what you get without an ID emulator. You need Reunion (for ReHLDS) or dproto (for original HLDS) to hand out stable IDs locally instead of waiting on Steam:</p><ul><li><strong>ReHLDS</strong> → Reunion. It requires a <code>SteamIdHashSalt</code> of at least 16 characters in its config or it refuses to load, leaving everyone pending.</li><li><strong>Original HLDS</strong> → dproto.</li></ul><p>With the emulator loaded, non-Steam clients get a deterministic <code>STEAM_0:...</code> derived from their client data, and <code>status</code> shows real IDs. See <a href="/tutorials/find-player-steamid">finding a player's SteamID</a> for how those IDs are formed and why a good salt matters for bans.</p><h2>5. Rule out a Steam-side outage</h2><p>Occasionally the whole thing is Valve's fault: during a Steam auth outage every server everywhere shows connecting players as pending until the backend recovers. If <code>sv_lan</code> is <code>0</code>, outbound connectivity is fine, and it started suddenly across all your servers at once, wait it out — there is no config that fixes a Steam-side outage. Check whether other unrelated servers are affected before you tear your config apart.</p><h2>Troubleshooting</h2><ul><li><strong>Everyone pending, server also not on master list</strong> — <code>sv_lan 1</code>. Set it to <code>0</code>.</li><li><strong>Real Steam clients pending on a VPS</strong> — outbound firewall to Steam, or a skewed system clock. Fix connectivity and time sync.</li><li><strong>Non-Steam clients always pending</strong> — no ID emulator. Install Reunion (ReHLDS) or dproto (HLDS); give Reunion a 16+ character salt.</li><li><strong>Reunion loaded but still pending</strong> — <code>SteamIdHashSalt</code> is missing or under 16 characters, so Reunion refused to load. Check the config and the startup log.</li><li><strong>Started suddenly on every server at once</strong> — likely a Steam auth outage. Confirm elsewhere and wait.</li></ul><h2>Verification</h2><p>Have a player reconnect and run <code>status</code> on the server. Their line should now show a real <code>STEAM_0:...</code> ID rather than <code>STEAM_ID_PENDING</code>. For a Steam server, confirm the ID matches the player's actual Steam account; for a non-Steam server with Reunion, confirm the same player gets the <em>same</em> ID on every reconnect — stable IDs are what make stats and bans work. If IDs resolve correctly, your auth path is healthy and SteamID-keyed features will behave.</p> |
popular-mods-by-region/en.md
A
| @@ -0,0 +1,9 @@ | ||
| 1 | +--- | |
| 2 | +title: "Which CS 1.6 Mods Are Popular in Which Regions" | |
| 3 | +excerpt: "A practical map of which CS 1.6 game modes fill servers where, why non-Steam dominates certain regions, and how to pick a mod that matches the players you can actually reach." | |
| 4 | +category: "hosting" | |
| 5 | +tags: "mods, regions" | |
| 6 | +image: "" | |
| 7 | +read_time: "8 min read" | |
| 8 | +--- | |
| 9 | +<p>CS 1.6 is still alive in 2026, but not evenly and not with the same game modes everywhere. Picking a mod that has players near you matters more than picking the mod you personally like — a brilliant server for a mode nobody in your latency range plays stays empty. This is a grounded look at how CS 1.6 mode popularity clusters by region, and how to use that to choose. Treat it as a starting hypothesis to confirm with real data, not gospel.</p><h2>1. Why region shapes the mode</h2><p>Two forces drive regional differences. The first is <strong>Steam versus non-Steam</strong>: in regions where the game spread heavily through non-Steam copies, the surviving population plays on servers that accept those clients via <a href="/tutorials/install-reunion">Reunion</a> or dproto, and those communities built up around specific mods. The second is <strong>latency</strong>: players fill servers they can reach with a good ping, so a mode thrives wherever a critical mass of servers hosting it sits inside a playable ping radius. Popularity is self-reinforcing — players go where players are, and once a mode reaches critical mass in a region it stays dominant there for years because newcomers keep joining the servers that are already full.</p><h2>2. The broad regional patterns</h2><p>From what fills servers in practice:</p><ul><li><strong>Eastern Europe, Russia, the Balkans</strong> — the deepest CS 1.6 population and the most non-Steam. Public classic 5v5, <a href="/tutorials/install-zombie-plague-mod">Zombie Plague</a>, GunGame, and DeatHRun all have healthy scenes here. This is where a non-Steam public server is easiest to fill.</li><li><strong>Latin America (especially Brazil)</strong> — a large, loyal 1.6 base, heavy on public classic and Zombie modes, and heavily non-Steam. Portuguese-language communities are strong.</li><li><strong>South and Southeast Asia</strong> — big non-Steam populations, strong on classic public, Zombie, and fun modes; internet cafe culture kept 1.6 alive.</li><li><strong>Turkey and the Middle East</strong> — active public and Zombie scenes, largely non-Steam.</li><li><strong>Western Europe and North America</strong> — thinner 1.6 population (many moved to CS:GO/CS2), more Steam-only, with the survivors clustered on a few well-known classic and retake-style servers.</li></ul><h2>3. Which modes travel and which are local</h2><p><strong>Classic public 5v5</strong> works everywhere there are players — it is the safe default. <strong>Zombie modes</strong> are the biggest crowd-puller in EE, LATAM and Asia and are a reliable choice if your region has non-Steam players. <strong>GunGame</strong> and <strong>DeatHRun</strong> fill fast in high-population regions but struggle where the overall base is thin. Niche or complex mods need a dense population to sustain, so they are a poor first choice outside the busiest regions. If you are in a thin region, do not fight it — run classic and accept non-Steam to widen your reachable pool with <a href="/tutorials/make-server-accept-nonsteam">a non-Steam-capable setup</a>.</p><h2>4. Match the mod to who you can reach</h2><p>Your reachable audience is everyone within a good ping of your <a href="/tutorials/choose-vps-for-cs16">VPS</a>. Host in or near the region whose players you want, then pick a mode that region actually plays. A Zombie server on a Frankfurt box aimed at Western Europe fights a thin base; the same server on an Eastern-European box taps a deep one. See <a href="/tutorials/pick-a-mod-with-players">picking a mod with players</a> for the decision framework.</p><h2>Common mistakes</h2><ul><li><strong>Great server, no players</strong> — you picked a mode with no local population. Check what fills nearby servers before committing.</li><li><strong>Steam-only in a non-Steam region</strong> — you locked out most of your reachable base. Accept non-Steam with <a href="/tutorials/install-reunion">Reunion</a> if that is where your players are.</li><li><strong>Hosting far from your target audience</strong> — high ping caps your fillable pool. Put the box near the players.</li><li><strong>Copying a huge server's niche mode</strong> — that mode fills for them because they already have the population; a newcomer needs a mode with broad demand.</li><li><strong>Ignoring language</strong> — a Portuguese or Russian community expects MOTD and adverts in their language; an English-only server feels foreign.</li></ul><h2>Verification</h2><p>Do not trust this article — measure. Open a live server browser or a public tracker, filter by the region you can host in, sort by players, and note which <em>modes</em> the full servers are running. That list is your regional demand map, current as of today. Pick the mode that appears full and is not already saturated by an untouchable giant, host near those players, and accept the client type they use. If full servers in your ping range are mostly Zombie and classic, those are your two safe bets — not whatever mode you saw a highlight video about.</p> |
read-amxx-error-log/en.md
A
| @@ -0,0 +1,12 @@ | ||
| 1 | +--- | |
| 2 | +title: "Reading AMXX error_*.log: What Each Error Actually Means" | |
| 3 | +excerpt: "Where AMX Mod X writes its logs, how to read a run-time error line, and what the common messages -- run time error 4, native error, invalid plugin, failed to load -- actually tell you." | |
| 4 | +category: "errors" | |
| 5 | +tags: "amxmodx, logs" | |
| 6 | +image: "" | |
| 7 | +read_time: "8 min read" | |
| 8 | +--- | |
| 9 | +<p>When a plugin misbehaves, AMX Mod X does not stay silent — it writes the reason to a dated log file. Learning to read that file turns "a plugin is broken" into "line 214 of this plugin divided by zero." This walks through where the logs live, how a single log line is structured, and what each of the recurring error types actually means so you can act on it instead of guessing.</p><h2>1. Find the log files</h2><p>AMX Mod X keeps its own logs, separate from the HLDS logs, in one directory:</p><pre>cstrike/addons/amxmodx/logs/</pre><p>Inside you will find files named by date, <code>error_20260715.log</code> and a matching <code>L20260715.log</code>. The <code>error_*</code> file is the one you want — it collects plugin load failures and run-time errors. Open the file whose date matches when the problem happened; a fresh error is always at the bottom.</p><h2>2. Read the anatomy of an error line</h2><p>A run-time error is a small block, not one line. A typical entry looks like this:</p><pre>L 07/15/2026 - 21:04:11: Run time error 4 (plugin "myplugin.amxx") (native "get_user_name") - debug not enabled! | |
| 10 | +L 07/15/2026 - 21:04:11: [AMXX] Displaying debug trace (plugin "myplugin.amxx") | |
| 11 | +L 07/15/2026 - 21:04:11: [AMXX] Run time error 4: index out of bounds | |
| 12 | +L 07/15/2026 - 21:04:11: [AMXX] [0] myplugin.sma::show_name (line 88)</pre><p>Read it bottom-up: the last <code>[0]</code> line is the exact <strong>source file, function, and line number</strong> where it broke. The <code>Run time error 4</code> tells you the <em>kind</em> of fault, and the plugin name tells you which file to open. That is everything you need — the file, the function, the line, and the class of error.</p><h2>3. Turn on the debug trace</h2><p>If the log says <code>debug not enabled!</code> you only get the error number, not the line. Enable line numbers by loading the plugin in debug mode — add the <code>debug</code> flag after its name in <code>plugins.ini</code>:</p><pre>myplugin.amxx debug</pre><p>Change the map and reproduce the error; now the trace includes the <code>::function (line N)</code> detail shown above. Leave debug on only while investigating — it is slightly slower — then remove the flag. This is covered in more depth in <a href="/tutorials/debug-amxx-plugins">debugging AMXX plugins</a>.</p><h2>4. What the common messages mean</h2><table><thead><tr><th>Log message</th><th>What it means</th></tr></thead><tbody><tr><td><code>Run time error 4: index out of bounds</code></td><td>The plugin read past the end of an array — often a player index of 0 or an unchecked loop. See <a href="/tutorials/fix-amxx-run-time-error-4">run time error 4</a>.</td></tr><tr><td><code>Run time error 10: native error</code></td><td>A native was called with a bad argument, or a required native does not exist — frequently a sub-plugin loading before its core.</td></tr><tr><td><code>failed to load: Module/Library "x" required</code></td><td>A module the plugin needs is not enabled in <code>modules.ini</code>. Enable it.</td></tr><tr><td><code>Plugin "x.amxx" is not a valid AMX Mod X plugin</code></td><td>The file is corrupt, truncated, or compiled for a different AMXX branch. Recompile it.</td></tr><tr><td><code>Function "y" was not found</code></td><td>A forward or a bind points at a public function name that does not exist in the source. A typo in the register call.</td></tr></tbody></table><h2>5. Tie the error to a plugin and act</h2><p>Once the line names a plugin, list your plugins to confirm its state:</p><pre>amxx plugins</pre><p>A plugin repeatedly throwing errors often shows as <code>debug</code> or, if it failed at load, <code>bad load</code>. If a single plugin is spamming the log every round, pause it immediately from the console so the server stops throwing while you fix the file:</p><pre>amxx pause myplugin.amxx</pre><p>If you cannot even tell which plugin crashes the whole server, the log narrows it down; the systematic hunt is in <a href="/tutorials/find-crashing-plugin">finding a crashing plugin</a>.</p><h2>Common errors</h2><ul><li><strong>Log shows an error number but no line</strong> — debug is off. Add the <code>debug</code> flag in <code>plugins.ini</code> and reproduce.</li><li><strong>Log directory is empty</strong> — the server has no write permission to <code>addons/amxmodx/logs/</code>. Fix directory ownership; nothing is being recorded.</li><li><strong>'not a valid AMX Mod X plugin'</strong> — the <code>.amxx</code> is corrupt or wrong-branch. Recompile from source with <a href="/tutorials/compile-sma-to-amxx">the matching compiler</a>.</li><li><strong>Same error repeats hundreds of times</strong> — it fires every event; the log grows fast. Pause the plugin, fix the line the trace names, reload.</li><li><strong>Error names a native you did not call</strong> — it is inside a plugin you loaded, not yours. The plugin name in the line is authoritative.</li></ul><h2>Verification</h2><p>After a fix, clear your view by noting the current bottom of the log, reproduce the action that used to error, and confirm no new line is appended. Then restart or change map and check <code>amxx plugins</code> shows the plugin <code>running</code> rather than <code>debug</code> or <code>bad load</code>. A quiet <code>error_*.log</code> after a full map is the signal that the plugin set is healthy — keep an eye on it, because a growing error log is the earliest warning that something regressed.</p> | |
| 0 | 13 |









