ScriptManager.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  1. #include "core/ConfigVar.h"
  2. #include "core/DebugDraw.h"
  3. #include "core/Logger.h"
  4. #include "core/ScriptManager.h"
  5. #include "core/ScriptManagerBinds.h"
  6. #include "core/ScriptManagerCommands.h"
  7. #include "core/Timer.h"
  8. #include "core/Utilites.h"
  9. #include "core/XmlScriptsFile.h"
  10. #include "LuaIncludes.h"
  11. ConfigVar cv_debug_script("debug_script", "Debug script flags. 0x01 - System, 0x02 - Entity, 0x04 - Ui.", "0");
  12. template<>ScriptManager *Ogre::Singleton<ScriptManager>::msSingleton = nullptr;
  13. Ogre::String script_entity_type[] = {"SYSTEM", "ENTITY", "UI"};
  14. bool
  15. priority_queue_compare(QueueScript a, QueueScript b)
  16. {
  17. return a.priority < b.priority;
  18. }
  19. ScriptManager::ScriptManager():
  20. m_SystemTableName("System"),
  21. m_EntityTableName("EntityContainer"),
  22. m_UiTableName("UiContainer")
  23. {
  24. LOG_TRIVIAL("ScriptManager started.");
  25. m_LuaState = lua_open();
  26. luabind::open(m_LuaState);
  27. luaopen_base(m_LuaState);
  28. luaopen_string(m_LuaState);
  29. luaopen_table(m_LuaState);
  30. luaopen_math(m_LuaState);
  31. InitBinds();
  32. InitCmd();
  33. RunFile("system/system.lua");
  34. XmlScriptsFile scripts("./data/scripts.xml");
  35. scripts.LoadScripts();
  36. }
  37. ScriptManager::~ScriptManager()
  38. {
  39. lua_close(m_LuaState);
  40. LOG_TRIVIAL("ScriptManager closed.");
  41. }
  42. void
  43. ScriptManager::Input(const QGears::Event& event)
  44. {
  45. if ((event.type == QGears::ET_KEY_PRESS || event.type == QGears::ET_KEY_REPEAT) &&
  46. (
  47. event.param1 == OIS::KC_RETURN ||
  48. event.param1 == OIS::KC_ESCAPE ||
  49. event.param1 == OIS::KC_SPACE ||
  50. event.param1 == OIS::KC_LCONTROL ||
  51. event.param1 == OIS::KC_LEFT ||
  52. event.param1 == OIS::KC_RIGHT ||
  53. event.param1 == OIS::KC_DOWN ||
  54. event.param1 == OIS::KC_UP
  55. ))
  56. {
  57. for(unsigned int i = 0; i < m_ScriptEntity.size(); ++i)
  58. {
  59. Ogre::String argument2 = "";
  60. if (event.type == QGears::ET_KEY_PRESS)
  61. {
  62. argument2 = "Press";
  63. }
  64. else if (event.type == QGears::ET_KEY_REPEAT)
  65. {
  66. argument2 = "Repeat";
  67. }
  68. ScriptRequest(&m_ScriptEntity[i], "on_button", 100, KeyToString((OIS::KeyCode)((int)event.param1)), argument2, false, false);
  69. }
  70. }
  71. }
  72. void
  73. ScriptManager::Update(const ScriptManager::Type type)
  74. {
  75. // resort all queue. This will give us correct info for debug draw.
  76. for(unsigned int i = 0; i < m_ScriptEntity.size(); ++i)
  77. {
  78. if(m_ScriptEntity[i].type == type)
  79. {
  80. if(m_ScriptEntity[i].queue.size() > 0)
  81. {
  82. if(m_ScriptEntity[i].resort == true)
  83. {
  84. std::stable_sort(m_ScriptEntity[i].queue.begin(), m_ScriptEntity[i].queue.end(), priority_queue_compare);
  85. m_ScriptEntity[i].resort = false;
  86. }
  87. }
  88. }
  89. }
  90. // draw debug before update. This way it will be posible to see scripts that run once
  91. int debug = cv_debug_script.GetI();
  92. if(debug != 0)
  93. {
  94. DEBUG_DRAW.SetTextAlignment(DEBUG_DRAW.LEFT);
  95. DEBUG_DRAW.SetScreenSpace(true);
  96. int y = 34;
  97. for(unsigned int i = 0; i < 3; ++i)
  98. {
  99. if((debug & (1 << i)) != 0)
  100. {
  101. DEBUG_DRAW.SetColour(Ogre::ColourValue(0.8f, 0.8f, 0.0f, 1.0f));
  102. DEBUG_DRAW.Text(10.0f, static_cast<float>(y), "Script \"" + script_entity_type[i] + "\" entity:");
  103. y += 16;
  104. for(unsigned int j = 0; j < m_ScriptEntity.size(); ++j)
  105. {
  106. if(m_ScriptEntity[j].type == i)
  107. {
  108. Ogre::String text = m_ScriptEntity[j].name;
  109. unsigned int queue_size = m_ScriptEntity[j].queue.size();
  110. if(queue_size > 0)
  111. {
  112. text += ": ";
  113. DEBUG_DRAW.SetColour(Ogre::ColourValue(0.8f, 0.8f, 0.0f, 1.0f));
  114. }
  115. else
  116. {
  117. DEBUG_DRAW.SetColour(Ogre::ColourValue(0.5, 0.5, 0.5, 1));
  118. }
  119. for(unsigned int k = 0; k < queue_size; ++k)
  120. {
  121. if(k > 0)
  122. {
  123. text += ", ";
  124. }
  125. text += "(" + Ogre::StringConverter::toString(m_ScriptEntity[j].queue[k].priority) + ")" + m_ScriptEntity[j].queue[k].function;
  126. if(m_ScriptEntity[j].queue[k].wait == true)
  127. {
  128. if(m_ScriptEntity[j].queue[k].seconds_to_wait != 0)
  129. {
  130. text += ":wait(" + Ogre::StringConverter::toString(m_ScriptEntity[j].queue[k].seconds_to_wait) + ")";
  131. }
  132. }
  133. }
  134. DEBUG_DRAW.Text(20.0f, static_cast<float>(y), text);
  135. y += 16;
  136. }
  137. }
  138. }
  139. }
  140. }
  141. for(unsigned int i = 0; i < m_ScriptEntity.size(); ++i)
  142. {
  143. if(m_ScriptEntity[i].type == type)
  144. {
  145. if(m_ScriptEntity[i].queue.size() > 0)
  146. {
  147. m_CurrentScriptId.entity = m_ScriptEntity[i].name;
  148. m_CurrentScriptId.function = m_ScriptEntity[i].queue[0].function;
  149. if(m_ScriptEntity[i].queue[0].wait == false)
  150. {
  151. if(m_ScriptEntity[i].queue[0].yield == false)
  152. {
  153. LOG_TRIVIAL("[SCRIPT] Start script \"" + m_CurrentScriptId.function + "\" for entity \"" + m_CurrentScriptId.entity + "\".");
  154. if(m_ScriptEntity[i].queue[0].paused_script_start.entity != "")
  155. {
  156. ContinueScriptExecution(m_ScriptEntity[i].queue[0].paused_script_start);
  157. m_ScriptEntity[i].queue[0].paused_script_start.entity = "";
  158. }
  159. luabind::object table = GetTableByEntityName(type, m_CurrentScriptId.entity, m_ScriptEntity[i].queue[0].state);
  160. if(table.is_valid() &&
  161. luabind::type(table) == LUA_TTABLE &&
  162. luabind::type(table[m_CurrentScriptId.function]) == LUA_TFUNCTION)
  163. {
  164. int ret = 0;
  165. try
  166. {
  167. ret = luabind::resume_function< int >(table[m_CurrentScriptId.function], table, m_ScriptEntity[i].queue[0].argument1.c_str(), m_ScriptEntity[i].queue[0].argument2.c_str());
  168. }
  169. catch(luabind::error& /*e*/)
  170. {
  171. LOG_ERROR(Ogre::String(lua_tostring(m_ScriptEntity[i].queue[0].state , -1)));
  172. }
  173. if(ret == 0)
  174. {
  175. LOG_TRIVIAL("[SCRIPT] Script \"" + m_CurrentScriptId.function + "\" for entity \"" + m_CurrentScriptId.entity + "\" finished.");
  176. if(m_ScriptEntity[i].queue[0].function != "on_update")
  177. {
  178. RemoveEntityTopScript(m_ScriptEntity[i]);
  179. }
  180. }
  181. else if(ret == 1)
  182. {
  183. LOG_TRIVIAL("[SCRIPT] Script \"" + m_CurrentScriptId.function + "\" for entity \"" + m_CurrentScriptId.entity + "\" not paused and will be continued next cycle.");
  184. m_ScriptEntity[i].queue[0].yield = true;
  185. }
  186. else
  187. {
  188. LOG_TRIVIAL("[SCRIPT] Script \"" + m_CurrentScriptId.function + "\" for entity \"" + m_CurrentScriptId.entity + "\" not finished yet.");
  189. m_ScriptEntity[i].queue[0].yield = true;
  190. m_ScriptEntity[i].queue[0].wait = true;
  191. }
  192. }
  193. else
  194. {
  195. LOG_WARNING("Script \"" + m_CurrentScriptId.function + "\" for entity \"" + m_CurrentScriptId.entity + "\" doesn't exist.");
  196. RemoveEntityTopScript(m_ScriptEntity[i]);
  197. }
  198. }
  199. else
  200. {
  201. LOG_TRIVIAL("[SCRIPT] Continue function \"" + m_CurrentScriptId.function + "\" for entity \"" + m_CurrentScriptId.entity + "\".");
  202. int ret = 0;
  203. try
  204. {
  205. ret = luabind::resume< int >(m_ScriptEntity[i].queue[0].state);
  206. }
  207. catch(luabind::error& e)
  208. {
  209. luabind::object error_msg(luabind::from_stack(e.state(), -1));
  210. LOG_WARNING(Ogre::String(luabind::object_cast< std::string >(error_msg)));
  211. }
  212. if(ret == 0) // finished
  213. {
  214. LOG_TRIVIAL("[SCRIPT] Script \"" + m_CurrentScriptId.function + "\" for entity \"" + m_CurrentScriptId.entity + "\" finished.");
  215. // stop yield for on_update
  216. m_ScriptEntity[i].queue[0].yield = false;
  217. if(m_ScriptEntity[i].queue[0].function != "on_update")
  218. {
  219. RemoveEntityTopScript(m_ScriptEntity[i]);
  220. }
  221. }
  222. else if(ret == 1)
  223. {
  224. LOG_TRIVIAL("[SCRIPT] Script \"" + m_CurrentScriptId.function + "\" for entity \"" + m_CurrentScriptId.entity + "\" not paused and will be continued next cycle.");
  225. }
  226. else
  227. {
  228. LOG_TRIVIAL("[SCRIPT] Script \"" + m_CurrentScriptId.function + "\" for entity \"" + m_CurrentScriptId.entity + "\" not finished yet.");
  229. m_ScriptEntity[i].queue[0].wait = true;
  230. }
  231. }
  232. }
  233. else if(m_ScriptEntity[i].queue[0].seconds_to_wait > 0)
  234. {
  235. m_ScriptEntity[i].queue[0].seconds_to_wait -= Timer::getSingleton().GetGameTimeDelta();
  236. m_ScriptEntity[i].queue[0].seconds_to_wait = (m_ScriptEntity[i].queue[0].seconds_to_wait < 0) ? 0 : m_ScriptEntity[i].queue[0].seconds_to_wait;
  237. if(m_ScriptEntity[i].queue[0].seconds_to_wait == 0)
  238. {
  239. m_ScriptEntity[i].queue[0].wait = false;
  240. }
  241. }
  242. }
  243. }
  244. }
  245. }
  246. void
  247. ScriptManager::RunString(const Ogre::String& lua)
  248. {
  249. if(luaL_dostring(m_LuaState, lua.c_str()) == 1)
  250. {
  251. LOG_ERROR(Ogre::String(lua_tostring(m_LuaState, -1)));
  252. }
  253. }
  254. void
  255. ScriptManager::RunFile(const Ogre::String& file)
  256. {
  257. if(luaL_dofile(m_LuaState, ("./data/" + file).c_str()) == 1)
  258. {
  259. LOG_ERROR(Ogre::String(lua_tostring(m_LuaState, -1)));
  260. }
  261. }
  262. void
  263. ScriptManager::AddEntity(const ScriptManager::Type type, const Ogre::String& entity_name, Entity* entity)
  264. {
  265. for(unsigned int i = 0; i < m_ScriptEntity.size(); ++i)
  266. {
  267. if(m_ScriptEntity[i].type == type && m_ScriptEntity[i].name == entity_name)
  268. {
  269. LOG_ERROR("Script \"" + script_entity_type[type] + "\" entity \"" + entity_name + "\" already exist in script manager.");
  270. return;
  271. }
  272. }
  273. luabind::object table = GetTableByEntityName(type, entity_name, m_LuaState);
  274. if(table.is_valid() && luabind::type(table) == LUA_TTABLE)
  275. {
  276. ScriptEntity script_entity;
  277. script_entity.name = entity_name;
  278. script_entity.type = type;
  279. // init entity field for model entity
  280. if(entity != nullptr)
  281. {
  282. table["entity"] = boost::ref(*entity);
  283. }
  284. // check "on_start" script
  285. if(luabind::type(table["on_start"]) == LUA_TFUNCTION)
  286. {
  287. QueueScript script;
  288. script.function = "on_start";
  289. script.priority = 0;
  290. script.state = lua_newthread(m_LuaState);
  291. // we dont want thread to be garbage collected so we store it
  292. script.state_id = luaL_ref(m_LuaState, LUA_REGISTRYINDEX);
  293. script.seconds_to_wait = 0;
  294. script.wait = false;
  295. script.yield = false;
  296. script_entity.queue.push_back(script);
  297. }
  298. // check "on_update" script
  299. if(luabind::type(table["on_update"]) == LUA_TFUNCTION)
  300. {
  301. QueueScript script;
  302. script.function = "on_update";
  303. script.priority = 999;
  304. script.state = lua_newthread(m_LuaState);
  305. // we dont want thread to be garbage collected so we store it
  306. script.state_id = luaL_ref(m_LuaState, LUA_REGISTRYINDEX);
  307. script.seconds_to_wait = 0;
  308. script.wait = false;
  309. script.yield = false;
  310. script_entity.queue.push_back(script);
  311. }
  312. m_ScriptEntity.push_back(script_entity);
  313. }
  314. }
  315. void
  316. ScriptManager::RemoveEntity(const ScriptManager::Type type, const Ogre::String& entity_name)
  317. {
  318. for(unsigned int i = 0; i < m_ScriptEntity.size(); ++i)
  319. {
  320. if(m_ScriptEntity[i].type == type && m_ScriptEntity[i].name == entity_name)
  321. {
  322. while(m_ScriptEntity[i].queue.size() > 0)
  323. {
  324. ScriptManager::RemoveEntityTopScript(m_ScriptEntity[i]);
  325. }
  326. m_ScriptEntity.erase(m_ScriptEntity.begin() + i);
  327. return;
  328. }
  329. }
  330. }
  331. void
  332. ScriptManager::RemoveEntityTopScript(ScriptEntity& entity)
  333. {
  334. if(entity.queue.size() > 0)
  335. {
  336. // delete thread
  337. luaL_unref(m_LuaState, LUA_REGISTRYINDEX, entity.queue[0].state_id);
  338. if(entity.queue[0].paused_script_end.entity != "")
  339. {
  340. ContinueScriptExecution(entity.queue[0].paused_script_end);
  341. entity.queue[0].paused_script_end.entity = "";
  342. }
  343. entity.queue.erase(entity.queue.begin());
  344. }
  345. }
  346. luabind::object
  347. ScriptManager::GetTableByEntityName(const ScriptManager::Type type, const Ogre::String& name, lua_State* state) const
  348. {
  349. // get real table by name
  350. Ogre::StringVector table_path = StringTokenise(name, ".");
  351. luabind::object table = luabind::globals(state);
  352. switch(type)
  353. {
  354. case ScriptManager::SYSTEM: table = table[m_SystemTableName]; break;
  355. case ScriptManager::ENTITY: table = table[m_EntityTableName]; break;
  356. case ScriptManager::UI: table = table[m_UiTableName]; break;
  357. }
  358. if(luabind::type(table) != LUA_TTABLE)
  359. {
  360. return luabind::object();
  361. }
  362. for(unsigned int i = 0; i < table_path.size(); ++i)
  363. {
  364. table = table[table_path[i]];
  365. if(luabind::type(table) != LUA_TTABLE)
  366. {
  367. return luabind::object();
  368. }
  369. }
  370. return table;
  371. }
  372. QueueScript*
  373. ScriptManager::GetScriptByScriptId(const ScriptId& script) const
  374. {
  375. for(unsigned int i = 0; i < m_ScriptEntity.size(); ++i)
  376. {
  377. if(script.entity == m_ScriptEntity[i].name)
  378. {
  379. for(unsigned int j = 0; j < m_ScriptEntity[i].queue.size(); ++j)
  380. {
  381. if(script.function == m_ScriptEntity[i].queue[j].function)
  382. {
  383. return (QueueScript*) &(m_ScriptEntity[i].queue[j]);
  384. }
  385. }
  386. return nullptr;
  387. }
  388. }
  389. return nullptr;
  390. }
  391. ScriptEntity*
  392. ScriptManager::GetScriptEntityByName(const Type type, const Ogre::String& entity_name) const
  393. {
  394. for(unsigned int i = 0; i < m_ScriptEntity.size(); ++i)
  395. {
  396. if(m_ScriptEntity[i].type == type && m_ScriptEntity[i].name == entity_name)
  397. {
  398. return (ScriptEntity*) &(m_ScriptEntity[i]);
  399. }
  400. }
  401. return nullptr;
  402. }
  403. const ScriptId
  404. ScriptManager::GetCurrentScriptId() const
  405. {
  406. return m_CurrentScriptId;
  407. }
  408. void
  409. ScriptManager::ContinueScriptExecution(const ScriptId& script)
  410. {
  411. QueueScript* script_pointer = GetScriptByScriptId(script);
  412. if(script_pointer == nullptr)
  413. {
  414. LOG_ERROR("There is no script \"" + script.function + "\" for entity \"" + script.entity + "\". Can't continue script execution.");
  415. return;
  416. }
  417. script_pointer->wait = false;
  418. }
  419. int
  420. ScriptManager::ScriptWait(const float seconds)
  421. {
  422. LOG_TRIVIAL("script:wait: We set script wait for " + Ogre::StringConverter::toString(seconds) + " seconds.");
  423. QueueScript* script = GetScriptByScriptId(m_CurrentScriptId);
  424. if(script == nullptr)
  425. {
  426. LOG_ERROR("script:wait: Currently no any script running.");
  427. return 1;
  428. }
  429. if(seconds == 0)
  430. {
  431. return 1;
  432. }
  433. script->seconds_to_wait = seconds;
  434. return -1;
  435. }
  436. void
  437. ScriptManager::ScriptRequest(const Type type, const char* entity, const char* function, const int priority)
  438. {
  439. LOG_TRIVIAL("[SCRIPT] script:request: Request function \"" + Ogre::String(function) + "\" for entity \"" + Ogre::String(entity) + "\" with priority " + Ogre::StringConverter::toString(priority) + ".");
  440. ScriptEntity* script_entity = GetScriptEntityByName(type, Ogre::String(entity));
  441. if(script_entity == nullptr)
  442. {
  443. LOG_WARNING("[SCRIPT] script:request: Entity \"" + Ogre::String(entity) + "\" doesn't exist.");
  444. return;
  445. }
  446. bool added = ScriptRequest(script_entity, function, priority, "", "", false, false);
  447. if(added == false)
  448. {
  449. LOG_WARNING("Script '" + Ogre::String(function) + "' for entity '" + Ogre::String(entity) + "' doesn't exist.");
  450. }
  451. }
  452. int
  453. ScriptManager::ScriptRequestStartSync(const Type type, const char* entity, const char* function, const int priority)
  454. {
  455. LOG_TRIVIAL("[SCRIPT] script:request_start_sync: Request function \"" + Ogre::String(function) + "\" for entity \"" + Ogre::String(entity) + "\" with priority " + Ogre::StringConverter::toString(priority) + ".");
  456. ScriptEntity* script_entity = GetScriptEntityByName(type, Ogre::String(entity));
  457. if(script_entity == nullptr)
  458. {
  459. LOG_ERROR("[SCRIPT] script:request_start_sync: Entity \"" + Ogre::String(entity) + "\" doesn't exist.");
  460. return 1;
  461. }
  462. bool added = ScriptRequest(script_entity, function, priority, "", "", true, false);
  463. if(added == false)
  464. {
  465. LOG_WARNING("Script '" + Ogre::String(function) + "' for entity '" + Ogre::String(entity) + "' doesn't exist.");
  466. return 1;
  467. }
  468. return -1;
  469. }
  470. int
  471. ScriptManager::ScriptRequestEndSync(const Type type, const char* entity, const char* function, const int priority)
  472. {
  473. LOG_TRIVIAL("[SCRIPT] script:request_end_sync: Request function \"" + Ogre::String(function) + "\" for entity \"" + Ogre::String(entity) + "\" with priority " + Ogre::StringConverter::toString(priority) + ".");
  474. ScriptEntity* script_entity = GetScriptEntityByName(type, Ogre::String(entity));
  475. if(script_entity == nullptr)
  476. {
  477. LOG_ERROR("[SCRIPT] script:request_end_sync: Entity \"" + Ogre::String(entity) + "\" doesn't exist.");
  478. return 1;
  479. }
  480. bool added = ScriptRequest(script_entity, function, priority, "", "", false, true);
  481. if(added == false)
  482. {
  483. LOG_WARNING("Script '" + Ogre::String(function) + "' for entity '" + Ogre::String(entity) + "' doesn't exist.");
  484. return 1;
  485. }
  486. return -1;
  487. }
  488. bool
  489. ScriptManager::ScriptRequest(ScriptEntity* script_entity, const Ogre::String& function, const int priority, const Ogre::String& argument1, const Ogre::String& argument2, bool start_sync, bool end_sync)
  490. {
  491. luabind::object table = GetTableByEntityName(script_entity->type, script_entity->name, m_LuaState);
  492. if(table.is_valid() && luabind::type(table) == LUA_TTABLE && luabind::type(table[function]) == LUA_TFUNCTION)
  493. {
  494. QueueScript script;
  495. script.function = function;
  496. script.argument1 = argument1;
  497. script.argument2 = argument2;
  498. script.priority = priority;
  499. script.state = lua_newthread(m_LuaState);
  500. // we dont want thread to be garbage collected so we store it
  501. script.state_id = luaL_ref(m_LuaState, LUA_REGISTRYINDEX);
  502. script.seconds_to_wait = 0;
  503. script.wait = false;
  504. script.yield = false;
  505. if(start_sync == true)
  506. {
  507. script.paused_script_start = GetCurrentScriptId();
  508. }
  509. if(end_sync == true)
  510. {
  511. script.paused_script_end = GetCurrentScriptId();
  512. }
  513. script_entity->queue.push_back(script);
  514. script_entity->resort = true;
  515. return true;
  516. }
  517. return false;
  518. }
  519. void
  520. ScriptManager::AddValueToStack(const float value)
  521. {
  522. QueueScript* script = GetScriptByScriptId(m_CurrentScriptId);
  523. if(script != nullptr)
  524. {
  525. lua_pushnumber(script->state, value);
  526. }
  527. }