LuaScriptingContext.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. /*
  2. * LuaScriptingContext.cpp, part of VCMI engine
  3. *
  4. * Authors: listed in file AUTHORS in main folder
  5. *
  6. * License: GNU General Public License v2.0 or later
  7. * Full text of license available in license.txt file, in main folder
  8. *
  9. */
  10. #include "StdInc.h"
  11. #include "LuaScriptingContext.h"
  12. #include <vstd/StringUtils.h>
  13. #include <vcmi/events/EventBus.h>
  14. #include <vcmi/ServerCallback.h>
  15. #include "vcmi/Services.h"
  16. #include "LuaStack.h"
  17. #include "api/Registry.h"
  18. #include "../../lib/json/JsonNode.h"
  19. #include "../../lib/filesystem/Filesystem.h"
  20. #include "../../lib/battle/IBattleInfoCallback.h"
  21. #include "../../lib/CGameInfoCallback.h"
  22. #include "../../lib/modding/ModScope.h"
  23. VCMI_LIB_NAMESPACE_BEGIN
  24. namespace scripting
  25. {
  26. const std::string LuaContext::STATE_FIELD = "DATA";
  27. LuaContext::LuaContext(const Script * source, const Environment * env_):
  28. ContextBase(env_->logger()),
  29. L(luaL_newstate()),
  30. script(source),
  31. env(env_)
  32. {
  33. static const std::vector<luaL_Reg> STD_LIBS =
  34. {
  35. {"", luaopen_base},
  36. {LUA_TABLIBNAME, luaopen_table},
  37. {LUA_STRLIBNAME, luaopen_string},
  38. {LUA_MATHLIBNAME, luaopen_math},
  39. {LUA_BITLIBNAME, luaopen_bit}
  40. };
  41. for(const luaL_Reg & lib : STD_LIBS)
  42. {
  43. lua_pushcfunction(L, lib.func);
  44. lua_pushstring(L, lib.name);
  45. lua_call(L, 1, 0);
  46. }
  47. popAll();
  48. cleanupGlobals();
  49. popAll();
  50. lua_newtable(L);
  51. modules = std::make_shared<LuaReference>(L);
  52. popAll();
  53. registerCore();
  54. popAll();
  55. LuaStack S(L);
  56. S.push(env->game());
  57. lua_setglobal(L, "GAME");
  58. S.push(env->battle(BattleID::NONE));
  59. lua_setglobal(L, "BATTLE");
  60. S.push(env->eventBus());
  61. lua_setglobal(L, "EVENT_BUS");
  62. S.push(env->services());
  63. lua_setglobal(L, "SERVICES");
  64. popAll();
  65. }
  66. LuaContext::~LuaContext()
  67. {
  68. modules.reset();
  69. scriptClosure.reset();
  70. lua_close(L);
  71. }
  72. void LuaContext::cleanupGlobals()
  73. {
  74. LuaStack S(L);
  75. S.clear();
  76. S.pushNil();
  77. lua_setglobal(L, "collectgarbage");
  78. S.pushNil();
  79. lua_setglobal(L, "dofile");
  80. S.pushNil();
  81. lua_setglobal(L, "load");
  82. S.pushNil();
  83. lua_setglobal(L, "loadfile");
  84. S.pushNil();
  85. lua_setglobal(L, "loadstring");
  86. S.pushNil();
  87. lua_setglobal(L, "print");
  88. S.clear();
  89. lua_getglobal(L, LUA_STRLIBNAME);
  90. S.push("dump");
  91. S.pushNil();
  92. lua_rawset(L, -3);
  93. S.clear();
  94. lua_getglobal(L, LUA_MATHLIBNAME);
  95. S.push("random");
  96. S.pushNil();
  97. lua_rawset(L, -3);
  98. S.push("randomseed");
  99. S.pushNil();
  100. lua_rawset(L, -3);
  101. S.clear();
  102. }
  103. void LuaContext::run(ServerCallback * server, const JsonNode & initialState)
  104. {
  105. {
  106. LuaStack S(L);
  107. S.push(server);
  108. lua_setglobal(L, "SERVER");
  109. S.clear();
  110. }
  111. run(initialState);
  112. // {
  113. // LuaStack S(L);
  114. // S.pushNil();
  115. // lua_setglobal(L, "SERVER");
  116. // S.clear();
  117. // }
  118. }
  119. void LuaContext::run(const JsonNode & initialState)
  120. {
  121. setGlobal(STATE_FIELD, initialState);
  122. int ret = luaL_loadbuffer(L, script->getSource().c_str(), script->getSource().size(), script->getName().c_str());
  123. if(ret)
  124. {
  125. logger->error("Script '%s' failed to load, error: %s", script->getName(), toStringRaw(-1));
  126. popAll();
  127. return;
  128. }
  129. scriptClosure = std::make_shared<LuaReference>(L);
  130. popAll();
  131. scriptClosure->push();
  132. ret = lua_pcall(L, 0, 0, 0);
  133. if(ret)
  134. {
  135. logger->error("Script '%s' failed to run, error: '%s'", script->getName(), toStringRaw(-1));
  136. popAll();
  137. }
  138. }
  139. int LuaContext::errorRetVoid(const std::string & message)
  140. {
  141. logger->error(message);
  142. popAll();
  143. return 0;
  144. }
  145. JsonNode LuaContext::callGlobal(const std::string & name, const JsonNode & parameters)
  146. {
  147. LuaStack S(L);
  148. lua_getglobal(L, name.c_str());
  149. if(!S.isFunction(-1))
  150. {
  151. boost::format fmt("%s is not a function");
  152. fmt % name;
  153. logger->error(fmt.str());
  154. S.clear();
  155. return JsonNode();
  156. }
  157. int argc = parameters.Vector().size();
  158. for(int idx = 0; idx < argc; idx++)
  159. S.push(parameters.Vector()[idx]);
  160. if(lua_pcall(L, argc, 1, 0))
  161. {
  162. std::string error = lua_tostring(L, -1);
  163. boost::format fmt("Lua function %s failed with message: %s");
  164. fmt % name % error;
  165. logger->error(fmt.str());
  166. S.clear();
  167. return JsonNode();
  168. }
  169. JsonNode ret;
  170. pop(ret);
  171. S.balance();
  172. return ret;
  173. }
  174. JsonNode LuaContext::callGlobal(ServerCallback * cb, const std::string & name, const JsonNode & parameters)
  175. {
  176. LuaStack S(L);
  177. S.push(cb);
  178. lua_setglobal(L, "SERVER");
  179. auto ret = callGlobal(name, parameters);
  180. S.pushNil();
  181. lua_setglobal(L, "SERVER");
  182. return ret;
  183. }
  184. void LuaContext::getGlobal(const std::string & name, int & value)
  185. {
  186. LuaStack S(L);
  187. lua_getglobal(L, name.c_str());
  188. lua_Integer temp;
  189. if(S.tryGetInteger(-1, temp))
  190. value = static_cast<int>(temp);
  191. else
  192. value = 0;
  193. S.balance();
  194. }
  195. void LuaContext::getGlobal(const std::string & name, std::string & value)
  196. {
  197. LuaStack S(L);
  198. lua_getglobal(L, name.c_str());
  199. if(!S.tryGet(-1, value))
  200. value.clear();
  201. S.balance();
  202. }
  203. void LuaContext::getGlobal(const std::string & name, double & value)
  204. {
  205. LuaStack S(L);
  206. lua_getglobal(L, name.c_str());
  207. if(!S.tryGet(-1, value))
  208. value = 0.0;
  209. S.balance();
  210. }
  211. void LuaContext::getGlobal(const std::string & name, JsonNode & value)
  212. {
  213. LuaStack S(L);
  214. lua_getglobal(L, name.c_str());
  215. pop(value);
  216. S.balance();
  217. }
  218. void LuaContext::setGlobal(const std::string & name, int value)
  219. {
  220. lua_pushinteger(L, static_cast<lua_Integer>(value));
  221. lua_setglobal(L, name.c_str());
  222. }
  223. void LuaContext::setGlobal(const std::string & name, const std::string & value)
  224. {
  225. lua_pushlstring(L, value.c_str(), value.size());
  226. lua_setglobal(L, name.c_str());
  227. }
  228. void LuaContext::setGlobal(const std::string & name, double value)
  229. {
  230. lua_pushnumber(L, value);
  231. lua_setglobal(L, name.c_str());
  232. }
  233. void LuaContext::setGlobal(const std::string & name, const JsonNode & value)
  234. {
  235. LuaStack S(L);
  236. S.push(value);
  237. lua_setglobal(L, name.c_str());
  238. S.balance();
  239. }
  240. JsonNode LuaContext::saveState()
  241. {
  242. JsonNode data;
  243. getGlobal(STATE_FIELD, data);
  244. return data;
  245. }
  246. void LuaContext::pop(JsonNode & value)
  247. {
  248. auto type = lua_type(L, -1);
  249. switch(type)
  250. {
  251. case LUA_TNUMBER:
  252. value.Float() = lua_tonumber(L, -1);
  253. break;
  254. case LUA_TBOOLEAN:
  255. value.Bool() = (lua_toboolean(L, -1) != 0);
  256. break;
  257. case LUA_TSTRING:
  258. value.String() = toStringRaw(-1);
  259. break;
  260. case LUA_TTABLE:
  261. {
  262. JsonNode asVector;
  263. JsonNode asStruct;
  264. lua_pushnil(L); /* first key */
  265. while(lua_next(L, -2) != 0)
  266. {
  267. /* 'key' (at index -2) and 'value' (at index -1) */
  268. JsonNode fieldValue;
  269. pop(fieldValue);
  270. if(lua_type(L, -1) == LUA_TNUMBER)
  271. {
  272. auto key = lua_tointeger(L, -1);
  273. if(key > 0)
  274. {
  275. if(asVector.Vector().size() < key)
  276. asVector.Vector().resize(key);
  277. --key;
  278. asVector.Vector().at(key) = fieldValue;
  279. }
  280. }
  281. else if(lua_isstring(L, -1))
  282. {
  283. auto key = toStringRaw(-1);
  284. asStruct[key] = fieldValue;
  285. }
  286. }
  287. if(!asVector.Vector().empty())
  288. {
  289. std::swap(value, asVector);
  290. }
  291. else
  292. {
  293. std::swap(value, asStruct);
  294. }
  295. }
  296. break;
  297. default:
  298. value.clear();
  299. break;
  300. }
  301. lua_pop(L, 1);
  302. }
  303. void LuaContext::push(const std::string & value)
  304. {
  305. lua_pushlstring(L, value.c_str(), value.size());
  306. }
  307. void LuaContext::push(lua_CFunction f, void * opaque)
  308. {
  309. lua_pushlightuserdata(L, opaque);
  310. lua_pushcclosure(L, f, 1);
  311. }
  312. void LuaContext::popAll()
  313. {
  314. lua_settop(L, 0);
  315. }
  316. std::string LuaContext::toStringRaw(int index)
  317. {
  318. size_t len = 0;
  319. const auto *raw = lua_tolstring(L, index, &len);
  320. return std::string(raw, len);
  321. }
  322. void LuaContext::registerCore()
  323. {
  324. push(&LuaContext::require, this);
  325. lua_setglobal(L, "require");
  326. push(&LuaContext::logError, this);
  327. lua_setglobal(L, "logError");
  328. popAll();//just in case
  329. for(const auto & registar : api::Registry::get()->getCoreData())
  330. {
  331. registar.second->pushMetatable(L); //table
  332. modules->push(); //table modules
  333. push(registar.first); //table modules name
  334. lua_pushvalue(L, -3); //table modules name table
  335. lua_rawset(L, -3);
  336. popAll();
  337. }
  338. }
  339. int LuaContext::require(lua_State * L)
  340. {
  341. auto * self = static_cast<LuaContext *>(lua_touserdata(L, lua_upvalueindex(1)));
  342. if(!self)
  343. {
  344. lua_pushstring(L, "internal error");
  345. lua_error(L);
  346. return 0;
  347. }
  348. return self->loadModule();
  349. }
  350. int LuaContext::loadModule()
  351. {
  352. int argc = lua_gettop(L);
  353. if(argc < 1)
  354. return errorRetVoid("Module name required");
  355. //if module is loaded already, assume that module name is valid
  356. modules->push();
  357. lua_pushvalue(L, -2);
  358. lua_rawget(L, -2);
  359. if(lua_istable(L, -1))
  360. {
  361. lua_replace(L, 1);
  362. lua_settop(L, 1);
  363. return 1;
  364. }
  365. //continue with more checks
  366. if(!lua_isstring(L, 1))
  367. return errorRetVoid("Module name must be string");
  368. std::string resourceName = toStringRaw(1);
  369. if(resourceName.empty())
  370. return errorRetVoid("Module name is empty");
  371. auto temp = vstd::split(resourceName, ":");
  372. std::string scope;
  373. std::string modulePath;
  374. if(temp.size() <= 1)
  375. {
  376. modulePath = temp.at(0);
  377. }
  378. else
  379. {
  380. scope = temp.at(0);
  381. modulePath = temp.at(1);
  382. }
  383. if(scope.empty())
  384. {
  385. const auto *registar = api::Registry::get()->find(modulePath);
  386. if(!registar)
  387. {
  388. return errorRetVoid("Module not found: "+modulePath);
  389. }
  390. registar->pushMetatable(L);
  391. }
  392. else if(scope == ModScope::scopeBuiltin())
  393. {
  394. // boost::algorithm::replace_all(modulePath, boost::is_any_of("\\/ "), "");
  395. boost::algorithm::replace_all(modulePath, ".", "/");
  396. auto *loader = CResourceHandler::get(ModScope::scopeBuiltin());
  397. modulePath = "scripts/lib/" + modulePath;
  398. ResourcePath id(modulePath, EResType::LUA);
  399. if(!loader->existsResource(id))
  400. return errorRetVoid("Module not found: "+modulePath);
  401. auto rawData = loader->load(id)->readAll();
  402. auto sourceText = std::string(reinterpret_cast<char *>(rawData.first.get()), rawData.second);
  403. int ret = luaL_loadbuffer(L, sourceText.c_str(), sourceText.size(), modulePath.c_str());
  404. if(ret)
  405. return errorRetVoid(toStringRaw(-1));
  406. ret = lua_pcall(L, 0, 1, 0);
  407. if(ret)
  408. {
  409. logger->error("Module '%s' failed to run, error: %s", modulePath, toStringRaw(-1));
  410. popAll();
  411. return 0;
  412. }
  413. }
  414. else
  415. {
  416. //todo: also allow loading scripts from same scope
  417. return errorRetVoid("No access to scope "+scope);
  418. }
  419. modules->push(); //name table modules
  420. lua_pushvalue(L, 1);//name table modules name
  421. if(!lua_isstring(L, -1))
  422. return errorRetVoid("Module name corrupted");
  423. lua_pushvalue(L, -3);//name table modules name table
  424. lua_rawset(L, -3);//name table modules
  425. lua_pop(L, 1);//name table
  426. lua_replace(L, 1);//table table
  427. lua_settop(L, 1);//table
  428. return 1;
  429. }
  430. int LuaContext::print(lua_State * L)
  431. {
  432. //TODO:
  433. lua_settop(L, 0);
  434. return 0;
  435. }
  436. int LuaContext::printImpl()
  437. {
  438. //TODO:
  439. return 0;
  440. }
  441. int LuaContext::logError(lua_State * L)
  442. {
  443. auto * self = static_cast<LuaContext *>(lua_touserdata(L, lua_upvalueindex(1)));
  444. if(!self)
  445. {
  446. lua_pushstring(L, "internal error");
  447. lua_error(L);
  448. return 0;
  449. }
  450. return self->logErrorImpl();
  451. }
  452. int LuaContext::logErrorImpl()
  453. {
  454. LuaStack S(L);
  455. std::string message;
  456. if(S.tryGet(1, message))
  457. logger->error(message);
  458. return S.retVoid();
  459. }
  460. }
  461. VCMI_LIB_NAMESPACE_END