diff --git a/luprex/core/cpp/world-accessor.cpp b/luprex/core/cpp/world-accessor.cpp index 1030d0e0..da680435 100644 --- a/luprex/core/cpp/world-accessor.cpp +++ b/luprex/core/cpp/world-accessor.cpp @@ -292,6 +292,128 @@ LuaDefine(tangible_scan, "plane,x,y,radius,omit_nowhere", return LS.result(); } +LuaDefine(tangible_start, "tangible,function,arg1,arg2...", + "|Start a thread." + "|" + "|Every thread is owned by a tangible. The first argument" + "|to 'tangible.start' indicates the tangible that owns" + "|the new thread." + "|" + "|The function can be a lua closure, or it can be a string." + "|If it's a string, then the tangible's class will be" + "|used to look up the relevant closure." + "|" + "|The arguments arg1,arg2... will be passed to the" + "|function." + "|" + "|Actor and place aren't passed to the function unless" + "|you manually include them in the list arg1, arg2, etc." + "|The new thread can, however, use the builtin " + "|functions 'tangible.actor' and 'tangible.place' to obtain " + "|actor and place. Actor will be the same actor who " + "|called 'tangible.start'. Place will be the tangible that" + "|owns the thread, ie, the tangible passed to 'tangible.start'." + "|" + "|The new thread doesn't start running instantly:" + "|it waits until the current thread is finished. The" + "|current thread must either block (eg, 'wait') or terminate" + "|before the new thread can actually begin execution." + "|" + "|If you start a thread, then start another, then both of" + "|the newly-started threads wait until the current thread" + "|is finished. At that point, it is undefined which of the" + "|two new threads runs first." + "|" + "|Threads are owned by tangibles. If a tangible is" + "|deleted, then none of its threads will ever be resumed," + "|for any reason." + "|" + "|However, if a thread deletes its own place (ie, the tangible" + "|that owns the thread), then the thread will be allowed" + "|to continue running until it blocks. But from that point" + "|forward, the thread will never be resumed for any reason.") { + + int top = lua_gettop(L); + if (top < 2) { + luaL_error(L, "Not enough arguments to tangible.start"); + return 0; + } + int varlen = top - 2; + + World *w = World::fetch_global_pointer(L); + w->guard_nopredict(L, "tangible.start"); + + LuaVar mt, classtab, plthreads, thread, thinfo; + LuaStack LS(L, mt, classtab, plthreads, thread, thinfo); + LuaSpecial place(1); + LuaSpecial func(2); + + // Confirm that the place is a valid tangible, + // and get the tangible ID. + w->tangible_get(LS, place); + int64_t place_id = LS.tanid(place); + + // Get place's metatable. + LS.getmetatable(mt, place); + if (!LS.istable(mt)) { + luaL_error(L, "invalid tangible passed to tangible.start"); + return 0; + } + + // Get place's threads table. + LS.rawget(plthreads, mt, "threads"); + if (!LS.istable(plthreads)) { + luaL_error(L, "invalid tangible passed to tangible.start"); + return 0; + } + + // If the function is actually a function-name, + // then convert it to a closure. + if (!LS.isfunction(func)) { + if (!LS.isstring(func)) { + luaL_error(L, "invalid function passed to tangible.start"); + return 0; + } + LS.rawget(classtab, mt, "__index"); + if (!LS.istable(classtab)) { + luaL_error(L, "tangible doesn't have a class table in tangible.start"); + return 0; + } + LS.rawget(func, classtab, func); + if (!LS.isfunction(func)) { + luaL_error(L, "tangible doesn't have specified method in tangible.start"); + return 0; + } + } + + // Get a thread ID for the new thread + int64_t tid = w->alloc_id_predictable(); + + // Create a new thread, set up function and arguments. + lua_State *CO = LS.newthread(thread); + lua_pushvalue(L, func.index()); + for (int i = 0; i < varlen; i++) { + lua_pushvalue(L, i + 3); + } + lua_xmove(L, CO, varlen + 1); + + // Create the thread info table. + LS.newtable(thinfo); + LS.rawset(thinfo, "thread", thread); + LS.rawset(thinfo, "actorid", w->lthread_actor_id_); + LS.rawset(thinfo, "isnew", true); + LS.rawset(thinfo, "useppool", false); + LS.rawset(thinfo, "print", false); + + LS.rawset(plthreads, tid, thinfo); + LS.result(); + + // Push the thread's ID into the runnable thread queue. + w->schedule(0, tid, place_id); + return LS.result(); +} + + LuaDefine(wait, "nticks", "|Wait the specified number of ticks.") { World *w = World::fetch_global_pointer(L); diff --git a/luprex/core/cpp/world.hpp b/luprex/core/cpp/world.hpp index fd6260b4..4eac0ca4 100644 --- a/luprex/core/cpp/world.hpp +++ b/luprex/core/cpp/world.hpp @@ -299,6 +299,9 @@ private: // static void store_global_pointer(lua_State *L, World *w); + // Start a thread on the specified tangible. + void tangible_start(Tangible *actor, Tangible *place, LuaStack &LS, LuaSlot func, int argpos, int nargs); + // Invoke a plan. // void invoke_plan(int64_t actor_id, int64_t place_id, const eng::string &action, const InvocationData &data); @@ -543,6 +546,7 @@ private: friend int lfn_tangible_nopredict(lua_State *L); friend int lfn_tangible_near(lua_State *L); friend int lfn_tangible_scan(lua_State *L); + friend int lfn_tangible_start(lua_State *L); friend int lfn_math_random(lua_State *L); friend int lfn_math_randomstate(lua_State *L); friend int lfn_wait(lua_State *L);