LuaDB - Kingdom Come Deliverance II Lua Persistence Module
This module provides SQLite-based Lua data persistence for Kingdom Come: Deliverance II mod developers, supporting two storage modes:
-
Global : Data persists across all game saves (independent of the game save).
-
Local : Bound to the game save, data is associated with a specific game save file:
- Automatically saved when the game is saved.
- Automatically loaded when the game loads that save.
- Install Ultimate ASI Loader
- Place the
.asifile of this mod and ASI loader in the game root directory (whereKingdomCome.exeresides)
Use DB.Create to create a database object, then use the methods of that object to store and retrieve data.
DB will automatically convert key, value into JSON strings for storage, and automatically parse JSON strings back into lua objects when reading.
You can now store any Lua object encodable by json.lua! Including tables, strings, numbers, booleans, etc.
Strings in objects are no longer limited by character sets.
Script.ReloadScript("scripts/<modid>/kcd2db_fake_db.lua")
local myDB = DB.Create("MyAwesomeMod") -- Use your Mod name or a unique enough string that won't conflict as the namespace
myDB.Set("player_health", 85.6)
myDB:Set("has_dragon_sword", true) -- myDB allows both .call and :call syntax
-- Test storage/retrieval of complex large objects
local test = {
a = 1,
b = "nihao",
c = {
d = 2,
e = "shijie",
f = {
g = 3,
h = "非ASCII字符"
}
},
d = string.rep("a", 1000)
}
myDB.Set("test", test)
-- Global APIs (Cross-save)
myDB.SetG("settings", {volume = 0.8, fullscreen = true})
local settings = myDB.GetG("settings") -- return table {volume = 0.8, fullscreen = true}Script.ReloadScript("scripts/yourmod/kcd2db_fake_db.lua")
YourMod = YourMod or (function()
local db = DB.Create("YourMod")
return {
version = "__VERSION__",
localData = db.L,
globalData = db.G
}
end)()
function YourMod:Init()
local settingA = YourMod.localData.settingA
local settingB = YourMod.globalData.settingB
-- do something with settings
end
function YourMod:Save()
YourMod.localData.settingA = "valueA"
YourMod.globalData.settingB = { a = 1, b = 2 }
-- save settings
end
function YourMod:DoSomething()
local settingA = YourMod.localData.settingA
local settingB = YourMod.globalData.settingB
-- do something with settings
-- update settings if something changed
YourMod.localData.settingA = settingA
YourMod.globalData.settingB = settingB
end
YourMod:Init()Mod authors can ship dist/kcd2db_fake_db.lua inside their own PAK and load it as the first line of the mod entry script:
Script.ReloadScript("scripts/<modid>/kcd2db_fake_db.lua")
local db = DB.Create("MyMod")Use a mod-specific path such as scripts/<modid>/kcd2db_fake_db.lua to avoid PAK path conflicts between mods. With kcd2db.asi installed, the wrapper uses the real persistent backend. Without kcd2db.asi, it provides a session-only fake backend so the mod can keep running, but data is memory-only, not saved to disk, and not isolated by save switch.
See docs/fake-db.md for packaging details and backend status fields.
The standalone fake DB release ZIP installs as Mods/aaa_kcd_fake_db/ so it loads early among manually installed local mods under the default alphabetical load order. This cannot override Steam Workshop load order or a user-defined Mods/mod_order.txt; if mod_order.txt exists, list aaa_kcd_fake_db before mods that call DB.Create.
DB.Get(key)- Get local key valueDB.Set(key, value)- Set local key valueDB.Del(key)- Delete local key valueDB.Exi(key)- Check if local key existsDB.All()- Get all local key valuesDB.GetG(key)- Get global key valueDB.SetG(key, value)- Set global key valueDB.DelG(key)- Delete global key valueDB.ExiG(key)- Check if global key existsDB.AllG()- Get all global key valuesDB.Dump()- Print all data (Note: $1~9 in strings will be treated as color characters)DB.Create("Your MOD")- Create a namespace instance
All methods support both . Call and : Call syntax, which can be selected according to personal preferences.
DB("Your MOD")- Equivalent toDB.Create("Your MOD")DB.key/DB["key"]- Access local key value (invalid if the method with the same name exists)DB.L.key/DB.L["key"]- Always access local key value, If you are not sure whether your key conflicts with an existing method nameDB.G.key/DB.G["key"]- Always access global key value, If you are not sure whether your key conflicts with an existing method name
- Namespace names cannot contain colons, "namespace:" will be used as a key prefix in the database to isolate data from different mods.
- When the key name is the same as an existing method, direct access will call the method instead of the key value, use. L or. G instead.
- All values will be automatically JSON encoded/decoded (the game has built-in json.lua V0.1.1).
- The JSON string size of a single object should not exceed 1 billion bytes (approximately 953MB), otherwise Sqlite will report an error "string or blob too big," making it impossible to store.
- When using
Dump(), $0~9 in strings will be parsed as color codes by the console. The in-game console cannot display non-ASCII characters, and DEBUG log data will be truncated to prevent lag.
LuaDB.* is the low-level C++ binding. It stores Lua booleans, numbers, and strings directly. Use the DB wrapper above when you need JSON-backed tables or other JSON-encodable Lua values.
-- Store data (automatically bound to current save)
LuaDB.Set(key, value)
-- Retrieve data
local value = LuaDB.Get(key)
-- Delete data
LuaDB.Del(key)
-- Check key existence
local exists = LuaDB.Exi(key)-- Store global data
LuaDB.SetG(key, value)
-- Retrieve global data
local value = LuaDB.GetG(key)
-- Delete global data
LuaDB.DelG(key)
-- Check global key existence
local exists = LuaDB.ExiG(key)-- View all stored data in console
LuaDB.Dump()DBwrapper keys are namespaced strings. Non-string keys are JSON-encoded or converted to strings before the namespace prefix is added.DBwrapper values are JSON-encoded before being passed toLuaDB, so tables, strings, numbers, booleans, and JSON-encodable nested values are supported whenjson.luais available.- Raw
LuaDBvalues are limited to booleans, numbers, and strings because the C++ layer storesScriptValueasbool,float, orstd::string. - Raw numbers are stored as single-precision floats. Raw booleans are stored as
0/1. Strings are stored as SQLiteTEXT.
Most mods should load kcd2db_fake_db.lua and use DB.Create. Use raw LuaDB calls only for low-level boolean, number, or string storage.
-- Save-associated data example
LuaDB.Set("player_health", 85.6)
LuaDB.Set("has_dragon_sword", true)
-- Global data example
LuaDB.SetG("global_kill_count", 42)
LuaDB.SetG("ending_unlocked", "bad_ending")
-- View data in console
LuaDB.Dump()cmake -B build -G "Visual Studio 17 2022" -DSQLITECPP_RUN_CPPCHECK=OFF -DSQLITECPP_RUN_CPPLINT=OFFcmake --build build --config Release
When building from WSL, make sure cmake resolves to the Windows CMake/MSVC toolchain and use source/build directories on a Windows-accessible path.
- Uses SQLite3 database named
kcd2db.dbin game root - Operation logs stored in
kcd2db.login game root - If launched with
-console,INFO,WARN, andERRORlogs also appear in the console by default with a[kcd2db]prefix.DEBUGlogs remain inkcd2db.log. - Use
-kcd2dbConsoleLog=debug|info|warn|error|offto change console log verbosity. - The Lua runner is disabled by default. Launch the game with
-kcd2dbLuaRunnerto accept script paths from supported VS Code Lua runner extensions on127.0.0.1:28771. Use-kcd2dbLuaRunner=<port>to override the port. - The runner executes queued scripts on the game update thread and reads UTF-8 Windows paths directly before passing script buffers to CryEngine.
- Existing clients can keep using the existing 4-byte little-endian length-prefixed comma-separated path payload. Native clients can send a length-prefixed UTF-8 payload beginning with
KCD2DB_LUA_RUNNER/1, followed bycommand=run, optionalmode=auto|buffer|file, and onepath=<absolute path>line per script.command=pingreturnspong. kcd2db.logmay include local paths and command-line arguments; redact them before sharing logs if needed.
This mod heavily utilizes reverse-engineered game internals and may be affected by game updates. If experiencing crashes
after game updates, try removing the mod file (or rename .asi extension to disable).
Want to know how I found the offset? Check out this How to find the address of gEnv
LuaDB - 天国拯救2 Lua 数据持久化模块
本模块为《天国拯救2》Mod开发者提供基于Sqlite 的 Lua 数据存储解决方案,支持两种存储模式:
-
全局:数据在所有游戏存档中持久存在(独立于游戏存档)。
-
本地:绑定到游戏存档,数据与特定的游戏存档文件关联:
- 游戏保存时自动保存。
- 游戏加载该存档时自动加载。
- 安装 终极 ASI 加载器
- 将本Mod的
.asi文件与ASI加载器共同放置于游戏根目录(KingdomCome.exe所在目录)
使用 DB.Create 创建一个数据库对象,然后使用该对象的方法进行数据存储和读取。
DB将自动把key,value转换为json字符串进行存储,读取时自动解析json字符串还原为lua对象。
你现在可以存储任何可以通过json.lua编码的Lua对象了!,包括table,string,number,boolean等等,对象中的字符串也不再有字符集限制
Script.ReloadScript("scripts/<modid>/kcd2db_fake_db.lua")
local myDB = DB.Create("MyAwesomeMod") -- 使用你的 Mod 名或者一个足够独特不会冲突的字符串作为命名空间
myDB.Set("player_health", 85.6)
myDB:Set("has_dragon_sword", true) -- myDB 允许.调用 和 :调用 语法
-- 测试复杂大对象的存储/读取
local test = {
a = 1,
b = "nihao",
c = {
d = 2,
e = "shijie",
f = {
g = 3,
h = "测试字符"
}
},
d = string.rep("a", 1000)
}
myDB.Set("test", test)
-- 全局API(跨存档)
myDB.SetG("settings", {volume = 0.8, fullscreen = true})
local settings = myDB.GetG("settings") -- return table {volume = 0.8, fullscreen = true}Script.ReloadScript("scripts/yourmod/kcd2db_fake_db.lua")
YourMod = YourMod or (function()
local db = DB.Create("YourMod")
return {
version = "__VERSION__",
localData = db.L,
globalData = db.G
}
end)()
function YourMod:Init()
local settingA = YourMod.localData.settingA
local settingB = YourMod.globalData.settingB
-- do something with settings
end
function YourMod:Save()
YourMod.localData.settingA = "valueA"
YourMod.globalData.settingB = { a = 1, b = 2 }
-- save settings
end
function YourMod:DoSomething()
local settingA = YourMod.localData.settingA
local settingB = YourMod.globalData.settingB
-- do something with settings
-- update settings if something changed
YourMod.localData.settingA = settingA
YourMod.globalData.settingB = settingB
end
YourMod:Init()Mod 作者可以把 dist/kcd2db_fake_db.lua 放进自己的 PAK,并在入口脚本第一行加载:
Script.ReloadScript("scripts/<modid>/kcd2db_fake_db.lua")
local db = DB.Create("MyMod")建议使用 scripts/<modid>/kcd2db_fake_db.lua 这类独立路径,避免多个 Mod 在合并 PAK 时覆盖同一个脚本路径。安装了 kcd2db.asi 时,fake DB 文件会保留真实持久化后端;未安装时,会提供仅当前 Lua session 有效的 fake 后端,让 Mod 继续运行,但数据只保存在内存中,不会写入磁盘,也不按存档切换隔离。
打包方式和后端状态字段见 docs/fake-db.md。
独立 fake DB 发布 ZIP 会安装为 Mods/aaa_kcd_fake_db/,在默认字母序规则下尽量早于其他本地手动安装 Mod 加载。它不能覆盖 Steam Workshop 的加载顺序,也不能覆盖用户自定义的 Mods/mod_order.txt;如果存在 mod_order.txt,需要把 aaa_kcd_fake_db 写在调用 DB.Create 的 Mod 前面。
DB.Get(key)- 获取本地键值DB.Set(key, value)- 设置本地键值DB.Del(key)- 删除本地键值DB.Exi(key)- 检查本地键是否存在DB.All()- 获取所有本地键值DB.GetG(key)- 获取全局键值DB.SetG(key, value)- 设置全局键值DB.DelG(key)- 删除全局键值DB.ExiG(key)- 检查全局键是否存在DB.AllG()- 获取所有全局键值DB.Dump()- 打印所有数据(注意: 字符串中的$1~9会被当作颜色字符)DB.Create("Your MOD")- 创建命名空间实例
所有的方法都同时支持 . 调用和 : 调用语法,可以根据个人喜好选择使用
DB("你的MOD")- 等同于DB.Create("你的MOD")DB.key/DB["key"]- 访问本地键值(若存在同名方法则无效)DB.L.key/DB.L["key"]- 始终访问本地键值,适用于不确定键名是否与现有方法名冲突时DB.G.key/DB.G["key"]- 始终访问全局键值,适用于不确定键名是否与现有方法名冲突时
- 命名空间名称不能包含冒号,“namespace:”将作为数据库中的键前缀,用于隔离不同模组的数据。
- 当键名与现有方法同名时,直接访问将调用方法而不是键值,请使用.L或.G代替。
- 所有值将自动进行JSON编码/解码(游戏内置了json.lua V0.1.1)。
- 单个对象的JSON字符串大小不应超过10亿字节(约953MB),否则Sqlite会报错“字符串或二进制数据过大”,导致无法存储。
- 使用
Dump()时,字符串中的$0~9将被控制台解析为颜色代码。游戏内控制台无法显示非ASCII字符,DEBUG日志数据将被截断以防止卡顿。
LuaDB.* 是底层 C++ 绑定,只直接存储 Lua 布尔值、数字和字符串。需要表或其他可 JSON 编码的 Lua 值时,请优先使用上方的 DB 包装层。
-- 存储数据(自动关联当前存档)
LuaDB.Set(key, value)
-- 获取数据
local value = LuaDB.Get(key)
-- 删除数据
LuaDB.Del(key)
-- 检查键是否存在
local exists = LuaDB.Exi(key)-- 存储全局数据
LuaDB.SetG(key, value)
-- 获取全局数据
local value = LuaDB.GetG(key)
-- 删除全局数据
LuaDB.DelG(key)
-- 检查全局键是否存在
local exists = LuaDB.ExiG(key)-- 控制台查看所有存储数据
LuaDB.Dump()DB包装层的键会带命名空间前缀;非字符串键会先经过 JSON 编码,失败时再转为字符串。DB包装层的值会先 JSON 编码再传给LuaDB,因此在json.lua可用时支持表、字符串、数字、布尔值以及可 JSON 编码的嵌套值。- 原始
LuaDB值仅支持布尔值、数字和字符串,因为 C++ 层的ScriptValue只保存bool、float或std::string。 - 原始数字以单精度浮点存储,布尔值以
0/1存储,字符串以 SQLiteTEXT存储。
大多数 Mod 应加载 kcd2db_fake_db.lua 并使用 DB.Create。仅在需要底层布尔值、数字或字符串存储时直接调用 raw LuaDB。
-- 存档关联数据示例
LuaDB.Set("player_health", 85.6)
LuaDB.Set("has_dragon_sword", true)
-- 全局数据示例
LuaDB.SetG("global_kill_count", 42)
LuaDB.SetG("ending_unlocked", "bad_ending")
-- 控制台查看数据
LuaDB.Dump()cmake -B build -G "Visual Studio 17 2022" -DSQLITECPP_RUN_CPPCHECK=OFF -DSQLITECPP_RUN_CPPLINT=OFFcmake --build build --config Release
在 WSL 中构建时,请确保 cmake 解析到 Windows CMake/MSVC 工具链,并使用 Windows 可访问路径下的源码与构建目录。
- 使用位于游戏根目录下名为
kcd2db.db的 SQLite3 数据库 - 操作日志存储在游戏根目录下的
kcd2db.log文件中 - 如果使用
-console参数启动,默认只在控制台显示INFO、WARN和ERROR日志,并带有[kcd2db]前缀。DEBUG日志仍会写入kcd2db.log。 - 使用
-kcd2dbConsoleLog=debug|info|warn|error|off调整控制台日志详细程度。 - Lua runner 默认关闭。使用
-kcd2dbLuaRunner启动游戏后,可在127.0.0.1:28771接收 VS Code Lua runner 扩展发送的脚本路径;如需避让端口,可使用-kcd2dbLuaRunner=<port>。 - runner 会在游戏 update 线程执行队列中的脚本,并优先按 UTF-8 Windows 路径读取文件内容后交给 CryEngine 执行。
- 旧客户端可继续使用现有的 4 字节 little-endian 长度前缀逗号分隔路径 payload。原生客户端可发送带长度前缀的 UTF-8 payload:首行为
KCD2DB_LUA_RUNNER/1,随后写入command=run、可选mode=auto|buffer|file,以及每个脚本一行path=<absolute path>。command=ping会返回pong。 kcd2db.log可能包含本机路径和命令行参数;如需分享日志,可先自行打码。
由于本MOD大量使用反汇编的游戏内部细节,可能会受到游戏更新的影响,如遇到更新后游戏崩溃问题,请尝试删除本MOD文件(或将后缀名
.asi改为其他后缀名)。