From 85c9df8cb6f88e0ca6a2b1594066bb5316abd533 Mon Sep 17 00:00:00 2001 From: Axel Cocat <ax.cocat@gmail.com> Date: Sat, 1 Apr 2023 17:55:51 +0200 Subject: [PATCH 1/2] feat: add async queries --- src/LuaEngine/GlobalMethods.h | 98 ++++++++++++++++++++++++++++++++-- src/LuaEngine/LuaEngine.cpp | 1 + src/LuaEngine/LuaEngine.h | 1 + src/LuaEngine/LuaFunctions.cpp | 3 ++ src/LuaEngine/ServerHooks.cpp | 1 + 5 files changed, 101 insertions(+), 3 deletions(-) diff --git a/src/LuaEngine/GlobalMethods.h b/src/LuaEngine/GlobalMethods.h index 3dc99be9ca..e0a0eb1543 100644 --- a/src/LuaEngine/GlobalMethods.h +++ b/src/LuaEngine/GlobalMethods.h @@ -1271,11 +1271,46 @@ namespace LuaGlobalFunctions return 0; } + template <typename T> + int DBQueryAsync(lua_State* L, DatabaseWorkerPool<T>& db) + { + const char* query = Eluna::CHECKVAL<const char*>(L, 1); + luaL_checktype(L, 2, LUA_TFUNCTION); + lua_pushvalue(L, 2); + int funcRef = luaL_ref(L, LUA_REGISTRYINDEX); + if (funcRef == LUA_REFNIL || funcRef == LUA_NOREF) + { + luaL_argerror(L, 2, "unable to make a ref to function"); + return 0; + } + + Eluna::GEluna->queryProcessor.AddCallback(db.AsyncQuery(query).WithCallback([L, funcRef](QueryResult result) + { + ElunaQuery* eq = result ? new ElunaQuery(result) : nullptr; + + LOCK_ELUNA; + + // Get function + lua_rawgeti(L, LUA_REGISTRYINDEX, funcRef); + + // Push parameters + Eluna::Push(L, eq); + + // Call function + Eluna::GEluna->ExecuteCall(1, 0); + + luaL_unref(L, LUA_REGISTRYINDEX, funcRef); + })); + + return 0; + } + /** * Executes a SQL query on the world database and returns an [ElunaQuery]. * * The query is always executed synchronously * (i.e. execution halts until the query has finished and then results are returned). + * If you need to execute the query asynchronously, use [Global:WorldDBQueryAsync] instead. * * local Q = WorldDBQuery("SELECT entry, name FROM creature_template LIMIT 10") * if Q then @@ -1308,6 +1343,29 @@ namespace LuaGlobalFunctions return 1; } + /** + * Executes an asynchronous SQL query on the world database and passes an [ElunaQuery] to a callback function. + * + * The query is executed asynchronously + * (i.e. the server keeps running while the query is executed in parallel, and results are passed to a callback function). + * If you need to execute the query synchronously, use [Global:WorldDBQuery] instead. + * + * WorldDBQueryAsync("SELECT entry, name FROM creature_template LIMIT 10", function(Q) + * if Q then + * repeat + * local entry, name = Q:GetUInt32(0), Q:GetString(1) + * print(entry, name) + * until not Q:NextRow() + * end + * end) + * + * @param string sql : query to execute + */ + int WorldDBQueryAsync(lua_State* L) + { + return DBQueryAsync(L, WorldDatabase); + } + /** * Executes a SQL query on the world database. * @@ -1315,7 +1373,7 @@ namespace LuaGlobalFunctions * If you need to execute the query synchronously, use [Global:WorldDBQuery] instead. * * Any results produced are ignored. - * If you need results from the query, use [Global:WorldDBQuery] instead. + * If you need results from the query, use [Global:WorldDBQuery] or [Global:WorldDBQueryAsync] instead. * * WorldDBExecute("DELETE FROM my_table") * @@ -1333,6 +1391,7 @@ namespace LuaGlobalFunctions * * The query is always executed synchronously * (i.e. execution halts until the query has finished and then results are returned). + * If you need to execute the query asynchronously, use [Global:CharDBQueryAsync] instead. * * For an example see [Global:WorldDBQuery]. * @@ -1359,6 +1418,22 @@ namespace LuaGlobalFunctions return 1; } + /** + * Executes an asynchronous SQL query on the character database and passes an [ElunaQuery] to a callback function. + * + * The query is executed asynchronously + * (i.e. the server keeps running while the query is executed in parallel, and results are passed to a callback function). + * If you need to execute the query synchronously, use [Global:CharDBQuery] instead. + * + * For an example see [Global:WorldDBQueryAsync]. + * + * @param string sql : query to execute + */ + int CharDBQueryAsync(lua_State* L) + { + return DBQueryAsync(L, CharacterDatabase); + } + /** * Executes a SQL query on the character database. * @@ -1366,7 +1441,7 @@ namespace LuaGlobalFunctions * If you need to execute the query synchronously, use [Global:CharDBQuery] instead. * * Any results produced are ignored. - * If you need results from the query, use [Global:CharDBQuery] instead. + * If you need results from the query, use [Global:CharDBQuery] or [Global:CharDBQueryAsync] instead. * * CharDBExecute("DELETE FROM my_table") * @@ -1384,6 +1459,7 @@ namespace LuaGlobalFunctions * * The query is always executed synchronously * (i.e. execution halts until the query has finished and then results are returned). + * If you need to execute the query asynchronously, use [Global:AuthDBQueryAsync] instead. * * For an example see [Global:WorldDBQuery]. * @@ -1410,6 +1486,22 @@ namespace LuaGlobalFunctions return 1; } + /** + * Executes an asynchronous SQL query on the character database and passes an [ElunaQuery] to a callback function. + * + * The query is executed asynchronously + * (i.e. the server keeps running while the query is executed in parallel, and results are passed to a callback function). + * If you need to execute the query synchronously, use [Global:AuthDBQuery] instead. + * + * For an example see [Global:WorldDBQueryAsync]. + * + * @param string sql : query to execute + */ + int AuthDBQueryAsync(lua_State* L) + { + return DBQueryAsync(L, LoginDatabase); + } + /** * Executes a SQL query on the login database. * @@ -1417,7 +1509,7 @@ namespace LuaGlobalFunctions * If you need to execute the query synchronously, use [Global:AuthDBQuery] instead. * * Any results produced are ignored. - * If you need results from the query, use [Global:AuthDBQuery] instead. + * If you need results from the query, use [Global:AuthDBQuery] or [Global:AuthDBQueryAsync] instead. * * AuthDBExecute("DELETE FROM my_table") * diff --git a/src/LuaEngine/LuaEngine.cpp b/src/LuaEngine/LuaEngine.cpp index 93421c5b9c..89a00d3899 100644 --- a/src/LuaEngine/LuaEngine.cpp +++ b/src/LuaEngine/LuaEngine.cpp @@ -164,6 +164,7 @@ enabled(false), L(NULL), eventMgr(NULL), httpManager(), +queryProcessor(), ServerEventBindings(NULL), PlayerEventBindings(NULL), diff --git a/src/LuaEngine/LuaEngine.h b/src/LuaEngine/LuaEngine.h index a4ecdd87ec..29bef71d17 100644 --- a/src/LuaEngine/LuaEngine.h +++ b/src/LuaEngine/LuaEngine.h @@ -240,6 +240,7 @@ class ELUNA_GAME_API Eluna lua_State* L; EventMgr* eventMgr; HttpManager httpManager; + QueryCallbackProcessor queryProcessor; BindingMap< EventKey<Hooks::ServerEvents> >* ServerEventBindings; BindingMap< EventKey<Hooks::PlayerEvents> >* PlayerEventBindings; diff --git a/src/LuaEngine/LuaFunctions.cpp b/src/LuaEngine/LuaFunctions.cpp index de9fc65446..2425c7ebeb 100644 --- a/src/LuaEngine/LuaFunctions.cpp +++ b/src/LuaEngine/LuaFunctions.cpp @@ -128,10 +128,13 @@ luaL_Reg GlobalMethods[] = { "RunCommand", &LuaGlobalFunctions::RunCommand }, { "SendWorldMessage", &LuaGlobalFunctions::SendWorldMessage }, { "WorldDBQuery", &LuaGlobalFunctions::WorldDBQuery }, + { "WorldDBQueryAsync", &LuaGlobalFunctions::WorldDBQueryAsync }, { "WorldDBExecute", &LuaGlobalFunctions::WorldDBExecute }, { "CharDBQuery", &LuaGlobalFunctions::CharDBQuery }, + { "CharDBQueryAsync", &LuaGlobalFunctions::CharDBQueryAsync }, { "CharDBExecute", &LuaGlobalFunctions::CharDBExecute }, { "AuthDBQuery", &LuaGlobalFunctions::AuthDBQuery }, + { "AuthDBQueryAsync", &LuaGlobalFunctions::AuthDBQueryAsync }, { "AuthDBExecute", &LuaGlobalFunctions::AuthDBExecute }, { "CreateLuaEvent", &LuaGlobalFunctions::CreateLuaEvent }, { "RemoveEventById", &LuaGlobalFunctions::RemoveEventById }, diff --git a/src/LuaEngine/ServerHooks.cpp b/src/LuaEngine/ServerHooks.cpp index 67a9809c2b..db2c537549 100644 --- a/src/LuaEngine/ServerHooks.cpp +++ b/src/LuaEngine/ServerHooks.cpp @@ -322,6 +322,7 @@ void Eluna::OnWorldUpdate(uint32 diff) eventMgr->globalProcessor->Update(diff); httpManager.HandleHttpResponses(); + queryProcessor.ProcessReadyCallbacks(); START_HOOK(WORLD_EVENT_ON_UPDATE); Push(diff); From ff470dd6134d316c2ef2664f41f06b9664a72a50 Mon Sep 17 00:00:00 2001 From: Axel Cocat <ax.cocat@gmail.com> Date: Sat, 1 Apr 2023 18:03:10 +0200 Subject: [PATCH 2/2] update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a751b5a894..9f5a43b0e1 100644 --- a/README.md +++ b/README.md @@ -118,3 +118,4 @@ Eluna API for AC: - Added `ItemTemplate` methods: https://github.com/azerothcore/mod-eluna/pull/84 - Added logging with `ELUNA_LOG_INFO` for `RunCommand()`: https://github.com/azerothcore/mod-eluna/pull/75 - Added `GetOwnerHalaa` and `SetOwnerHalaa`: https://github.com/azerothcore/mod-eluna/pull/79 +- Added `WorldDBQueryAsync`, `CharDBQueryAsync` and `AuthDBQueryAsync`: https://github.com/azerothcore/mod-eluna/pull/113