Old School Ops

Storage

Keeping module data across restarts, in bags for small values and custom tables for lists.

Applies to RF Online 4.55RF Online 4.15RF Online 1.5RF Online 2.2.3.2 · updated 2026-11-06

Each module has its own storage, in the game database. No other module can read it, and a module never sees the game's own tables.

Bags

A bag keeps small values.

bagholds values for
m:global()the whole server
m:char(player)one character (a player table or a character id)
m:account(player)one account (a player table or an account id)
m:item(item)one item instance (an item table or its uid), never a slot or an item code
local bag = m:char(ev.player)
bag:set("kills", (bag:get("kills") or 0) + 1)
bag:delete("kills")
for _, key in ipairs(bag:keys()) do ... end

Values are strings, numbers, booleans, or tables of those up to four levels deep. A value is at most 4096 bytes once stored, a key at most 64 characters, and one bag holds at most 256 keys.

rf.get(scope, owner, key) and rf.set(scope, owner, key, value) reach the same values by scope name ("server" with no owner, "character", "account", "item"). Prefer the bags.

An item's bag stays with that item. It follows the item through the bag, the trunk, the mail, a trade and the auction, and it is deleted when the item is destroyed.

Stacks have a rule of their own. Splitting a stack makes a new item with an empty bag, and the part left behind keeps the old one. Merging keeps the target stack's bag, and a stack that merges away loses its own.

Custom Tables

For data that is a list rather than a few values, declare a table:

tables = {
  kills = {
    columns = { char = "int", boss = "text(16)", n = "int", last = "time" },
    key = { "char", "boss" },
    index = { { "boss" } },
  },
},

The table is created in the game database as mod_<module>_<name> when the module loads, and a column added later is added to it. Changing a column's kind or the key is refused.

column kindholds
inta whole number
numa number
booltrue or false
text(N)a string; N is at most 1024
timeUnix seconds

Use it as m.db.kills:

m.db.kills:put{ char = p.id, boss = "01005", n = 1, last = m:now() }  -- insert or replace
local row = m.db.kills:get{ char = p.id, boss = "01005" }             -- by the full key, or nil
m.db.kills:delete{ char = p.id, boss = "01005" }
local top = m.db.kills:find{ where = { boss = "01005" }, order = "-n", limit = 10 }
local n = m.db.kills:count{ where = { boss = "01005" } }

find returns at most 500 rows, or 100 if no limit is given. Leave where out to match every row, and order = "-n" sorts descending. There is no SQL.

Something here wrong?

Out of date, incomplete, or assuming something it should not. Documentation errors are bugs, and this one arrives tagged and pointed at this page.

Top xp sources

How xp works
Loading…

Open the full search page