LuaScriptingContext.cpp 11 KB

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