Data

Files

@amxts/core/fs reads and writes files the way Node's fs does:

import * as 
fs
from "@amxts/core/fs";
const
rulesFile
= `${
server
.
configsDir
}/myplugin/rules.txt`;
server
.
addCommand
("/rules", ({
player
}) => showRules(
player
));
function showRules(
player
: Player) {
const
text
=
fs
.
readFileSync
(
rulesFile
);
if (
text
== null) return; // no such file
for (const
line
of
text
.
split
("\n"))
console
.
log
(
line
);
fs
.
appendFileSync
(`${
server
.
dataDir
}/myplugin-rules.log`, `${
player
.
name
} read the rules\n`);
}

Every function has a synchronous form and a promise form, for await:

SynchronousPromiseDoes
readFileSync(path) → string | nullreadFile(path) → Promise<string>reads the whole file as text
writeFileSync(path, text) → booleanwriteFile(path, text) → Promise<void>writes the file, replacing what was there
appendFileSync(path, text) → booleanappendFile(path, text) → Promise<void>adds to the end, making the file if needed
existsSync(path) → booleanexists(path) → Promise<boolean>a file or a folder is there
readdirSync(path) → string[] | nullreaddir(path) → Promise<string[]>the names in a folder, without . and ..
mkdirSync(path, options?) → booleanmkdir(path, options?) → Promise<void>makes a folder; { recursive: true } makes the missing folders above it too
import * as 
fs
from "@amxts/core/fs";
async function saveMap() { await
fs
.
mkdir
(`${
server
.
dataDir
}/myplugin`, {
recursive
: true });
await
fs
.
writeFile
(`${
server
.
dataDir
}/myplugin/last-map.txt`,
server
.
map
);
}
fs
.
readFile
("missing.txt").
catch
((
error
) =>
console
.
log
(
error
.message));
// ENOENT: no such file or directory, open 'missing.txt'

Paths

A path is relative to the game folder, cstrike/, as for any AMX Mod X plugin. server.configsDir is the configs folder (addons/amxmodx/configs) and server.dataDir the data folder (addons/amxmodx/data), wherever the server keeps them. Keep your plugin's files in a folder or under a name of its own: configs/myplugin/, data/myplugin-stats.txt.

Text

Files are read and written as UTF-8 text. A file is read whole, whatever its size. A byte-order mark at the start of a file is kept, as in Node.

JSON

JSON reads and writes JSON text as in JavaScript. JSON.parse is told what it reads - JSON.parse<Stats>(text), or a type the value goes to - and builds that object, its arrays, Records and fields included:

import * as 
fs
from "@amxts/core/fs";
interface Stats {
kills
: number;
deaths
: number;
lastMap
?: string;
} const
statsFile
= `${
server
.
dataDir
}/myplugin/stats.json`;
function loadStats(): Stats { const
text
=
fs
.
readFileSync
(
statsFile
);
if (
text
== null) return {
kills
: 0,
deaths
: 0 };
return
JSON
.
parse
(
text
);
} function saveStats(
stats
: Stats) {
fs
.
writeFileSync
(
statsFile
,
JSON
.
stringify
(
stats
, null, 2));
}

Text that is not JSON is a SyntaxError; a value of another kind than the type says - text where a number goes, a field that must be there and is not - is a TypeError that names the field. Catch them with try/catch. JSON.stringify leaves out a field that is undefined, writes a Date as its ISO text and an object with a toJSON() as what that gives.

For settings a server owner edits, a typed config of config-core reads YAML, JSON and INI alike.

When it fails

A synchronous function says it failed by its result: null from readFileSync and readdirSync, false from writeFileSync, appendFileSync and mkdirSync - a folder that does not exist, say. A promise function rejects with an Error whose message starts with ENOENT, as Node's do (mkdir's with EEXIST or ENOENT).

The work is done at once either way: a promise function has settled by the time it returns.

The operating system

import { 
EOL
,
platform
} from "@amxts/core/os";
if (
platform
() == "linux")
console
.
log
("running on Linux");
const
lines
= ["first", "second"].
join
(
EOL
); // "\r\n" on Windows, "\n" on Linux

platform() is "win32" or "linux", and EOL the line ending of that system.

In a test, the fake server keeps the game folder in memory: server.writeFile(path, text) before the plugin reads, server.file(path) after it writes (Testing).