IMIsle Modkit
Pages in this area

Persistence

For most data the key/value store is less work than a file of your own. See Storing data. This page covers files you read and write yourself.

Server scripts forget everything when the game server restarts. To keep data, write it to a file in your resource folder.

There is no database layer. For most resources a JSON file is enough.

The pattern

local FILE = "data.json"
local data = modkit.json.decode(modkit.resources.file(FILE) or "{}")

local function save()
  if not modkit.resources.save(FILE, modkit.json.encode(data)) then
    print("could not write " .. FILE)
  end
end

modkit.events.add("playerConnected", function(player)
  local entry = data[player.steamId] or { joins = 0 }
  entry.joins = entry.joins + 1
  entry.name = player.name
  data[player.steamId] = entry
  save()
  player:message("Visit number " .. entry.joins)
end)

modkit.resources.file(path) reads a file of your own resource and returns its content, or nil when it does not exist. modkit.resources.save(path, text) writes it. Both take a path relative to your resource folder. Absolute paths and .. are refused.

save writes to a temporary file first and then replaces the old file. A server that is killed in the middle of a write leaves the old file intact.

Why not io.open

io.open works, but relative paths do not start in your resource folder. The game server runs with a different working directory. io.open("resources/skins/data.json", "w") fails quietly on a normal setup, and the skins template has exactly this problem.

If you need io, build an absolute path yourself. modkit.resources is the simpler way.

Use Steam IDs as keys

player.steamId is stable. player.id is the script link id of the current session and changes on every join. player.name can change.

Keep Steam IDs as strings. They have 17 digits and do not fit into a Lua float without loss.

Do not save too often

File writes run on the game thread and block it until the disk is done. Keep the file small and do not write on every chat line or every tick. Save on meaningful changes, or mark the data dirty and save on a timer.

local dirty = false
local function touch() dirty = true end

modkit.setInterval(30000, function()
  if dirty then dirty = false save() end
end)

The runner stops the game server by killing the process. There is no shutdown event, so data that is only in memory at that moment is lost.

Data files are private

Files you write this way are not sent to clients. Clients can only download what the manifest lists under client, files and nui. Do not put a data file under a folder that a files wildcard covers, for example ui\.

Client side

Client scripts have no file access. Keep client state on the server and send it with an event when the player joins.

Reference: resources natives, json natives.