Skip to content

Commit bb563e3

Browse files
committed
增加便利性 Lua API
日志正确显示 LuaDB 存储数量
1 parent 0e669cb commit bb563e3

File tree

5 files changed

+146
-7
lines changed

5 files changed

+146
-7
lines changed

.github/workflows/release.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ jobs:
6262
tag_name: release-${{ steps.date.outputs.formattedDate }}
6363
files: kcd2db-${{ steps.date.outputs.formattedDate }}.zip
6464
make_latest: true
65+
generate_release_notes: true
6566
body: |
6667
## Installation
6768

src/db/Database.cpp

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,15 @@ void Database::OnSaveGame(ISaveGame* pSaveGame)
162162
std::lock_guard lock(m_mutex);
163163
if (!m_saveCache.empty())
164164
{
165-
BatchOperation(*m_db, m_saveCache, newSave);
166-
LogInfo("Data saved: %d entries", m_saveCache.size());
165+
if (const int successCount = BatchOperation(*m_db, m_saveCache, newSave);
166+
successCount == m_saveCache.size())
167+
{
168+
LogInfo("Data saved: %d entries", m_saveCache.size());
169+
}
170+
else
171+
{
172+
LogError("Save data error: %d/%d entries saved", successCount, m_saveCache.size());
173+
}
167174
}
168175
}
169176
catch (const std::exception& e)
@@ -181,9 +188,12 @@ void Database::OnPostUpdate(float /*fDeltaTime*/)
181188
std::lock_guard lock(m_mutex);
182189
if (!m_globalDirty) return;
183190
if (const auto now = steady_clock::now(); now - m_lastSaveTime < SAVE_INTERVAL) return;
191+
192+
int successCount = 0;
193+
184194
try
185195
{
186-
ExecuteTransaction([this](SQLite::Database& db)
196+
ExecuteTransaction([this, &successCount](const SQLite::Database& db)
187197
{
188198
SQLite::Statement clearStmt(db, "DELETE FROM Store WHERE savefile = ''");
189199
clearStmt.exec();
@@ -201,12 +211,20 @@ void Database::OnPostUpdate(float /*fDeltaTime*/)
201211
stmt.bind(3, *val);
202212
stmt.exec();
203213
stmt.reset();
214+
successCount++;
204215
}
205216
}
206217
});
207218
m_globalDirty = false;
208219
m_lastSaveTime = steady_clock::now();
209-
LogInfo("Global data saved: %d entries", m_globalCache.size());
220+
if (successCount == m_globalCache.size())
221+
{
222+
LogInfo("Global data saved: %d entries", m_globalCache.size());
223+
}
224+
else
225+
{
226+
LogError("Global save failed: %d/%d entries saved", successCount, m_globalCache.size());
227+
}
210228
}
211229
catch (const std::exception& e)
212230
{
@@ -221,7 +239,7 @@ void Database::ExecuteTransaction(const std::function<void(SQLite::Database&)>&
221239
transaction.commit();
222240
}
223241

224-
void Database::BatchOperation(SQLite::Database& db, const Cache& cache, const std::string& savefile)
242+
int Database::BatchOperation(SQLite::Database& db, const Cache& cache, const std::string& savefile)
225243
{
226244
static constexpr auto UPSERT_SQL = R"sql(
227245
INSERT INTO Store (key, savefile, type, value, updated_at)
@@ -235,6 +253,8 @@ void Database::BatchOperation(SQLite::Database& db, const Cache& cache, const st
235253
SQLite::Statement stmt(db, UPSERT_SQL);
236254
SQLite::Transaction transaction(db);
237255

256+
int count = 0;
257+
238258
for (const auto& [k, v] : cache)
239259
{
240260
if (auto val = SerializeAnyValue(v))
@@ -246,8 +266,10 @@ void Database::BatchOperation(SQLite::Database& db, const Cache& cache, const st
246266
stmt.exec();
247267
stmt.reset();
248268
}
269+
count++;
249270
}
250271
transaction.commit();
272+
return count;
251273
}
252274

253275
std::optional<ScriptAnyValue> Database::ParseAnyValue(const int type, const std::string& value)
@@ -341,7 +363,7 @@ int Database::Dump(IFunctionHandler* pH)
341363

342364
dumpCache(m_globalCache, "[Global Data]");
343365
std::ostringstream oss;
344-
oss << "[Save Data For : " << (m_currentSaveGame.empty() ? "No active save file" : m_currentSaveGame) <<"]";
366+
oss << "[Save Data For : " << (m_currentSaveGame.empty() ? "No active save file" : m_currentSaveGame) << "]";
345367
dumpCache(m_saveCache, oss.str());
346368
return pH->EndFunction();
347369
}

src/db/Database.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class Database final : public CScriptableBase, public IGameFrameworkListener {
5050
bool& changedFlag;
5151
};
5252

53-
static void BatchOperation(SQLite::Database& db, const Cache& cache, const std::string& savefile);
53+
static int BatchOperation(SQLite::Database& db, const Cache& cache, const std::string& savefile);
5454

5555
int GenericAccess(IFunctionHandler* pH, AccessType action, bool isGlobal = false);
5656
void SyncCacheWithDatabase();

src/kcd2db.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#include "kcd2/env.h"
1111
#include "kcd2/IConsole.h"
1212
#include "log/log.h"
13+
#include "lua/db.h"
14+
1315

1416
std::optional<uintptr_t> find_env_addr()
1517
{
@@ -56,14 +58,19 @@ void start()
5658
return;
5759
}
5860
LogDebug("Found environment address: 0x%llX", *env_addr);
61+
5962
auto* env_ptr = reinterpret_cast<SSystemGlobalEnvironment*>(env_addr.value());
6063
while (env_ptr->pGame == nullptr)
6164
{
6265
Sleep(1000);
6366
}
6467
LogDebug("Game Started");
68+
6569
gEnv = *env_ptr;
6670
const auto db = new Database(env_ptr);
71+
72+
env_ptr->pScriptSystem->ExecuteBuffer(db_lua, strlen(db_lua), "db.lua");
73+
6774
LogInfo("Database initialized...%s", db->getName());
6875
}
6976
else

