Hey everyone,
I decided to share a simple, native C++ implementation for database queries. I've noticed that some bases out there still rely on the old mysql_query Lua function (the one that uses os.execute to open the terminal, run the query, and generate a temporary text file in /tmp).
The main issue with that legacy method is that it halts the main game thread. During peak moments, such as guild wars, this causes the notorious "step lag" / "core lag". The code below creates a native db.query function utilizing the direct connection from the DBManager, making the execution instantaneous and completely lag-free.
## Source Implementation (Game)
Step 1: questlua.cpp
Open the file game/src/questlua.cpp and search for the ScriptToString function. Right after its closing bracket (the last }), skip a line and paste the new code.
Example of how it should look:
lua_settop(L,x);
return retstr;
} // <-- End of ScriptToString
// --- START NATIVE DB.QUERY ---
int db_query(lua_State* L)
{
if (!lua_isstring(L, 1))
return 0;
const char* szQuery = lua_tostring(L, 1);
if (!szQuery || strlen(szQuery) == 0)
return 0;
SQLMsg* pMsg = DBManager::instance().DirectQuery(szQuery);
if (pMsg)
{
delete pMsg;
}
return 0;
}
void RegisterDBFunctionTable()
{
luaL_reg db_functions[] =
{
{ "query", db_query },
{ NULL, NULL }
};
CQuestManager::instance().AddLuaFunctionTable("db", db_functions);
}
// --- END NATIVE DB.QUERY ---
Step 2: Still in questlua.cpp
In the exact same file, search for RegisterHorseFunctionTable();. Skip a line below it and register our new function.
Example of how it should look:
RegisterHorseFunctionTable();
RegisterDBFunctionTable();
Step 3: questlua.h
Open game/src/questlua.h, look for the registry list (where the extern void Register... declarations are located), and add the following line:
extern void RegisterDBFunctionTable();
## Final Steps
Now simply compile your source and replace the game executable in your server.
Go to your quests folder (e.g., share/locale/english/quest), open the quest_functions file, and add the following line: db.query
## Quest Usage Example
You can use string.format to build your queries in a clean and safe way. Here is an example of a kill counter updating the kill column inside the player table:
quest ranking_kills begin
state start begin
when kill with npc.is_pc() begin
local pid = pc.get_player_id()
-- Direct, fast query with no core lag
local query = string.format("UPDATE player.player SET kill = kill + 1 WHERE id = %d", pid)
db.query(query)
end
end
end
## Caveats and Limitations
Before applying this to all your systems, please keep two things in mind:
Execution Only (Does not read SELECTs): This function was built specifically for write performance (UPDATE, INSERT, DELETE, REPLACE). Due to the delete pMsg; command in the C++ code, it does not return SELECT rows into Lua tables. If you try to fetch data, it will return 0 or nil.
Beware of SQL Injection: Never place an input() command directly inside db.query without strictly sanitizing it first. If you ask a player to type a string and feed it directly into the query, a malicious user could run commands to drop your tables. Always use safe, native source functions like pc.get_player_id(), pc.get_name(), etc., or heavily restrict the allowed input characters.