Merge branch 'fixes' into main/rendor-staging
[ryzomcore.git] / nel / src / gui / lua_object.cpp
blob608bb0053513b0cdc593e933a08e92636adec60e
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 // Copyright (C) 2013-2014 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 //
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "stdpch.h"
23 #include "nel/gui/lua_object.h"
24 #include "nel/gui/lua_ihm.h"
25 #include "nel/gui/lua_helper.h"
27 #ifdef DEBUG_NEW
28 #define new DEBUG_NEW
29 #endif
31 namespace NLGUI
34 // *************************************************
35 CLuaObject::CLuaObject(CLuaState &state, const char *id)
37 pop(state, id);
40 // *************************************************
41 CLuaObject::CLuaObject(CLuaState &state, const std::string &id)
43 pop(state, id.c_str());
47 // *************************************************
48 bool CLuaObject::isValid() const
50 return getLuaState() != NULL;
53 // *************************************************
54 CLuaState *CLuaObject::getLuaState() const
56 return _LuaState;
59 // *************************************************
60 CLuaObject::CLuaObject(const CLuaObject &other)
62 if (other.isValid())
64 other.push();
65 nlassert(other.getLuaState());
66 pop(*other.getLuaState());
67 _Id = other._Id;
69 // else ... copy of an invalid CLuaObject( is an invalid CLuaObject
72 // *************************************************
73 CLuaObject &CLuaObject::operator=(const CLuaObject &other)
75 if (!other.isValid())
77 release();
78 return *this;
80 other.push();
81 pop(*other.getLuaState());
82 _Id = other._Id;
83 return *this;
86 // *************************************************
87 bool CLuaObject::rawEqual(const CLuaObject &other) const
89 nlassert(other.getLuaState() == getLuaState());
90 push();
91 other.push();
92 bool equal = other.getLuaState()->rawEqual(-1, -2);
93 getLuaState()->pop(2);
94 return equal;
97 // *************************************************
98 CLuaObject::~CLuaObject()
100 release();
103 // *************************************************
104 void CLuaObject::release()
106 if (_LuaState)
108 CLuaStackChecker lsc(_LuaState);
109 _LuaState->pushLightUserData((void *) this);
110 _LuaState->pushNil();
111 _LuaState->setTable(LUA_REGISTRYINDEX);
112 _LuaState = NULL;
116 // *************************************************
117 void CLuaObject::pop(CLuaState &luaState, const char *id)
119 release();
120 nlassert(luaState.getTop() >= 1);
122 CLuaStackChecker lsc(&luaState);
123 luaState.pushLightUserData((void *) this);
124 luaState.pushValue(-2); // copy original object
125 luaState.setTable(LUA_REGISTRYINDEX);
127 luaState.pop();
128 _LuaState = &luaState;
129 _Id = id;
132 // *************************************************
133 void CLuaObject::push() const
135 nlassert(isValid());
136 _LuaState->pushLightUserData((void *) this);
137 _LuaState->getTable(LUA_REGISTRYINDEX);
140 // *************************************************
141 int CLuaObject::type() const
143 push();
144 int type = _LuaState->type();
145 _LuaState->pop();
146 return type;
149 // *************************************************
150 const char *CLuaObject::getTypename() const
152 push();
153 const char *typeName = _LuaState->getTypename(-1);
154 _LuaState->pop();
155 return typeName;
158 // *************************************************
159 bool CLuaObject::isNil() const { push(); bool result = _LuaState->isNil(); _LuaState->pop(); return result; }
160 bool CLuaObject::isNumber() const { push(); bool result = _LuaState->isNumber(); _LuaState->pop(); return result; }
161 bool CLuaObject::isInteger() const { push(); bool result = _LuaState->isInteger(); _LuaState->pop(); return result; }
162 bool CLuaObject::isBoolean() const { push(); bool result = _LuaState->isBoolean(); _LuaState->pop(); return result; }
163 bool CLuaObject::isString() const { push(); bool result = _LuaState->isString(); _LuaState->pop(); return result; }
164 bool CLuaObject::isFunction() const { push(); bool result = _LuaState->isFunction(); _LuaState->pop(); return result; }
165 bool CLuaObject::isCFunction() const { push(); bool result = _LuaState->isCFunction(); _LuaState->pop(); return result; }
166 bool CLuaObject::isTable() const { push(); bool result = _LuaState->isTable(); _LuaState->pop(); return result; }
167 bool CLuaObject::isUserData() const { push(); bool result = _LuaState->isUserData(); _LuaState->pop(); return result; }
168 bool CLuaObject::isLightUserData() const { push(); bool result = _LuaState->isLightUserData(); _LuaState->pop(); return result; }
169 bool CLuaObject::isRGBA() const
171 if (!isUserData()) return false;
172 push();
173 NLMISC::CRGBA dummy;
174 return CLuaIHM::pop(*_LuaState, dummy);
177 // *************************************************
178 bool CLuaObject::toBoolean() const { push(); bool result = _LuaState->toBoolean(); _LuaState->pop(); return result; }
179 lua_Number CLuaObject::toNumber() const { push(); lua_Number result = _LuaState->toNumber(); _LuaState->pop(); return result; }
180 lua_Integer CLuaObject::toInteger() const { push(); lua_Integer result = _LuaState->toInteger(); _LuaState->pop(); return result; }
181 std::string CLuaObject::toString() const
183 push();
184 const char *str = _LuaState->toString();
185 std::string result = str ? str : "";
186 _LuaState->pop();
187 return result;
189 lua_CFunction CLuaObject::toCFunction() const { push(); lua_CFunction result = _LuaState->toCFunction(); _LuaState->pop(); return result; }
190 void *CLuaObject::toUserData() const { push(); void *result = _LuaState->toUserData(); _LuaState->pop(); return result; }
191 const void *CLuaObject::toPointer() const { push(); const void *result = _LuaState->toPointer(); _LuaState->pop(); return result; }
192 NLMISC::CRGBA CLuaObject::toRGBA() const
194 NLMISC::CRGBA result;
195 push();
196 if (CLuaIHM::pop(*_LuaState, result))
198 return result;
200 return NLMISC::CRGBA::Black;
203 // *************************************************
204 CLuaObject::operator bool() const { return toBoolean(); }
205 CLuaObject::operator float() const { return (float) toNumber(); }
206 CLuaObject::operator double() const { return (double) toNumber(); }
207 CLuaObject::operator sint32() const { return (sint32) toInteger(); }
208 CLuaObject::operator sint64() const { return (sint64) toInteger(); }
209 CLuaObject::operator std::string() const { return toString(); }
212 // *************************************************
213 bool CLuaObject::isEnumerable() const
215 if (isTable()) return true;
216 CLuaStackRestorer lsr(_LuaState, _LuaState->getTop());
217 push();
218 if (_LuaState->getMetaTable(-1))
220 _LuaState->remove(-2);
221 _LuaState->push("__next");
222 _LuaState->getTable(-2);
223 return _LuaState->isFunction();
225 return false;
228 // *************************************************
229 CLuaEnumeration CLuaObject::enumerate()
231 if (!isEnumerable())
233 throw ELuaNotATable(NLMISC::toString("Called CLuaObject::enumerate on an object that has no enumeration (not a table or no '__next' method in the metatable). Object is '%s' with type '%s'", getId().c_str(), getTypename()).c_str());
235 return CLuaEnumeration(*this);
238 // *************************************************
239 CLuaObject CLuaObject::operator[](const char *key) const
241 nlassert(key);
242 nlassert(isValid());
243 if (!isEnumerable())
245 _LuaState->pushNil();
246 CLuaObject result(*_LuaState);
247 return result;
249 CLuaStackChecker lsc(_LuaState);
250 push();
251 _LuaState->push(key);
252 _LuaState->getTable(-2);
253 CLuaObject subObject(*_LuaState, concatId(getId(), key));
254 _LuaState->pop(); // pop the sub object
255 return subObject;
258 // *************************************************
259 CLuaObject CLuaObject::operator[](double key) const
261 nlassert(isValid());
262 if (!isEnumerable())
264 _LuaState->pushNil();
265 CLuaObject result(*_LuaState);
266 return result;
268 CLuaStackChecker lsc(_LuaState);
269 push();
270 _LuaState->push(key);
271 _LuaState->getTable(-2);
272 CLuaObject subObject(*_LuaState, concatId(getId(), NLMISC::toString(key)));
273 _LuaState->pop(); // pop the sub object
274 return subObject;
277 // *************************************************
278 CLuaObject CLuaObject::at(const char *key) const
280 if (!isEnumerable()) throw ELuaNotATable(NLMISC::toString("Can't get key '%s' in object '%s' because type is '%s', it is not a table.", key, getId().c_str(), getTypename()).c_str());
281 return operator[](key);
284 // *************************************************
285 bool CLuaObject::hasKey(const char *key) const
287 if (!isEnumerable()) throw ELuaNotATable(NLMISC::toString("Trying to access key '%s' on object '%s' of type %s.", key, getId().c_str(), getTypename()).c_str());
288 CLuaObject value = operator[](key);
289 return (!value.isNil());
292 // *************************************************
293 CLuaObject CLuaObject::newTable(const char *tableName)
295 nlassert(tableName);
296 nlassert(isValid());
297 if (!isTable()) throw ELuaNotATable(NLMISC::toString("Trying to create a subtable '%s' on object '%s' of type %s (not a table).", tableName, getId().c_str(), getTypename()));
298 CLuaStackChecker lsc(_LuaState);
299 push();
300 _LuaState->push(tableName);
301 _LuaState->newTable();
302 _LuaState->setTable(-3);
303 _LuaState->pop();
304 return at(tableName); //\TODO nico double copy here ...
307 // *************************************************
308 void CLuaObject::setValue(const char *key, const CLuaObject &value)
310 nlassert(key);
311 nlassert(isValid());
312 nlassert(value.isValid());
313 nlassert(getLuaState() == value.getLuaState());
314 if (!isTable()) throw ELuaNotATable(NLMISC::toString("Trying to set a value '%s' on object '%s' of type %s (not a table).", key, getId().c_str(), getTypename()));
315 CLuaStackChecker lsc(_LuaState);
316 push();
317 _LuaState->push(key);
318 value.push();
319 _LuaState->setTable(-3);
320 _LuaState->pop();
323 // *************************************************
324 void CLuaObject::setNil(const char *key)
326 nlassert(key);
327 nlassert(isValid());
328 if (!isTable()) throw ELuaNotATable(NLMISC::toString("Trying to set a value 'nil' at key %s on object '%s' of type %s (not a table).", key, getId().c_str(), getTypename()));
329 CLuaStackChecker lsc(_LuaState);
330 push();
331 _LuaState->push(key);
332 _LuaState->pushNil();
333 _LuaState->setTable(-3);
334 _LuaState->pop();
337 // *************************************************
338 void CLuaObject::setValue(const char *key, const char *value)
340 nlassert(value);
341 nlassert(key);
342 nlassert(isValid());
343 if (!isTable()) throw ELuaNotATable(NLMISC::toString("Trying to set a value '%s' at key %s on object '%s' of type %s (not a table).", value, key, getId().c_str(), getTypename()));
344 CLuaStackChecker lsc(_LuaState);
345 push();
346 _LuaState->push(key);
347 _LuaState->push(value);
348 _LuaState->setTable(-3);
349 _LuaState->pop();
352 // *************************************************
353 void CLuaObject::setValue(const char *key, const std::string &value)
355 setValue(key, value.c_str());
358 // *************************************************
359 void CLuaObject::setValue(const char *key, bool value)
361 nlassert(key);
362 nlassert(isValid());
363 if (!isTable()) throw ELuaNotATable(NLMISC::toString("Trying to set a value '%s' at key %s on object '%s' of type %s (not a table).", value ? "true" : "false", key, getId().c_str(), getTypename()));
364 CLuaStackChecker lsc(_LuaState);
365 push();
366 _LuaState->push(key);
367 _LuaState->push(value);
368 _LuaState->setTable(-3);
369 _LuaState->pop();
372 // *************************************************
373 void CLuaObject::setValue(const char *key, TLuaWrappedFunction value)
375 nlassert(key);
376 nlassert(isValid());
377 if (!isTable()) throw ELuaNotATable(NLMISC::toString("Trying to set a function value '%p' at key %s on object '%s' of type %s (not a table).", (void *)value, key, getId().c_str(), getTypename()));
378 CLuaStackChecker lsc(_LuaState);
379 push();
380 _LuaState->push(key);
381 _LuaState->push(value);
382 _LuaState->setTable(-3);
383 _LuaState->pop();
386 // *************************************************
387 void CLuaObject::setValue(const char *key, double value)
389 nlassert(key);
390 nlassert(isValid());
391 if (!isTable()) throw ELuaNotATable(NLMISC::toString("Trying to set a value '%lf' at key %s on object '%s' of type %s (not a table).", value, key, getId().c_str(), getTypename()));
392 CLuaStackChecker lsc(_LuaState);
393 push();
394 _LuaState->push(key);
395 _LuaState->push(value);
396 _LuaState->setTable(-3);
397 _LuaState->pop();
400 // *************************************************
401 void CLuaObject::setValue(const char *key, uint32 value)
403 nlassert(key);
404 nlassert(isValid());
405 if (!isTable()) throw ELuaNotATable(NLMISC::toString("Trying to set a value '%u' at key %s on object '%s' of type %s (not a table).", value, key, getId().c_str(), getTypename()));
406 CLuaStackChecker lsc(_LuaState);
407 push();
408 _LuaState->push(key);
409 _LuaState->push(value);
410 _LuaState->setTable(-3);
411 _LuaState->pop();
414 // *************************************************
415 void CLuaObject::setValue(const char *key, sint32 value)
417 nlassert(key);
418 nlassert(isValid());
419 if (!isTable()) throw ELuaNotATable(NLMISC::toString("Trying to set a value '%d' at key %s on object '%s' of type %s (not a table).", value, key, getId().c_str(), getTypename()));
420 CLuaStackChecker lsc(_LuaState);
421 push();
422 _LuaState->push(key);
423 _LuaState->push(value);
424 _LuaState->setTable(-3);
425 _LuaState->pop();
428 // *************************************************
429 void CLuaObject::setValue(const char *key, sint64 value)
431 nlassert(key);
432 nlassert(isValid());
433 if (!isTable()) throw ELuaNotATable(NLMISC::toString("Trying to set a value '%" NL_I64 "d' at key %s on object '%s' of type %s (not a table).", value, key, getId().c_str(), getTypename()));
434 CLuaStackChecker lsc(_LuaState);
435 push();
436 _LuaState->push(key);
437 _LuaState->push(value);
438 _LuaState->setTable(-3);
439 _LuaState->pop();
442 // *************************************************
443 void CLuaObject::eraseValue(const char *key)
445 nlassert(isValid());
446 nlassert(key);
447 if (!isTable()) throw ELuaNotATable(NLMISC::toString("Trying to erase a value with key '%s' on object '%s' of type %s (not a table).", key, getId().c_str(), getTypename()));
448 CLuaStackChecker lsc(_LuaState);
449 push();
450 _LuaState->push(key);
451 _LuaState->pushNil();
452 _LuaState->setTable(-3);
453 _LuaState->pop();
456 // *************************************************
457 bool CLuaObject::callNoThrow(int numArgs, int numRet)
459 nlassert(isValid());
460 if (!isFunction())
462 nlwarning("Calling a non function object (id = %s, type = %s)", getId().c_str(), getTypename());
463 _LuaState->pop(numArgs);
464 return false;
466 // TMP TMP
467 static volatile bool dumpFunction = false;
468 if (dumpFunction)
470 CLuaStackRestorer lsr(_LuaState, _LuaState->getTop());
471 push();
472 lua_Debug ar;
473 lua_getinfo (_LuaState->getStatePointer(), ">lS", &ar);
474 nlwarning((std::string(ar.what) + ", at line " + NLMISC::toString(ar.linedefined) + " in " + std::string(ar.source)).c_str());
476 push();
477 _LuaState->insert(-1 - numArgs); // put the function before its arguments
478 int result = _LuaState->pcall(numArgs, numRet);
479 switch (result)
481 case LUA_ERRRUN:
482 case LUA_ERRMEM:
483 case LUA_ERRERR:
484 nlwarning(_LuaState->toString(-1));
485 _LuaState->pop();
486 return false;
487 break;
488 case 0:
489 return true;
490 break;
491 default:
492 nlassert(0);
493 break;
495 return false;
498 // *************************************************
499 bool CLuaObject::callMethodByNameNoThrow(const char *name, int numArgs, int numRet)
501 nlassert(isValid());
502 int initialStackSize = _LuaState->getTop();
503 if (!isTable() && !isUserData())
505 nlwarning("Can't call method : object is not a table (id = %s)", getId().c_str());
506 _LuaState->setTop(std::max(0, initialStackSize - numArgs));
507 return false;
509 CLuaObject method = (*this)[name];
510 push(); // the 'self' parameter
511 _LuaState->insert(-1 - numArgs); // put 'self' before the arguments
512 if (method.callNoThrow(numArgs + 1, numRet))
514 return true;
516 _LuaState->setTop(std::max(0, initialStackSize - numArgs));
517 return false;
520 /////////////////////
521 // CLuaEnumeration //
522 /////////////////////
524 // *************************************************
525 CLuaEnumeration::CLuaEnumeration(CLuaObject &table)
527 nlassert(table.isEnumerable());
528 CLuaState *luaState = table.getLuaState();
529 CLuaStackChecker lsc(luaState);
530 // get pointer to the 'next' function
531 #if LUA_VERSION_NUM >= 502
532 luaState->pushGlobalTable();
533 #else
534 luaState->pushValue(LUA_GLOBALSINDEX);
535 #endif
536 _NextFunction = CLuaObject(*luaState)["next"];
538 nlassert(luaState);
539 table.push();
540 luaState->pushNil();
541 _HasNext = false;
542 if (_NextFunction.callNoThrow(2, 2))
544 _Value.pop(*luaState);
545 _Key.pop(*luaState);
546 _HasNext = !_Key.isNil();
547 _Value.setId(CLuaObject::concatId(table.getId(), _Key.toString()));
548 _Table = table;
553 // *************************************************
554 const CLuaObject &CLuaEnumeration::nextKey() const
556 nlassert(_HasNext);
557 return _Key;
560 // *************************************************
561 CLuaObject &CLuaEnumeration::nextValue()
563 nlassert(_HasNext);
564 return _Value;
567 // *************************************************
568 CLuaObject CLuaObject::getMetaTable() const
570 nlassert(isValid());
571 CLuaStackChecker lsc(_LuaState);
572 push();
573 if (_LuaState->getMetaTable(-1))
575 _LuaState->remove(-2);
576 return CLuaObject(*_LuaState);
578 _LuaState->pop();
579 _LuaState->pushNil();
580 return CLuaObject(*_LuaState);
583 // *************************************************
584 bool CLuaObject::setMetaTable(CLuaObject &metatable)
586 nlassert(isValid());
587 nlassert(metatable.isValid());
588 nlassert(this->getLuaState() == metatable.getLuaState());
589 CLuaStackChecker lsc(_LuaState);
590 push();
591 metatable.push();
592 bool ok = _LuaState->setMetaTable(-2);
593 _LuaState->pop(1);
594 return ok;
597 // *************************************************
598 std::string CLuaObject::toStringRecurse(uint depth /*=0*/, uint maxDepth /*= 20*/, std::set<const void *> *alreadySeen /*= NULL */) const
600 if (maxDepth != 0 && depth > maxDepth) return "";
601 const uint INDENT_NUM_BLANK = 2;
602 std::string indentStr(depth * INDENT_NUM_BLANK, ' ');
603 nlassert(_LuaState);
604 if (isEnumerable()) // is enumeration possible on that object ?
606 std::string result;
607 if (alreadySeen)
609 if (alreadySeen->count(toPointer())) // avoid cyclic graph (infinite recursion else)
611 result += indentStr +"<cycle>";
612 return result;
614 alreadySeen->insert(toPointer());
616 result += indentStr + "{\n";
617 CLuaObject *table = const_cast<CLuaObject *>(this);
618 uint numElem = 0;
619 ENUM_LUA_TABLE(*table, it)
621 //nlwarning("entering table %s", it.nextKey().toString().c_str());
622 result += std::string((depth + 1) * INDENT_NUM_BLANK, ' ') + it.nextKey().toString() + " = ";
623 if (it.nextValue().isEnumerable())
625 result += "\n" + it.nextValue().toStringRecurse(depth + 1, maxDepth, alreadySeen);
627 else
629 result += it.nextValue().toStringRecurse();
631 result += ",\n";
632 ++ numElem;
633 if (numElem > 4000)
635 throw NLMISC::Exception("possible infinite loop, aborting enumeration");
638 result += indentStr + "}";
639 return result;
641 else if (isNil())
643 return (indentStr + "nil").c_str();
645 else if (isString())
647 return (indentStr + "\"" + toString() + "\"").c_str();
649 else if (isFunction())
651 return (indentStr + "<function>").c_str();
653 else
655 return ((indentStr + toString()).c_str());
659 // *************************************************
660 void CLuaObject::dump(uint maxDepth /*= 20*/, std::set<const void *> *alreadySeen /*= NULL */) const
664 std::string str = toStringRecurse(0, maxDepth, alreadySeen);
665 std::vector<std::string> res;
666 NLMISC::explode(str, std::string("\n"), res);
667 for(uint k = 0; k < res.size(); ++k)
669 NLMISC::InfoLog->forceDisplayRaw((res[k] + "\n") .c_str());
672 catch(const std::exception &e)
674 //CLuaIHMRyzom::dumpCallStack();
675 nlwarning(e.what());
679 // *************************************************
680 std::string CLuaObject::concatId(const std::string &left,const std::string &right)
682 if (!right.empty() && isdigit(right[0]))
684 if (left.empty()) return "[" + right + "]";
685 return left + "[" + right + "]";
687 if (left.empty()) return right;
688 return left + "." + right;
692 // *************************************************
693 void CLuaEnumeration::next()
695 nlassert(_HasNext);
696 CLuaState *luaState = _Table.getLuaState();
697 nlassert(luaState);
698 CLuaStackChecker lsc(luaState);
699 _Table.push();
700 _Key.push();
701 _HasNext = false;
702 if (_NextFunction.callNoThrow(2, 2))
704 _Value.pop(*luaState);
705 _Key.pop(*luaState);
706 _HasNext = !_Key.isNil();
707 _Value.setId(_Table.getId() + "." + _Key.toString());