src/lua/db.h

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
//
2+
// Created by muyuanjin on 2025/3/26.
3+
//
4+
5+
#ifndef DB_H
6+
#define DB_H
7+
8+
inline auto db_lua = R"lua(
9+
_G.DB = _G.DB or (function()
10+
-- 检查 json 是否可用
11+
local _json_available = false
12+
13+
local json_available = function()
14+
if _json_available == false then
15+
_json_available = type(json) == "table" and type(json.encode) == "function" and type(json.decode) == "function"
16+
end
17+
return _json_available
18+
end
19+
20+
local function encode_value(value)
21+
if json_available() then
22+
return json.encode(value)
23+
end
24+
return value
25+
end
26+
27+
local function decode_value(value)
28+
if json_available() and type(value) == "string" then
29+
local success, result = pcall(json.decode, value)
30+
if success then
31+
return result
32+
end
33+
end
34+
return value
35+
end
36+
37+
-- 主模块表
38+
local M = {}
39+
40+
-- 创建带命名空间的 DB 实例
41+
function M.Create(namespace)
42+
assert(type(namespace) == "string" and #namespace > 0,
43+
"Namespace must be a non-empty string")
44+
45+
-- 确保命名空间以冒号结尾作为分隔符
46+
if not namespace:match(":$") then
47+
namespace = namespace .. ":"
48+
end
49+
50+
local instance = {}
51+
52+
-- 内部方法添加命名空间前缀
53+
local function prefix_key(key)
54+
if type(key) ~= "string" then
55+
key = encode_value(key)
56+
end
57+
return namespace .. key
58+
end
59+
60+
61+
-- 存档关联API
62+
63+
function instance.Set(key, value)
64+
return LuaDB.Set(prefix_key(key), encode_value(value))
65+
end
66+
67+
function instance.Get(key)
68+
return decode_value(LuaDB.Get(prefix_key(key)))
69+
end
70+
71+
function instance.Del(key)
72+
return LuaDB.Del(prefix_key(key))
73+
end
74+
75+
function instance.Exi(key)
76+
return LuaDB.Exi(prefix_key(key))
77+
end
78+
79+
-- 全局API跨存档
80+
81+
function instance.SetG(key, value)
82+
return LuaDB.SetG(prefix_key(key), encode_value(value))
83+
end
84+
85+
function instance.GetG(key)
86+
return decode_value(LuaDB.GetG(prefix_key(key)))
87+
end
88+
89+
function instance.DelG(key)
90+
return LuaDB.DelG(prefix_key(key))
91+
end
92+
93+
function instance.ExiG(key)
94+
return LuaDB.ExiG(prefix_key(key))
95+
end
96+
97+
-- 调试命令在实现特定前缀查询前原样转发
98+
function instance.Dump()
99+
return LuaDB.Dump()
100+
end
101+
102+
return instance
103+
end
104+
105+
return M
106+
end)()
107+
)lua";
108+
109+
#endif //DB_H

0 commit comments

Comments
 (0)