#include #include #include #include #include #include #include enum threadstatus{ THREAD_NULL, THREAD_RUNNING, THREAD_FINISHED, THREAD_ERROR, }; struct context{ int status; void* contextaddr; char* param; int paramlen; char* func; int funclen; char* out; int outlen; dmThread::Thread threadthread; }; static const char* reader(lua_State* L, void* data, size_t* size) { if (lua_isstring(L, -1)) { const char* str = lua_tostring(L, -1); *size = lua_strlen(L, -1); // Get the length of the string lua_pop(L, 1); // Pop the string from the stack return str; // Return the chunk of Lua code } return nullptr; // Signal the end of the code } static std::vector dumpluadata(lua_State* L){ //dumps the first element on the stack so that it may be loaded later. std::vector output; const char* start="return "; output.insert(output.end(),start,start+strlen(start)); if(lua_isnil(L,-1)){ //nil is easy. const char* o="nil"; output.insert(output.end(),o,o+strlen(o)); lua_remove(L,-1); //and remove from the stack. }else if(lua_isnumber(L,-1) && lua_type(L,-1)!=LUA_TSTRING ){ //this is some mickey mouse shit. float num=lua_tonumber(L,-1); if (num==1.0/0.0){ //infinity const char* o="1/0"; output.insert(output.end(),o,o+strlen(o)); }else if (num==-1.0/0.0){ //negative infinity const char* o="-1/0"; output.insert(output.end(),o,o+strlen(o)); }else if (std::isnan(num) ){ //NaN const char* o="0/0"; output.insert(output.end(),o,o+strlen(o)); }else{ //actual numbers. const char* str=std::to_string(num).c_str(); //lua_tostring(L,-1); output.insert(output.end(),str,str+strlen(str)); } lua_remove(L,-1); }else if(lua_isboolean(L,-1)){ if (lua_toboolean(L,-1)){ const char* o="true"; output.insert(output.end(),o,o+strlen(o)); lua_remove(L,-1); }else{ const char* o="false"; output.insert(output.end(),o,o+strlen(o)); lua_remove(L,-1); } }else if (lua_isstring(L,-1)){ dmLogInfo("walkin1"); const char* backslash="\\"; const char* backslash2="\\\\"; const char* quote="\""; const char* quote2="\\\""; const char* carrigereturn="\r"; const char* carrigereturn2="\\r"; const char* newline="\n"; const char* newline2="\\n"; const char* str=lua_tostring(L,-1); const char* str2=luaL_gsub(L,str,backslash,backslash2); lua_pop(L,1); const char* str3=luaL_gsub(L,str2,quote,quote2); lua_pop(L,1); const char* str4=luaL_gsub(L,str3,carrigereturn,carrigereturn2); lua_pop(L,1); const char* str5=luaL_gsub(L,str4,newline,newline2); lua_pop(L,1); output.push_back('\"'); output.insert(output.end(),str5,str5+strlen(str5)); output.push_back('\"'); lua_remove(L,-1); }else if( lua_istable(L,-1)){ output.push_back('{'); //current stack: table (TOP) lua_pushnil(L); //current stack: table, nil (TOP) while (lua_next(L, -2) != 0) { //current stack: table, key, value (TOP) lua_insert(L,2);//current stack: table, value, key (TOP) lua_pushvalue(L,-1); //because dumpluadata pops the top of the stack, copy it. output.push_back('['); const char* indexstr=dumpluadata(L).data(); output.insert(output.end(),indexstr,indexstr+strlen(indexstr)); output.push_back(']'); output.push_back('='); lua_insert(L,2);//current stack: table, key, value (TOP) const char* valuestr=dumpluadata(L).data(); output.insert(output.end(),valuestr,valuestr+strlen(valuestr)); output.push_back(','); } lua_remove(L,-1);//current stack: table (TOP) output.push_back('}'); } return output; } static void execthread(void* _ctx){ //the thing that is executed in a thread. dmLogInfo("oh1?"); context* ctx = (context*)_ctx; dmLogInfo(ctx->param); dmLogInfo( std::to_string(ctx->paramlen).c_str() ); lua_State* L = luaL_newstate(); dmLogInfo("oh2?"); lua_pushlstring(L,ctx->func,ctx->funclen); //stack: (1) funcstr (top) int status = lua_load(L,reader, nullptr,"chunk");//stack: (1) function (top) if (status != 0) { lua_settop(L,0); //clear the stack lua_close(L); ctx->status = THREAD_ERROR; dmLogError("Unable to load thread function!"); return; //if it fails to compile, just give up. } dmLogInfo("oh3?"); lua_pushlstring(L,ctx->param,ctx->paramlen); //stack: (1) funcstr, argstr (top) status = lua_load(L,reader, nullptr,NULL);//stack: (1) function, args (top) dmLogInfo(ctx->param); dmLogInfo( std::to_string(ctx->paramlen).c_str() ); if (status != 0) { dmLogWarning("Unable to load thread arg!"); lua_pop(L,-1); //clear the args, we can push nul into a function lua_pushnil(L); } dmLogInfo("oh4?"); //stack: (1) funcstr function (top) lua_call(L,1,1); //we are calling that function now. only 1 arg in, so you better make it count. dmLogInfo("oh5?"); static std::vector returndata=dumpluadata(L); ctx->out=returndata.data(); ctx->outlen=returndata.size(); dmLogInfo("oh6?"); lua_settop(L,0); lua_close(L); ctx->status = THREAD_FINISHED; dmLogInfo("oh7?"); } static int writer(lua_State* L, const void* p, size_t sz, void* data) { std::vector* buffer = static_cast*>(data); const char* charP = static_cast(p); buffer->insert(buffer->end(), charP, charP + sz); return 0; } int newthread(lua_State* L) //creates a thread { if(lua_gettop(L)<1 || ( !( lua_isfunction(L,1) ) )){ //we need a function as the argument return luaL_error(L,"Expected Lua function as argument #1"); } if(lua_gettop(L)<2 ){ //push nil to #2 in the stack if we don't have the arg. lua_pushnil(L); } if(lua_gettop(L)>2 ){ //ensure we only have 2 args to work with to make our lives easier. lua_pop(L,lua_gettop(L)-2); } lua_insert(L,1); //move the second paramater to the first place in the stack so that the function is on top std::vector funcbuffer; lua_dump(L,writer,&funcbuffer); //we need it on top to dump it lua_insert(L,1);//then move so that the second parameter (the data to be passed) is now on top //const char* preamble= "return "; std::vector paramdat = dumpluadata(L); //paramdat.insert(paramdat.begin(), preamble, preamble + strlen(preamble) ); lua_pop(L,2); //then pop the stack so that it is empty context ctx; //make a new context ctx.func=funcbuffer.data(); //give it the function to execute ctx.funclen=funcbuffer.size(); dmLogInfo(paramdat.data()); dmLogInfo( std::to_string(paramdat.size()).c_str() ); ctx.param=paramdat.data(); //and send it the relevent data. ctx.paramlen=paramdat.size(); dmLogInfo("mhm1."); ctx.status=THREAD_RUNNING; //status indicators. ctx.contextaddr=&ctx; //why not? dmThread::Thread thread = dmThread::New(execthread, 0x80000, (void*)&ctx, "defoldthread"); dmLogInfo("mhm2."); ctx.threadthread=thread; //and store the thread for passing to other functions dmLogInfo("mhm3."); context** ud = (context**)lua_newuserdata(L, sizeof(context*)); //theifery from AI *ud = &ctx; //didn't originally include the &, but the compiler says jump, i say how high. dmLogInfo("mhm4."); return 1; } int threadstatus(lua_State* L){ //returns if a thead is running or complete if(lua_gettop(L)<1 || ( !lua_isuserdata(L,1) )) { //we need a function as the argument return luaL_error(L,"Expected threaddata as argument"); } context* threaddata = (context*)lua_touserdata(L, 1); lua_pushinteger(L,threaddata->status); return 1; } static const luaL_Reg module_methods[] = { //build the module {"newthread", newthread}, {"yield", yieldthread}, {"status",threadstatus}, {NULL, NULL} }; static void LuaInit(lua_State* L) //put these functions into _G { luaL_register(L,"tedding",module_methods); lua_pop(L, 1); lua_getglobal(L,"tedding"); //luaL_register only does functions. we have to declare enums via table assignment lua_pushinteger(L,THREAD_NULL); lua_setfield(L,-2,"THREAD_NULL"); lua_pushinteger(L,THREAD_RUNNING); lua_setfield(L,-2,"THREAD_RUNNING"); lua_pushinteger(L,THREAD_FINISHED); lua_setfield(L,-2,"THREAD_FINISHED"); lua_pushinteger(L,THREAD_ERROR); lua_setfield(L,-2,"THREAD_ERROR"); lua_pop(L, 1); } dmExtension::Result INIT(dmExtension::Params* params){ //init function to declare the lib LuaInit(params->m_L); return dmExtension::RESULT_OK; } DM_DECLARE_EXTENSION(multitedding,"multitedding",NULL,NULL,INIT,NULL,NULL,NULL); //this actually gives it to the engine