From 909c760af6fd5b5cd898d7a96321c04a81e46cf3 Mon Sep 17 00:00:00 2001 From: joey <1166538+xtjoeytx@users.noreply.github.com> Date: Mon, 5 Dec 2022 22:45:38 -0500 Subject: [PATCH 1/3] Rework joined classes, and initial work on passing events without using registerTrigger nonsense. --- bin/servers/default/bootstrap.js | 171 ++++++++++++++---- server/CMakeLists.txt | 1 + .../include/Scripting/v8/V8ScriptWrappers.h | 110 +++-------- server/include/TNPC.h | 2 + server/src/Scripting/CScriptEngine.cpp | 7 +- server/src/Scripting/GS2ScriptManager.cpp | 2 +- server/src/Scripting/v8/V8EnvironmentImpl.cpp | 25 +++ server/src/Scripting/v8/V8NPCImpl.cpp | 96 +++++----- server/src/TNPC.cpp | 28 +++ server/src/TScriptClass.cpp | 11 -- server/src/TServer.cpp | 7 +- 11 files changed, 275 insertions(+), 185 deletions(-) diff --git a/bin/servers/default/bootstrap.js b/bin/servers/default/bootstrap.js index 32981dd9f..9089342bf 100644 --- a/bin/servers/default/bootstrap.js +++ b/bin/servers/default/bootstrap.js @@ -1,26 +1,121 @@ 'use strict'; +const INTERNAL_VAR = "__internal"; + (function (env) { + function addInternalData(obj, key, value) { + if (!(INTERNAL_VAR in obj)) { + obj[INTERNAL_VAR] = { + '': obj['__main'] || {} + }; + } + + if (typeof value !== 'object') { + delete obj[INTERNAL_VAR][key]; + } else { + obj[INTERNAL_VAR][key] = value; + } + + mapInternalData(obj); + } + + function unmapInternalData(obj) { + if ("__public" in obj) { + for (const fnName in obj['__public']) + obj[fnName] = undefined; + } + + obj['__events'] = {}; + obj['__public'] = {}; + } + + function mapInternalData(obj) { + if (!(INTERNAL_VAR in obj)) { + return; + } + + unmapInternalData(obj); + + const internalData = obj[INTERNAL_VAR]; + for (const [className, classData] of Object.entries(internalData)) { + const scopeFn = internalData[className]['scope']; + + for (const [ dataType, dataRows] of Object.entries(classData)) { + if (dataType === "events" && typeof scopeFn === 'function') { + for (const [ fnName, fnObj ] of Object.entries(dataRows)) { + if (!(fnName in obj["__events"])) + obj["__events"][fnName] = []; + obj["__events"][fnName].push([fnObj, scopeFn]); + } + } else if (dataType === "public") { + for (const [ fnName, fnObj ] of Object.entries(dataRows)) { + obj["__public"][fnName] = true; + obj[fnName] = fnObj; + } + } + } + } + } + + function callEvent(obj, player, eventName, ...args) { + if (eventName in obj['__events']) { + for (const val of obj['__events'][eventName]) { + // val[0] -> event function + // val[1] -> set player scope + val[1](player); + val[0].apply(obj, args); + val[1](undefined); + } + } + } + + /** + * NPC -> Join Class + */ + env.setCallBack("npc.joinclass", function (npc, className, classData) { + try { + if (className.length > 0) { + addInternalData(npc, className, classData); + env.setNpcEvents(npc, Object.keys(npc["__events"] || {})); + } + } catch (e) { + env.reportException("NPC Exception at " + npc.levelname + "," + npc.x + "," + npc.y + ": " + e.name + " - " + e.message); + } + }); + + /** + * NPC -> Leave Class + */ + env.setCallBack("npc.leaveclass", function (npc, className) { + try { + if (className.length > 0) { + addInternalData(npc, className, undefined); + env.setNpcEvents(npc, Object.keys(npc["__events"] || {})); + } + } catch (e) { + env.reportException("NPC Exception at " + npc.levelname + "," + npc.x + "," + npc.y + ": " + e.name + " - " + e.message); + } + }); + /** * Events -> onCreated(npc, args...) */ - env.setCallBack("npc.created", function (npc, ...args) { + env.setCallBack("npc.created", function (npc) { try { // Set whether the npc supports these events - env.setNpcEvents(npc, - (npc.onCreated && 1 << 0) | - (npc.onTimeout && 1 << 1) | - (npc.onPlayerChats && 1 << 2) | - (npc.onPlayerEnters && 1 << 3) | - (npc.onPlayerLeaves && 1 << 4) | - (npc.onPlayerTouchsMe && 1 << 5) | - (npc.onPlayerLogin && 1 << 6) | - (npc.onPlayerLogout && 1 << 7) | - (npc.onNpcWarped && 1 << 8) - ); - - if (npc.onCreated) - npc.onCreated.apply(npc, args); + addInternalData(npc, "", npc.__main); + env.setNpcEvents(npc, Object.keys(npc["__events"] || {})); + + callEvent(npc, undefined, "onCreated"); + } catch (e) { + env.reportException("NPC Exception at " + npc.levelname + "," + npc.x + "," + npc.y + ": " + e.name + " - " + e.message); + } + }); + + // Eventually migrate everything to this + env.setCallBack("npc.callevent", function (npc, player, eventName, args) { + try { + callEvent(npc, player, eventName, args); } catch (e) { env.reportException("NPC Exception at " + npc.levelname + "," + npc.x + "," + npc.y + ": " + e.name + " - " + e.message); } @@ -31,44 +126,40 @@ */ env.setCallBack("npc.playerchats", function (npc, player, message) { try { - if (npc.onPlayerChats) - npc.onPlayerChats(player, message); + callEvent(npc, player, "onPlayerChats", player, message); } catch (e) { env.reportException("NPC Exception at " + npc.levelname + "," + npc.x + "," + npc.y + ": " + e.name + " - " + e.message); } }); - + /** * Event -> onPlayerEnters(npc, player) */ env.setCallBack("npc.playerenters", function (npc, player) { try { - if (npc.onPlayerEnters) - npc.onPlayerEnters(player); + callEvent(npc, player, "onPlayerEnters", player); } catch (e) { env.reportException("NPC Exception at " + npc.levelname + "," + npc.x + "," + npc.y + ": " + e.name + " - " + e.message); } }); - + /** * Event -> onPlayerLeaves(npc, player) */ env.setCallBack("npc.playerleaves", function (npc, player) { try { - if (npc.onPlayerLeaves) - npc.onPlayerLeaves(player); + callEvent(npc, player, "onPlayerLeaves", player); } catch (e) { env.reportException("NPC Exception at " + npc.levelname + "," + npc.x + "," + npc.y + ": " + e.name + " - " + e.message); } }); - + /** * Event -> onPlayerTouchsMe(npc, player) */ env.setCallBack("npc.playertouchsme", function (npc, player) { try { - if (npc.onPlayerTouchsMe) - npc.onPlayerTouchsMe(player); + callEvent(npc, player, "onPlayerTouchsMe", player); } catch (e) { env.reportException("NPC Exception at " + npc.levelname + "," + npc.x + "," + npc.y + ": " + e.name + " - " + e.message); } @@ -79,8 +170,7 @@ */ env.setCallBack("npc.playerlogin", function (npc, player) { try { - if (npc.onPlayerLogin) - npc.onPlayerLogin(player); + callEvent(npc, player, "onPlayerLogin", player); } catch (e) { env.reportException(npc.name + " Exception at onPlayerLogin: " + e.name + " - " + e.message); } @@ -91,8 +181,7 @@ */ env.setCallBack("npc.playerlogout", function (npc, player) { try { - if (npc.onPlayerLogout) - npc.onPlayerLogout(player); + callEvent(npc, player, "onPlayerLogout", player); } catch (e) { env.reportException(npc.name + " Exception at onPlayerLogout: " + e.name + " - " + e.message); } @@ -115,13 +204,25 @@ */ env.setCallBack("npc.warped", function (npc, ...args) { try { - if (npc.onNpcWarped) - npc.onNpcWarped.apply(npc, args); + callEvent(npc, player, "onNpcWarped", ...args); } catch (e) { env.reportException("NPC Warped Exception at " + npc.levelname + "," + npc.x + "," + npc.y + ": " + e.name + " - " + e.message); } }); + env.setCallBack("npc.triggeron", function (npc, evName, player, data) { + try { + if (npc[evName]) { + const params = tokenize(data, ','); + npc[evName].apply(npc, params); + } else { + env.reportException("Unknown event: " + evName); + } + } catch (e) { + env.reportException("NPC Trigger Exception at " + npc.levelname + "," + npc.x + "," + npc.y + ": " + e.name + " - " + e.message); + } + }); + /* * Event -> Triggeractions */ @@ -201,11 +302,11 @@ } } } - + stringList.push(currentString); return stringList; }; - + // Math helper functions (function() { const _intVecX = [0, -1, 0, 1]; @@ -214,7 +315,7 @@ env.global.vecx = function(dir) { return _intVecX[dir % 4]; }; - + env.global.vecy = function(dir) { return _intVecY[dir % 4]; }; diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index daa8baf54..14d557303 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -125,6 +125,7 @@ if(V8NPCSERVER) src/Scripting/v8/V8NPCImpl.cpp src/Scripting/v8/V8PlayerImpl.cpp src/Scripting/v8/V8ScriptEnv.cpp + src/Scripting/v8/V8ScriptWrappers.cpp src/Scripting/v8/V8ServerImpl.cpp src/Scripting/v8/V8WeaponImpl.cpp ) diff --git a/server/include/Scripting/v8/V8ScriptWrappers.h b/server/include/Scripting/v8/V8ScriptWrappers.h index 2e564f208..fa9be102c 100644 --- a/server/include/Scripting/v8/V8ScriptWrappers.h +++ b/server/include/Scripting/v8/V8ScriptWrappers.h @@ -3,61 +3,15 @@ #ifndef V8SCRIPTWRAPPERS_H #define V8SCRIPTWRAPPERS_H -#include #include -#include -#include -const std::regex word_regex(R"((public[\s]+function[\s]+){1}(\w+)[\s]*\()"); +enum V8ScriptWrapperFlags { + EMIT_NONE = 0, + EMIT_SCOPE = 1, + EMIT_CLASS = 2 +}; -//TODO: Move to CPP-file -inline std::string getPublicFunctions(const std::string_view& code) -{ - std::vector eventList; - - std::string s(code); - - auto words_begin = std::sregex_iterator(s.begin(), s.end(), word_regex); - auto words_end = std::sregex_iterator(); - - for (std::sregex_iterator i = words_begin; i != words_end; ++i) { - std::smatch match = *i; - - std::string match_str = match.str(); - if (match.size() == 3) - eventList.push_back(match[2]); - } - - std::string varNames = ""; - std::string varNameQuotes = ""; - for (const auto eventName : eventList) { - varNames += eventName + ","; - varNameQuotes += "\"" + eventName + "\"" + ","; - } - if (!varNames.empty()) { - varNames.pop_back(); - varNameQuotes.pop_back(); - } - - std::string out; - if (!varNames.empty()) - { - out += "const __publicNames = [" + varNameQuotes + "];\n"; - out += "const __publicFuncs = [" + varNames + "];\n"; - out += R"( - for (let i = 0; i < __publicNames.length; ++i) { - if (__publicFuncs[i]) { - //print("Found fn ", __publicNames[i]); - self[__publicNames[i]] = __publicFuncs[i]; - } else { - //print("Not found fn ", __publicNames[i]); - } - } - )"; - } - - return out; -} +std::string prepareScript(const std::string& code, uint32_t scriptFlags = EMIT_NONE); template inline std::string WrapScript(const std::string& code) { @@ -72,33 +26,28 @@ inline std::string WrapScript(const std::string_view& code) { class TNPC; template <> inline std::string WrapScript(const std::string_view& code) { - // self.onCreated || onCreated, for first declared to take precedence - // if (onCreated) for latest function to override static const char* prefixString = "(function(npc) {" \ - "var onCreated, onTimeout, onNpcWarped, onPlayerChats, onPlayerEnters, onPlayerLeaves, onPlayerTouchsMe, onPlayerLogin, onPlayerLogout;" \ - "const self = npc;" \ - "if (onCreated) self.onCreated = onCreated;" \ - "if (onTimeout) self.onTimeout = onTimeout;" \ - "if (onNpcWarped) self.onNpcWarped = onNpcWarped;" \ - "if (onPlayerChats) self.onPlayerChats = onPlayerChats;" \ - "if (onPlayerEnters) self.onPlayerEnters = onPlayerEnters;" \ - "if (onPlayerLeaves) self.onPlayerLeaves = onPlayerLeaves;" \ - "if (onPlayerTouchsMe) self.onPlayerTouchsMe = onPlayerTouchsMe;" \ - "if (onPlayerLogin) self.onPlayerLogin = onPlayerLogin;" \ - "if (onPlayerLogout) self.onPlayerLogout = onPlayerLogout;" \ - "\n"; - + "const self = npc;\n"; + std::string wrappedCode = std::string(prefixString); + wrappedCode.append(prepareScript(std::string{ code }, EMIT_SCOPE)); + wrappedCode.append("\n});"); + return wrappedCode; +} - std::string publicFunctions = getPublicFunctions(code); - std::string fixedCode = std::regex_replace(std::string(code), word_regex, "function $2("); +class TScriptClass; +template <> +inline std::string WrapScript(const std::string_view& code) { + static const char* prefixString = "(function(npc) {" \ + "const self = npc;\n"; - wrappedCode.append(fixedCode); - wrappedCode.append(publicFunctions); + std::string wrappedCode = std::string(prefixString); + wrappedCode.append(prepareScript(std::string{ code }, EMIT_SCOPE | EMIT_CLASS)); wrappedCode.append("\n});"); return wrappedCode; } + class TPlayer; template <> inline std::string WrapScript(const std::string_view& code) { @@ -106,12 +55,7 @@ inline std::string WrapScript(const std::string_view& code) { "const self = player;\n"; std::string wrappedCode = std::string(prefixString); - - std::string publicFunctions = getPublicFunctions(code); - std::string fixedCode = std::regex_replace(std::string(code), word_regex, "function $2("); - - wrappedCode.append(fixedCode); - wrappedCode.append(publicFunctions); + wrappedCode.append(prepareScript(std::string{ code }, EMIT_CLASS)); wrappedCode.append("\n});"); return wrappedCode; } @@ -120,18 +64,10 @@ class TWeapon; template <> inline std::string WrapScript(const std::string_view& code) { static const char* prefixString = "(function(weapon) {" \ - "var onCreated, onActionServerSide;" \ - "const self = weapon;" \ - "self.onCreated = onCreated;" \ - "self.onActionServerSide = onActionServerSide;\n"; + "const self = weapon;\n"; std::string wrappedCode = std::string(prefixString); - - std::string publicFunctions = getPublicFunctions(code); - std::string fixedCode = std::regex_replace(std::string(code), word_regex, "function $2("); - - wrappedCode.append(fixedCode); - wrappedCode.append(publicFunctions); + wrappedCode.append(prepareScript(std::string{ code }, EMIT_SCOPE)); wrappedCode.append("\n});"); return wrappedCode; } diff --git a/server/include/TNPC.h b/server/include/TNPC.h index 0e6b2e374..a084dcb43 100644 --- a/server/include/TNPC.h +++ b/server/include/TNPC.h @@ -9,6 +9,7 @@ #ifdef V8NPCSERVER #include +#include #include #include #include "ScriptAction.h" @@ -288,6 +289,7 @@ class TNPC // bool hasScriptEvent(int flag) const; void setScriptEvents(int mask); + void setScriptEvents(const std::set& eventList); ScriptExecutionContext& getExecutionContext(); IScriptObject * getScriptObject() const; diff --git a/server/src/Scripting/CScriptEngine.cpp b/server/src/Scripting/CScriptEngine.cpp index bc9e6c6e1..9945f8eba 100644 --- a/server/src/Scripting/CScriptEngine.cpp +++ b/server/src/Scripting/CScriptEngine.cpp @@ -64,7 +64,12 @@ bool CScriptEngine::Initialize() // Create a new context (occurs on initial compile) _bootstrapFunction = _env->Compile("bootstrap", bootstrapScript.text()); - assert(_bootstrapFunction); + if (!_bootstrapFunction) + { + _server->getScriptLog().append("Failed to compile bootstrap script"); + reportScriptException(_env->getScriptError()); + return false; + } // Bind the server into two separate objects _environmentObject = ScriptFactory::WrapObject(_env, "environment", _server); diff --git a/server/src/Scripting/GS2ScriptManager.cpp b/server/src/Scripting/GS2ScriptManager.cpp index a32ad63ce..ff0149bdb 100644 --- a/server/src/Scripting/GS2ScriptManager.cpp +++ b/server/src/Scripting/GS2ScriptManager.cpp @@ -1,6 +1,6 @@ #include "GS2ScriptManager.h" -const uint32_t THREADPOOL_WORKERS = 4; +const uint32_t THREADPOOL_WORKERS = 0; GS2ScriptManager::GS2ScriptManager() : _compilerThreadPool(THREADPOOL_WORKERS) diff --git a/server/src/Scripting/v8/V8EnvironmentImpl.cpp b/server/src/Scripting/v8/V8EnvironmentImpl.cpp index 82b5ec1e6..fc11936b1 100644 --- a/server/src/Scripting/v8/V8EnvironmentImpl.cpp +++ b/server/src/Scripting/v8/V8EnvironmentImpl.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "CScriptEngine.h" #include "TNPC.h" @@ -96,6 +97,30 @@ void Environment_SetNpcEvents(const v8::FunctionCallbackInfo& args) npcObject->setScriptEvents(args[1]->Int32Value(context).ToChecked()); } } + else if (args[0]->IsObject() && args[1]->IsArray()) + { + v8::Local context = args.GetIsolate()->GetCurrentContext(); + v8::Local obj = args[0]->ToObject(context).ToLocalChecked(); + + std::string npcConstructor = *v8::String::Utf8Value(isolate, obj->GetConstructorName()); + if (npcConstructor == "npc") + { + auto arrayobj = v8::Local::Cast(args[1]); + + std::set vec; + for (auto i = 0; i < arrayobj->Length(); i++) { + auto arrayValue = arrayobj->Get(context, i); + if (!arrayValue.IsEmpty()) + { + auto arrayStrValue = arrayValue.ToLocalChecked()->ToString(context).ToLocalChecked(); + vec.insert(*v8::String::Utf8Value(isolate, arrayStrValue)); + } + } + + TNPC *npcObject = UnwrapObject(obj); + npcObject->setScriptEvents(vec); + } + } SCRIPTENV_D("End Environment::setNpcEvents()\n\n"); } diff --git a/server/src/Scripting/v8/V8NPCImpl.cpp b/server/src/Scripting/v8/V8NPCImpl.cpp index 43d3c427c..b64b6c1f5 100644 --- a/server/src/Scripting/v8/V8NPCImpl.cpp +++ b/server/src/Scripting/v8/V8NPCImpl.cpp @@ -965,13 +965,13 @@ void NPC_Function_Join(const v8::FunctionCallbackInfo& args) CScriptEngine *scriptEngine = static_cast(data->Value()); std::string className = *v8::String::Utf8Value(isolate, args[0]->ToString(context).ToLocalChecked()); - +// printf("Joining class: %s\n", className.c_str()); V8ENV_SAFE_UNWRAP(args, TNPC, npcObject); TScriptClass *classObject = npcObject->joinClass(className); if (classObject != nullptr) { - auto classCodeWrap = WrapScript(classObject->getSource().getServerSide()); + auto classCodeWrap = WrapScript(classObject->getSource().getServerSide()); auto scriptFunction = scriptEngine->CompileCache(classCodeWrap, false); if (scriptFunction != nullptr) @@ -985,54 +985,59 @@ void NPC_Function_Join(const v8::FunctionCallbackInfo& args) v8::MaybeLocal scriptTableRet = localFunction->Call(context, args.This(), 1, newArgs); if (!scriptTableRet.IsEmpty()) { - args.GetReturnValue().Set(scriptTableRet.ToLocalChecked()); + auto joinClassCb = scriptEngine->getCallBack("npc.joinclass"); + V8ScriptFunction* v8_function2 = static_cast(joinClassCb); + v8::Local newArgs2[] = { args.This(), args[0], scriptTableRet.ToLocalChecked() }; + v8::Local localFunction2 = v8_function2->Function(); + v8::MaybeLocal scriptTableRet2 = localFunction2->Call(context, args.This(), 3, newArgs2); + if (scriptTableRet2.IsEmpty()) { + // Failed to execute callback, should we leave this class. + // Bootstrap shouldnt fail, generally... + printf("---------FAILED to execute joinclass"); + } + args.GetReturnValue().Set(args.This()); return; } } } + } +} + +// NPC Function: NPC.leave("class"); +void NPC_Function_Leave(const v8::FunctionCallbackInfo& args) +{ + v8::Isolate* isolate = args.GetIsolate(); - //TServer *server = scriptEngine->getServer(); - //auto classObj = server->getClass(className); - - //if (classObj && !classObj->source().empty()) - //{ - // V8ENV_SAFE_UNWRAP(args, TNPC, npcObject); - - // // Split the code - // std::string serverCode = classObj->serverCode(); - // std::string clientCode = classObj->clientCode(); - - // //auto currentClass = server->getClassObject(className); - // //if (currentClass == nullptr) - // // currentClass = server->addClass(className, clientCode); - // //npcObject->addClassCode(className, clientCode); - // // Add class to npc - // //npcObject->addClassCode(className, clientCode); - // - // // Wrap code - // std::string classCodeWrap = CScriptEngine::WrapScript(serverCode); - - // // TODO(joey): maybe we shouldn't cache this using this method, since classes can be used with - // // multiple wrappers. - // IScriptFunction *function = scriptEngine->CompileCache(classCodeWrap, false); - // if (function == nullptr) - // return; - - // V8ScriptFunction *v8_function = static_cast(function); - // v8::Local newArgs[] = { args.This() }; - - // // Execute - // v8::TryCatch try_catch(isolate); - // v8::Local scriptFunction = v8_function->Function(); - // v8::MaybeLocal scriptTableRet = scriptFunction->Call(context, args.This(), 1, newArgs); - // if (!scriptTableRet.IsEmpty()) - // { - // args.GetReturnValue().Set(scriptTableRet.ToLocalChecked()); - // return; - // } - - // // TODO(joey): error handling - //} + V8ENV_THROW_CONSTRUCTOR(args, isolate); + V8ENV_THROW_ARGCOUNT(args, isolate, 1); + + if (args[0]->IsString()) + { + v8::Local context = isolate->GetCurrentContext(); + v8::Local data = args.Data().As(); + CScriptEngine *scriptEngine = static_cast(data->Value()); + + std::string className = *v8::String::Utf8Value(isolate, args[0]->ToString(context).ToLocalChecked()); +// printf("Leaving class: %s\n", className.c_str()); + V8ENV_SAFE_UNWRAP(args, TNPC, npcObject); + + if (npcObject->joinedClass(className)) { + // Execute + auto joinClassCb = scriptEngine->getCallBack("npc.leaveclass"); + + v8::TryCatch try_catch(isolate); + V8ScriptFunction* v8_function = static_cast(joinClassCb); + v8::Local newArgs[] = { args.This(), args[0] }; + v8::Local localFunction = v8_function->Function(); + v8::MaybeLocal scriptTableRet = localFunction->Call(context, args.This(), 2, newArgs); + if (scriptTableRet.IsEmpty()) { + // Failed to execute callback, should we leave this class. + // Bootstrap shouldnt fail, generally... + printf("---------FAILED to execute leaveclass\n"); + } + args.GetReturnValue().Set(args.This()); + return; + } } } @@ -1529,6 +1534,7 @@ void bindClass_NPC(CScriptEngine *scriptEngine) npc_proto->Set(v8::String::NewFromUtf8Literal(isolate, "warpto"), v8::FunctionTemplate::New(isolate, NPC_Function_Warpto, engine_ref)); // warpto levelname,x,y; npc_proto->Set(v8::String::NewFromUtf8Literal(isolate, "join"), v8::FunctionTemplate::New(isolate, NPC_Function_Join, engine_ref)); + npc_proto->Set(v8::String::NewFromUtf8Literal(isolate, "leave"), v8::FunctionTemplate::New(isolate, NPC_Function_Leave, engine_ref)); npc_proto->Set(v8::String::NewFromUtf8Literal(isolate, "registerTrigger"), v8::FunctionTemplate::New(isolate, NPC_Function_RegisterTrigger, engine_ref)); npc_proto->Set(v8::String::NewFromUtf8Literal(isolate, "setpm"), v8::FunctionTemplate::New(isolate, NPC_Function_SetPM, engine_ref)); npc_proto->Set(v8::String::NewFromUtf8Literal(isolate, "scheduleevent"), v8::FunctionTemplate::New(isolate, NPC_Function_ScheduleEvent, engine_ref)); diff --git a/server/src/TNPC.cpp b/server/src/TNPC.cpp index c706d5e78..9c238efac 100644 --- a/server/src/TNPC.cpp +++ b/server/src/TNPC.cpp @@ -964,6 +964,34 @@ void TNPC::setTimeout(int newTimeout) server->getScriptEngine()->UnregisterNpcTimer(this); } +int getScriptEventFlag(const std::string& str) +{ + static const std::unordered_map eventValues = + { + { "onCreated", NPCEVENTFLAG_CREATED }, + { "onTimeout", NPCEVENTFLAG_TIMEOUT }, + { "onPlayerChats", NPCEVENTFLAG_PLAYERCHATS }, + { "onPlayerEnters", NPCEVENTFLAG_PLAYERENTERS }, + { "onPlayerLeaves", NPCEVENTFLAG_PLAYERLEAVES }, + { "onPlayerTouchsMe", NPCEVENTFLAG_PLAYERTOUCHSME }, + { "onPlayerLogin", NPCEVENTFLAG_PLAYERLOGIN }, + { "onPlayerLogout", NPCEVENTFLAG_PLAYERLOGOUT }, + { "onNpcWarped", NPCEVENTFLAG_NPCWARPED }, + }; + + auto iter = eventValues.find(str); + return iter == eventValues.end() ? 0 : iter->second; +} + +void TNPC::setScriptEvents(const std::set& eventList) +{ + auto mask = 0u; + for (const auto& v : eventList) + mask |= getScriptEventFlag(v); + + setScriptEvents(mask); +} + void TNPC::queueNpcAction(const std::string& action, TPlayer *player, bool registerAction) { assert(_scriptObject); diff --git a/server/src/TScriptClass.cpp b/server/src/TScriptClass.cpp index 56c424404..cb66cc7e4 100644 --- a/server/src/TScriptClass.cpp +++ b/server/src/TScriptClass.cpp @@ -34,17 +34,6 @@ void TScriptClass::parseScripts(TServer *server, const std::string& classSource) _bytecode.clear(bytecodeWithHeader.length()); _bytecode.write((const char*)bytecodeWithHeader.buffer(), bytecodeWithHeader.length()); - - // temp: save bytecode to file - //CString bytecodeFile; - //bytecodeFile << _server->getServerPath() << "bytecode/classes/"; - //std::filesystem::create_directories(bytecodeFile.text()); - //bytecodeFile << "class_" << _className << ".gs2bc"; - - //CString bytecodeDump; - //bytecodeDump.writeInt(1); - //bytecodeDump.write((const char*)bytecodeWithHeader.buffer(), bytecodeWithHeader.length()); - //bytecodeDump.save(bytecodeFile); } }); } diff --git a/server/src/TServer.cpp b/server/src/TServer.cpp index 7f5a0c3d5..0f1582f59 100644 --- a/server/src/TServer.cpp +++ b/server/src/TServer.cpp @@ -603,11 +603,8 @@ int TServer::loadConfigFiles() loadAllowedVersions(); // Load folders config and file system. - serverlog.out("[%s] Folder config: ", name.text()); - if ( !settings.getBool("nofoldersconfig", false)) - { - serverlog.append("ENABLED\n"); - } else serverlog.append("disabled\n"); + serverlog.out("[%s] Folder config: %s", name.text(), settings.getBool("nofoldersconfig", false) ? "DISABLED" : "ENABLED"); + serverlog.out("[%s] Loading file system...\n", name.text()); loadFileSystem(); From 88faf1d7f2d1e16ed1fda076b0d3276e2103dad9 Mon Sep 17 00:00:00 2001 From: joey <1166538+xtjoeytx@users.noreply.github.com> Date: Mon, 5 Dec 2022 22:57:45 -0500 Subject: [PATCH 2/3] you were supposed to be included! --- server/src/Scripting/v8/V8ScriptWrappers.cpp | 131 +++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 server/src/Scripting/v8/V8ScriptWrappers.cpp diff --git a/server/src/Scripting/v8/V8ScriptWrappers.cpp b/server/src/Scripting/v8/V8ScriptWrappers.cpp new file mode 100644 index 000000000..fa420ddd4 --- /dev/null +++ b/server/src/Scripting/v8/V8ScriptWrappers.cpp @@ -0,0 +1,131 @@ +#include +#include +#include +#include "V8ScriptWrappers.h" + +const std::regex word_regex(R"(((public[\s]+)?function[\s]+){1}(\w+)[\s]*\()"); + +struct FunctionMetaData +{ + bool isPublic = false; + bool isEvent = false; + std::string fnName; +}; + +std::string make_js_hash_code(const std::string& str) +{ + // "onCreated": onCreated + std::string out; + out.reserve(4 + str.length() * 2); + out.append("\"").append(str).append("\": ").append(str); + return out; +} + +std::unordered_map getFunctionList(const std::string& code) +{ + std::unordered_map fns; + auto words_begin = std::sregex_iterator(code.begin(), code.end(), word_regex); + auto words_end = std::sregex_iterator(); + + for (std::sregex_iterator i = words_begin; i != words_end; ++i) + { + const std::smatch& match = *i; + if (match.size() == 4) + { + auto matchFnName = match[3].str(); + if (!matchFnName.empty()) + { + bool isPub = (match[2].compare("public") == 1); + bool isEvent = (matchFnName.starts_with("on")); + + fns[matchFnName] = { + isPub || fns[matchFnName].isPublic, + isEvent || fns[matchFnName].isEvent, + matchFnName + }; + } + } + } + + return fns; +} + +std::pair getScriptTables(const std::string& code) +{ + auto fns = getFunctionList(code); + + std::string eventTable; + std::string publicTable; + + for (const auto& [fnName, fn] : fns) + { + if (fn.isPublic) + { + if (!publicTable.empty()) + publicTable.append(",\n"); + publicTable.append("\t").append(make_js_hash_code(fnName)); + } + + if (fn.isEvent) + { + if (!eventTable.empty()) + eventTable.append(",\n"); + eventTable.append("\t").append(make_js_hash_code(fnName)); + } + } + + return std::make_pair(eventTable, publicTable); +} + +std::string prepareScript(const std::string& code, uint32_t flags) +{ + std::string newCode; + newCode.reserve(code.length() + 1024); + + if (flags & V8ScriptWrapperFlags::EMIT_SCOPE) + { + newCode += R"( + var player; + const __setScope = function(value) { + player = value; + }; + )"; + } + else + { + // Require something, not doing checks on callback. Can't keep player global + // as player-class has that variable although no events should really flow that way + newCode += "const __setScope = function(value) {};"; + } + + // Emit script table + auto scriptTables = getScriptTables(code); + newCode += "const __eventFunctions = {\n" + + scriptTables.first + "\n};\n" + + +"const __publicFunctions = {\n" + + scriptTables.second + "\n};\n"; + + newCode += R"( + const __getScriptTable = () => ({ + 'events': __eventFunctions, + 'public': __publicFunctions, + 'scope': __setScope + }); + )"; + + // Replace public functions, and emit the code + newCode += std::regex_replace(std::string(code), word_regex, "function $3("); + + // PLAYER / NPC / WEAPONS: + // Don't want classes overriding via self + if (!(flags & V8ScriptWrapperFlags::EMIT_CLASS)) + { + newCode += "self.__main = __getScriptTable();"; + } + + // Classes require this, an event will be dispatched to the bootstrap file using + // callback npc.joincls with this script table so we can reconstruct the event-flow + newCode += "return __getScriptTable();"; + + return newCode; +} From b06a2d93f25ec9578468b66bbba957d54c04fd4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AD=BB=E4=BD=93?= Date: Mon, 4 Sep 2023 21:54:13 +0200 Subject: [PATCH 3/3] Update gs2lib submodule --- dependencies/gs2lib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies/gs2lib b/dependencies/gs2lib index 11361958c..2a6e87b82 160000 --- a/dependencies/gs2lib +++ b/dependencies/gs2lib @@ -1 +1 @@ -Subproject commit 11361958cd14eb17e4ab9bad3a6bd6af6607abf9 +Subproject commit 2a6e87b82894b6cfa80149273cce83768ac0d3c3