LuaScriptingContext.cpp 11 KB

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