Capítulo 6.8: E/S de Archivos y JSON
Inicio | << Anterior: Timers & CallQueue | File I/O & JSON | Siguiente: Networking & RPC >>
Introducción
DayZ provides file I/O operations for reading and writing text files, JSON serialization/deserialization, directory management, and file enumeration. All file operations use special path prefixes ($profile:, $saves:, $mission:) rather than absolute filesystem paths. This chapter covers every file operation available in Enforce Script.
Path Prefixes
| Prefijo | Ubicación | Escribible |
|---|---|---|
$profile: | Server/client profile directory (e.g., DayZServer/profiles/) | Yes |
$saves: | Save directory | Yes |
$mission: | Current mission folder (e.g., mpmissions/dayzOffline.chernarusplus/) | Read typically |
$CurrentDir: | Current working directory | Depends |
| No prefix | Relative to game root | Read only |
Importante: Most file write operations are restricted to
$profile:and$saves:. Attempting to write elsewhere may silently fail.
File Existence Check
proto bool FileExist(string name);Returns true if the file exists at the given path.
Ejemplo:
if (FileExist("$profile:MyMod/config.json"))
{
Print("Config file found");
}
else
{
Print("Config file not found, creating defaults");
}Opening & Closing Files
proto FileHandle OpenFile(string name, FileMode mode);
proto void CloseFile(FileHandle file);FileMode Enum
enum FileMode
{
READ, // Open for reading (file must exist)
WRITE, // Open for writing (creates new / overwrites existing)
APPEND // Open for appending (creates if not exists)
}FileHandle is an integer handle. A return value of 0 indicates failure.
Ejemplo:
FileHandle fh = OpenFile("$profile:MyMod/log.txt", FileMode.WRITE);
if (fh != 0)
{
// File opened successfully
// ... do work ...
CloseFile(fh);
}Critical: Always call
CloseFile()when done. Failure to close files can cause data loss and resource leaks.
Writing Files
FPrintln (Write Line)
proto void FPrintln(FileHandle file, void var);Writes the value followed by a newline character.
FPrint (Write Without Newline)
proto void FPrint(FileHandle file, void var);Writes the value without a trailing newline.
Ejemplo --- write a log file:
void WriteLog(string message)
{
FileHandle fh = OpenFile("$profile:MyMod/log.txt", FileMode.APPEND);
if (fh != 0)
{
int year, month, day, hour, minute;
GetGame().GetWorld().GetDate(year, month, day, hour, minute);
string timestamp = string.Format("[%1-%2-%3 %4:%5]", year, month, day, hour, minute);
FPrintln(fh, timestamp + " " + message);
CloseFile(fh);
}
}Reading Files
FGets (Read Line)
proto int FGets(FileHandle file, string var);Reads one line from the file into var. Returns the number of characters read, or -1 at end of file.
Ejemplo --- read a file line by line:
void ReadConfigFile()
{
FileHandle fh = OpenFile("$profile:MyMod/settings.txt", FileMode.READ);
if (fh != 0)
{
string line;
while (FGets(fh, line) >= 0)
{
Print("Line: " + line);
ProcessLine(line);
}
CloseFile(fh);
}
}ReadFile (Raw Binary Read)
proto int ReadFile(FileHandle file, void param_array, int length);Reads raw bytes into a buffer. Used for binary data.
Directory Operations
MakeDirectory
proto native bool MakeDirectory(string name);Creates a directory. Returns true on success. Creates only the final directory --- parent directories must already exist.
Ejemplo --- ensure directory structure:
void EnsureDirectories()
{
MakeDirectory("$profile:MyMod");
MakeDirectory("$profile:MyMod/data");
MakeDirectory("$profile:MyMod/logs");
}DeleteFile
proto native bool DeleteFile(string name);Deletes a file. Only works in $profile: and $saves: directories.
CopyFile
proto native bool CopyFile(string sourceName, string destName);Copies a file from source to destination.
Ejemplo:
// Backup before overwriting
if (FileExist("$profile:MyMod/config.json"))
{
CopyFile("$profile:MyMod/config.json", "$profile:MyMod/config.json.bak");
}File Enumeration (FindFile / FindNextFile)
Enumerate files matching a pattern in a directory.
proto FindFileHandle FindFile(string pattern, out string fileName,
out FileAttr fileAttributes, FindFileFlags flags);
proto bool FindNextFile(FindFileHandle handle, out string fileName,
out FileAttr fileAttributes);
proto native void CloseFindFile(FindFileHandle handle);FileAttr Enum
enum FileAttr
{
DIRECTORY, // Entry is a directory
HIDDEN, // Entry is hidden
READONLY, // Entry is read-only
INVALID // Invalid entry
}FindFileFlags Enum
enum FindFileFlags
{
DIRECTORIES, // Return only directories
ARCHIVES, // Return only files
ALL // Return both
}Ejemplo --- enumerate all JSON files in a directory:
void ListJsonFiles()
{
string fileName;
FileAttr fileAttr;
FindFileHandle handle = FindFile(
"$profile:MyMod/missions/*.json", fileName, fileAttr, FindFileFlags.ALL
);
if (handle)
{
// Process first result
if (!(fileAttr & FileAttr.DIRECTORY))
{
Print("Found: " + fileName);
}
// Process remaining results
while (FindNextFile(handle, fileName, fileAttr))
{
if (!(fileAttr & FileAttr.DIRECTORY))
{
Print("Found: " + fileName);
}
}
CloseFindFile(handle);
}
}Importante:
FindFilereturns just the file name, not the full path. You must prepend the directory path yourself when processing the files.
Ejemplo --- count files in a directory:
int CountFiles(string pattern)
{
int count = 0;
string fileName;
FileAttr fileAttr;
FindFileHandle handle = FindFile(pattern, fileName, fileAttr, FindFileFlags.ARCHIVES);
if (handle)
{
count++;
while (FindNextFile(handle, fileName, fileAttr))
{
count++;
}
CloseFindFile(handle);
}
return count;
}JsonFileLoader (Generic JSON)
Archivo: 3_Game/tools/jsonfileloader.c (173 lines)
The recommended way to load and save JSON data. Works with any class that has public fields.
Modern API (Preferred)
class JsonFileLoader<Class T>
{
// Load JSON file into object
static bool LoadFile(string filename, out T data, out string errorMessage);
// Save object to JSON file
static bool SaveFile(string filename, T data, out string errorMessage);
// Parse JSON string into object
static bool LoadData(string string_data, out T data, out string errorMessage);
// Serialize object to JSON string
static bool MakeData(T inputData, out string outputData,
out string errorMessage, bool prettyPrint = true);
}All methods return bool --- true on success, false on failure with the error in errorMessage.
Legacy API (Deprecated)
class JsonFileLoader<Class T>
{
static void JsonLoadFile(string filename, out T data); // Returns void!
static void JsonSaveFile(string filename, T data);
static void JsonLoadData(string string_data, out T data);
static string JsonMakeData(T data);
}Critical Gotcha:
JsonLoadFile()returnsvoid. You CANNOT use it in anifcondition:c// WRONG - will not compile or will always be false if (JsonFileLoader<MyConfig>.JsonLoadFile(path, cfg)) { } // CORRECT - use the modern LoadFile() which returns bool if (JsonFileLoader<MyConfig>.LoadFile(path, cfg, error)) { }
Data Class Requirements
The target class must have public fields with default values. The JSON serializer maps field names directly to JSON keys.
class MyConfig
{
int MaxPlayers = 60;
float SpawnRadius = 150.0;
string ServerName = "My Server";
bool EnablePVP = true;
ref array<string> AllowedItems = new array<string>;
ref map<string, int> ItemPrices = new map<string, int>;
void MyConfig()
{
AllowedItems.Insert("BandageDressing");
AllowedItems.Insert("Canteen");
}
}This produces JSON:
{
"MaxPlayers": 60,
"SpawnRadius": 150.0,
"ServerName": "My Server",
"EnablePVP": true,
"AllowedItems": ["BandageDressing", "Canteen"],
"ItemPrices": {}
}Complete Load/Save Example
class MyModConfig
{
int Version = 1;
float RespawnTime = 300.0;
ref array<string> SpawnItems = new array<string>;
}
class MyModConfigManager
{
protected static const string CONFIG_PATH = "$profile:MyMod/config.json";
protected ref MyModConfig m_Config;
void Init()
{
MakeDirectory("$profile:MyMod");
m_Config = new MyModConfig();
Load();
}
void Load()
{
if (!FileExist(CONFIG_PATH))
{
Save(); // Create default config
return;
}
string error;
if (!JsonFileLoader<MyModConfig>.LoadFile(CONFIG_PATH, m_Config, error))
{
Print("[MyMod] Config load error: " + error);
m_Config = new MyModConfig(); // Reset to defaults
Save();
}
}
void Save()
{
string error;
if (!JsonFileLoader<MyModConfig>.SaveFile(CONFIG_PATH, m_Config, error))
{
Print("[MyMod] Config save error: " + error);
}
}
MyModConfig GetConfig()
{
return m_Config;
}
}JsonSerializer (Direct Use)
Archivo: 3_Game/gameplay.c
For cases where you need to serialize/deserialize JSON strings directly without file operations:
class JsonSerializer : Serializer
{
proto bool WriteToString(void variable_out, bool nice, out string result);
proto bool ReadFromString(void variable_in, string jsonString, out string error);
}Ejemplo:
MyConfig cfg = new MyConfig();
cfg.MaxPlayers = 100;
JsonSerializer js = new JsonSerializer();
// Serialize to string
string jsonOutput;
js.WriteToString(cfg, true, jsonOutput); // true = pretty print
Print(jsonOutput);
// Deserialize from string
MyConfig parsed = new MyConfig();
string parseError;
js.ReadFromString(parsed, jsonOutput, parseError);
Print("MaxPlayers: " + parsed.MaxPlayers);Resumen
| Operation | Función | Notas |
|---|---|---|
| Check exists | FileExist(path) | Returns bool |
| Open | OpenFile(path, FileMode) | Returns handle (0 = fail) |
| Close | CloseFile(handle) | Always call when done |
| Write line | FPrintln(handle, data) | With newline |
| Write | FPrint(handle, data) | Without newline |
| Read line | FGets(handle, out line) | Returns -1 at EOF |
| Make dir | MakeDirectory(path) | Single level only |
| Delete | DeleteFile(path) | Only $profile: / $saves: |
| Copy | CopyFile(src, dst) | -- |
| Find files | FindFile(pattern, ...) | Returns handle, iterate with FindNextFile |
| JSON load | JsonFileLoader<T>.LoadFile(path, data, error) | Modern API, returns bool |
| JSON save | JsonFileLoader<T>.SaveFile(path, data, error) | Modern API, returns bool |
| JSON string | JsonSerializer.WriteToString() / ReadFromString() | Direct string operations |
| Concepto | Punto Clave |
|---|---|
| Path prefixes | $profile: (writable), $mission: (read), $saves: (writable) |
| JsonLoadFile | Returns void --- use LoadFile() (bool) instead |
| Data classes | Public fields with defaults, ref for arrays/maps |
| Always close | Every OpenFile must have a matching CloseFile |
| FindFile | Returns only filenames, not full paths |
Mejores Prácticas
- Always wrap file operations in existence checks and close handles in all code paths. An unclosed
FileHandleleaks resources and can prevent the file from being written to disk. Use guard patterns: checkfh != 0, do work, thenCloseFile(fh)before everyreturn. - Use the modern
JsonFileLoader<T>.LoadFile()(returns bool) instead of the legacyJsonLoadFile()(returns void). The legacy API cannot report errors, and attempting to use its void return in a condition silently fails. - Create directories with
MakeDirectory()in order from parent to child.MakeDirectoryonly creates the final directory segment.MakeDirectory("$profile:A/B/C")fails ifA/Bdoes not exist. Create each level sequentially. - Use
CopyFile()to create backups before overwriting config files. JSON parse errors from corrupted saves are unrecoverable. A.bakcopy lets server owners restore the last good state. - Remember that
FindFile()returns only filenames, not full paths. You must concatenate the directory prefix yourself when loading files found viaFindFile/FindNextFile.
Compatibilidad e Impacto
Mod Compatibility: File I/O is inherently isolated per mod when each mod uses its own
$profile:subdirectory. Conflicts occur only when two mods read/write the same file path.
- Load Order: File I/O has no load-order dependency. Mods read and write independently.
- Modded Class Conflicts: No class conflicts. The risk is two mods using the same
$profile:subdirectory name or filename, causing data corruption. - Performance Impact: JSON serialization via
JsonFileLoaderis synchronous and blocks the main thread. Loading large JSON files (>100KB) during gameplay causes frame hitches. Load configs inOnInit()orOnMissionStart(), never inOnUpdate(). - Server/Client: File writes are restricted to
$profile:and$saves:. On clients,$profile:points to the client profile directory. On dedicated servers, it points to the server profile.$mission:is typically read-only on both sides.
Observado en Mods Reales
These patterns were confirmed by studying the source code of professional DayZ mods.
| Patrón | Mod | File/Location |
|---|---|---|
MakeDirectory chain + FileExist check + LoadFile with fallback to defaults | Expansion | Settings manager (ExpansionSettings) |
CopyFile backup before config save | COT | Permission file management |
FindFile/FindNextFile to enumerate per-player JSON files in $profile: | VPP Admin Tools | Player data loader |
JsonSerializer.WriteToString() for RPC payload serialization (no file) | Dabs Framework | Network config sync |
<< Anterior: Timers & CallQueue | File I/O & JSON | Siguiente: Networking & RPC >>
