// // #include "scripting/ade.h" #include "globalincs/version.h" #include "ade.h" #include "ade_api.h" #include "ade_args.h" #include "doc_parser.h" #include "scripting.h" #include "scripting_doc.h" #include "def_files/def_files.h" #include "mod_table/mod_table.h" #include "network/multi.h" #include "network/multimsgs.h" #include "scripting/api/objs/asteroid.h" #include "scripting/api/objs/beam.h" #include "scripting/api/objs/debris.h" #include "scripting/api/objs/object.h" #include "scripting/api/objs/ship.h" #include "scripting/api/objs/waypoint.h" #include "scripting/api/objs/weapon.h" #include "scripting/lua/LuaFunction.h" #include "ship/ship.h" namespace { using namespace scripting; //1: Userspace variables (ie in object table) //2: Handle-specific values //3: Entries in metatable (ie defined by ADE) //4: Virtual variables //5: Use the indexer, if possible //6: Set userspace variable //7: Set handle-specific variables //X: Mission failed. // //On the stack when this is called: //Index 1 - Object (Can be anything with Lua 5.1; Number to a library) //Index 2 - String (ie the key we're trying to access; Object.string, Object:string, Object['string'], etc) //Index 3 - (Optional) Argument we are trying to set Object.String = Argument int ade_index_handler(lua_State* L) { Assert(L != NULL); const int obj_ldx = 1; const int key_ldx = 2; const int arg_ldx = 3; int last_arg_ldx = lua_gettop(L); const char* type_name = NULL; uint ade_id = UINT_MAX; int mtb_ldx = INT_MAX; ade_table_entry* entry = 0; //*****STEP 1: Check for user-defined objects if (lua_istable(L, obj_ldx) && !ADE_SETTING_VAR) { lua_pushvalue(L, key_ldx); lua_rawget(L, obj_ldx); if (!lua_isnil(L, -1)) { return 1; } else lua_pop(L, 1); //nil value } //*****STEP 1.5: Set-up metatable if (lua_getmetatable(L, obj_ldx)) { mtb_ldx = lua_gettop(L); lua_pushcfunction(L, ade_friendly_error); int err_ldx = lua_gettop(L); int i; //*****WMC - go for the type name lua_pushstring(L, "__adeid"); lua_rawget(L, mtb_ldx); if (lua_isnumber(L, -1)) { ade_id = (uint) lua_tonumber(L, -1); if (ade_id < ade_manager::getInstance()->getNumEntries()) { entry = &ade_manager::getInstance()->getEntry(ade_id); type_name = entry->Name; } } lua_pop(L, 1); //*****STEP 2: Check for __ademember objects (ie defaults) lua_pushstring(L, "__ademembers"); lua_rawget(L, mtb_ldx); if (lua_istable(L, -1)) { int amt_ldx = lua_gettop(L); lua_pushvalue(L, key_ldx); lua_rawget(L, amt_ldx); if (!lua_isnil(L, -1)) { return 1; } else lua_pop(L, 1); //nil value } lua_pop(L, 1); //member table //*****STEP 3: Check for virtual variables lua_pushstring(L, "__virtvars"); lua_rawget(L, mtb_ldx); if (lua_istable(L, -1)) { //Index virtvar function int vvt_ldx = lua_gettop(L); lua_pushvalue(L, key_ldx); lua_rawget(L, vvt_ldx); if (lua_isfunction(L, -1)) { //Set upvalue lua_pushvalue(L, lua_upvalueindex(ADE_SETTING_UPVALUE_INDEX)); if (lua_setupvalue(L, -2, ADE_SETTING_UPVALUE_INDEX) == NULL) { LuaError(L, "Unable to set upvalue for virtual variable"); } //Set arguments //WMC - Skip setting the key lua_pushvalue(L, obj_ldx); int numargs = 1; for (i = arg_ldx; i <= last_arg_ldx; i++) { lua_pushvalue(L, i); numargs++; } //Execute function lua_pcall(L, numargs, LUA_MULTRET, err_ldx); return (lua_gettop(L) - vvt_ldx); } else { lua_pop(L, 1); //non-function value } } lua_pop(L, 1); //virtvar table //*****STEP 4: Use the indexer //NOTE: Requires metatable from step 1.5 //Get indexer lua_pushstring(L, "__indexer"); lua_rawget(L, mtb_ldx); if (lua_isfunction(L, -1)) { //Function already on stack //Set upvalue lua_pushvalue(L, lua_upvalueindex(ADE_SETTING_UPVALUE_INDEX)); if (lua_setupvalue(L, -2, ADE_SETTING_UPVALUE_INDEX) == NULL) { LuaError(L, "Unable to set upvalue for indexer"); } //Set arguments for (i = 1; i <= last_arg_ldx; i++) { lua_pushvalue(L, i); } //Execute function lua_pcall(L, last_arg_ldx, LUA_MULTRET, err_ldx); return (lua_gettop(L) - err_ldx); } lua_pop(L, 2); //WMC - Don't need __indexer or error handler } //*****STEP 6: Set a new variable or die. if (ADE_SETTING_VAR && lua_istable(L, obj_ldx)) { lua_pushvalue(L, key_ldx); lua_pushvalue(L, arg_ldx); lua_rawset(L, obj_ldx); lua_pushvalue(L, key_ldx); lua_rawget(L, obj_ldx); return 1; } lua_pop(L, 1); //WMC - metatable if (type_name != nullptr) { LuaError(L, "Could not find index '%s' in type '%s'", lua_tostring_nullsafe(L, key_ldx), type_name); } else { LuaError(L, "Could not find index '%s'", lua_tostring_nullsafe(L, key_ldx)); } return 0; } ade_table_entry& getTableEntry(size_t idx) { return ade_manager::getInstance()->getEntry(idx); } //*************************Lua operators************************* //These are the various types of operators you can //set in Lua. Use these as function name to activate. // //Format string should be "*o" or "o*", where "*" is the type of //variable you want to deal with. //The order varies with order of variables string_conv ade_Operators[] = { {"__add", "+"}, //var + obj {"__sub", "-"}, //var - obj {"__mul", "*"}, //var * obj {"__div", "/"}, //var / obj {"__mod", "%"}, //var % obj {"__pow", "^"}, //var ^ obj {"__unm", "~"}, //var ~ obj {"__concat", ".."}, //var .. obj {"__len", "#"}, //#var {"__eq", "=="}, //var == obj {"__lt", "<"}, //var < obj {"__le", "<="}, //var <= obj {"__newindex", "="}, //var = obj {"__call", ""}, //*shrug* {"__gc", "__gc"}, //Lua's equivelant of a destructor //WMC - Used with tostring() scripting operator. {"__tostring", "(string)"}, //tostring(var) //WMC - This is NOT a Lua type, but for the LUA_INDEXER define {"__indexer", "[]"}, //obj[var] }; //WMC - Sometimes this gets out of sync between Lua versions static const char *Lua_type_names[] = { "nil", "boolean", "light userdata", "number", "string", "table", "function", "userdata", "thread", "any", // Special type which means any possible type "void", }; const ptrdiff_t Lua_type_names_num = std::distance(std::begin(Lua_type_names), std::end(Lua_type_names)); } namespace scripting { ade_manager* ade_manager::getInstance() { static std::unique_ptr manager(new ade_manager()); return manager.get(); } ade_manager::ade_manager() { _type_names.insert(_type_names.end(), std::begin(Lua_type_names), std::end(Lua_type_names)); } size_t ade_manager::addTableEntry(const ade_table_entry& entry) { _table_entries.push_back(entry); _table_entries.back().Idx = _table_entries.size() - 1; if (entry.Type == 'o' && !entry.Instanced) { // Collect valid type names _type_names.push_back(entry.Name); } return _table_entries.size() - 1; } ade_table_entry& ade_manager::getEntry(size_t idx) { Assertion(idx < _table_entries.size(), "Invalid index " SIZE_T_ARG " specified!", idx); return _table_entries[idx]; } const ade_table_entry& ade_manager::getEntry(size_t idx) const { Assertion(idx < _table_entries.size(), "Invalid index " SIZE_T_ARG " specified!", idx); return _table_entries[idx]; } const SCP_vector& ade_manager::getTypeNames() const { return _type_names; } static int deprecatedFunctionHandler(lua_State* L) { const char* functionName = lua_tostring_nullsafe(L, lua_upvalueindex(1)); LuaError(L, "Deprecated function '%s' has been called that is not available in the targeted engine version. Check " "the documentation for a possible replacement.", functionName); return 0; } ade_table_entry::ade_table_entry() { memset(Subentries, 0, sizeof(Subentries)); } // Think of n_mtb_ldx as the parent metatable int ade_table_entry::SetTable(lua_State* L, int p_amt_ldx, int p_mtb_ldx) { uint i; int cleanup_items = 0; int mtb_ldx = INT_MAX; int data_ldx = INT_MAX; int desttable_ldx = INT_MAX; int amt_ldx = INT_MAX; if (Instanced) { //Set any actual data int nset = 0; switch (Type) { case 'o': { // Create an empty userdata value and use it as the value for this library lua_newuserdata(L, 0); // Create or get object metatable luaL_getmetatable(L, Name); // Set the metatable for the object lua_setmetatable(L, -2); nset++; break; } case 'u': case 'v': { if (DeprecationVersion.isValid() && mod_supports_version(DeprecationVersion.major, DeprecationVersion.minor, DeprecationVersion.build)) { // The deprecation mechanism. Set a function that always errors instead. if (Name != nullptr) { lua_pushstring(L, Name); } else if (ShortName != nullptr) { lua_pushstring(L, ShortName); } else { lua_pushliteral(L, ""); } lua_pushcclosure(L, deprecatedFunctionHandler, 1); } else { // WMC - This hack by taylor is a necessary evil. // 64-bit function pointers do not get passed properly // when using va_args for some reason. lua_pushstring(L, ""); lua_pushboolean(L, 0); lua_pushlightuserdata(L, Obj_upvalue); // upvalue(3) = Reference to ade_obj, if this is a generated function lua_pushcclosure(L, Function, 3); } nset++; break; } default: Assertion(false, "Unhandled value type '%c'!", Type); break; } if (nset) { data_ldx = lua_gettop(L); } else { LuaError(L, "ade_table_entry::SetTable - Could not set data for '%s' (" SIZE_T_ARG ")", GetName(), Idx); } if (data_ldx != INT_MAX) { // Remove data once we are done cleanup_items++; //WMC - Handle virtual variables by getting their table if (Type == 'v') { //Get virtvars table lua_pushstring(L, "__virtvars"); lua_rawget(L, p_mtb_ldx); if (lua_istable(L, -1)) { cleanup_items++; //Virtual variables are stored in virtvar table, //rather than the parent table desttable_ldx = lua_gettop(L); } else { lua_pop(L, 1); } } else { //WMC - Member objects prefixed with __ are assumed to be metatable objects if (strnicmp("__", GetName(), 2) != 0 && lua_istable(L, p_amt_ldx)) { desttable_ldx = p_amt_ldx; } else if (lua_istable(L, p_mtb_ldx)) { desttable_ldx = p_mtb_ldx; } } if (desttable_ldx != INT_MAX) { //If we are setting a function... if (lua_isfunction(L, data_ldx)) { //Set the FIRST upvalue to its name, //so we can always find out what it is for debugging lua_pushstring(L, GetName()); if (lua_setupvalue(L, data_ldx, 1) == NULL) { LuaError(L, "ade_table_entry::SetTable - Could not set upvalue for '%s' (" SIZE_T_ARG ")", GetName(), Idx); } } //Register name and shortname if (Name != NULL) { lua_pushstring(L, Name); lua_pushvalue(L, data_ldx); lua_rawset(L, desttable_ldx); } if (ShortName != NULL) { lua_pushstring(L, ShortName); lua_pushvalue(L, data_ldx); lua_rawset(L, desttable_ldx); } } else { LuaError(L, "ade_table_entry::SetTable - Could not instance '%s' (" SIZE_T_ARG ")", GetName(), Idx); } } } //If subentries, create a metatable pointer and set it if (Num_subentries || (DerivatorIdx != UINT_MAX && ade_manager::getInstance()->getEntry(DerivatorIdx).Num_subentries)) { //Create the new metatable if (!luaL_newmetatable(L, Name)) { LuaError(L, "ade_table_entry::SetTable - Couldn't create metatable for table entry '%s' - does a Lua object already exist with this name?", Name); return 0; } mtb_ldx = lua_gettop(L); cleanup_items++; //Push a copy of the metatable and set it for this object //WMC - Make sure it's instanced, too. This helps keep crashes from happening... if (data_ldx != INT_MAX) { lua_pushvalue(L, mtb_ldx); lua_setmetatable(L, data_ldx); } //***Create index handler entry lua_pushstring(L, "__index"); lua_pushstring(L, "ade_index_handler(get)"); //upvalue(1) = function name lua_pushboolean(L, 0); //upvalue(2) = setting true/false lua_pushcclosure(L, ade_index_handler, 2); lua_rawset(L, mtb_ldx); //***Create newindex handler entry lua_pushstring(L, "__newindex"); lua_pushstring(L, "ade_index_handler(set)"); //upvalue(1) = function name lua_pushboolean(L, 1); //upvalue(2) = setting true/false lua_pushcclosure(L, ade_index_handler, 2); lua_rawset(L, mtb_ldx); if (Destructor != nullptr) { // Set up the destructor of this type if it exists lua_pushstring(L, "__gc"); lua_pushfstring(L, "%s Destructor", GetName()); // upvalue(1) = function name lua_pushboolean(L, 0); // upvalue(2) = setting true/false lua_pushlightuserdata(L, Obj_upvalue); // upvalue(3) = Reference to ade_obj lua_pushcclosure(L, Destructor, 3); lua_rawset(L, mtb_ldx); } //***Create virtvar storage facility lua_pushstring(L, "__virtvars"); lua_newtable(L); lua_rawset(L, mtb_ldx); //***Create ade members table lua_createtable(L, 0, (int) Num_subentries); if (lua_istable(L, -1)) { //WMC - was lua_gettop(L) - 1 for soem amt_ldx = lua_gettop(L); cleanup_items++; //Set it lua_pushstring(L, "__ademembers"); lua_pushvalue(L, amt_ldx); //dup lua_rawset(L, mtb_ldx); } //***Create ID entries lua_pushstring(L, "__adeid"); lua_pushnumber(L, static_cast(Idx)); lua_rawset(L, mtb_ldx); if (DerivatorIdx != UINT_MAX) { lua_pushstring(L, "__adederivid"); lua_pushinteger(L, DerivatorIdx); lua_rawset(L, mtb_ldx); } } if (amt_ldx != INT_MAX) { //Fill out ze metatable if (DerivatorIdx != UINT_MAX) { for (i = 0; i < ade_manager::getInstance()->getEntry(DerivatorIdx).Num_subentries; i++) { ade_manager::getInstance()->getEntry(ade_manager::getInstance()->getEntry(DerivatorIdx).Subentries[i]).SetTable( L, amt_ldx, mtb_ldx); } } for (i = 0; i < Num_subentries; i++) { ade_manager::getInstance()->getEntry(Subentries[i]).SetTable(L, amt_ldx, mtb_ldx); } } //Pop the metatable and data (cleanup) lua_pop(L, cleanup_items); return 1; } size_t ade_table_entry::AddSubentry(ade_table_entry& n_ate) { ade_table_entry ate = n_ate; ate.ParentIdx = Idx; // If a subentry with this name already exists (e.g. an explicitly declared function overriding one that was // automatically generated), replace it in place so that both Lua and the documentation see only the latest one if (ate.Name != nullptr) { for (size_t i = 0; i < Num_subentries; i++) { auto& existing = ade_manager::getInstance()->getEntry(Subentries[i]); if (existing.Name != nullptr && strcmp(existing.Name, ate.Name) == 0) { ate.Idx = existing.Idx; existing = ate; return ate.Idx; } } } size_t new_idx = ade_manager::getInstance()->addTableEntry(ate); //WMC - Oi. Moving the Ade_table_entries vector //invalidates the "this" pointer. Workaround time. auto new_this = &ade_manager::getInstance()->getEntry(ate.ParentIdx); size_t idx = new_this->Num_subentries++; new_this->Subentries[idx] = new_idx; return new_idx; } std::unique_ptr ade_table_entry::ToDocumentationElement( const scripting::DocumentationErrorReporter& errorReporter) { using namespace scripting; if (Name == nullptr && ShortName == nullptr) { Warning(LOCATION, "Data entry with no name or shortname"); return std::unique_ptr(); } std::unique_ptr element; //***Begin entry switch (Type) { case 'o': { if (!Instanced) { // Classes std::unique_ptr obj(new DocumentationElementClass()); obj->type = ElementType::Class; if (DerivatorIdx != UINT_MAX) { obj->superClass = getTableEntry(DerivatorIdx).GetName(); } element = std::move(obj); } else { // Libraries std::unique_ptr obj(new DocumentationElement()); obj->type = ElementType::Library; element = std::move(obj); } break; } case 'u': { // Functions std::unique_ptr obj(new DocumentationElementFunction()); auto ao = ade_get_operator(Name); if (ao != nullptr) { obj->type = ElementType::Operator; } else { obj->type = ElementType::Function; } const auto& typeNames = ade_manager::getInstance()->getTypeNames(); if (ReturnType != nullptr) { type_parser type_parser(typeNames); if (type_parser.parse(ReturnType)) { obj->returnType = type_parser.getType(); } else { if (errorReporter) { errorReporter(GetFullPath() + "(Return Type):\n" + type_parser.getErrorMessage()); } obj->returnType = ade_type_info(); } } else { obj->returnType = ade_type_info(); } for (const auto& overload : Arguments.overloads()) { DocumentationElementFunction::argument_list overloadArgList; argument_list_parser arg_parser(typeNames); if (arg_parser.parse(overload)) { bool optional = false; for (const auto& arg : arg_parser.getArgList()) { if (!optional && (arg.optional || !arg.def_val.empty())) { optional = true; } auto argCopy = arg; argCopy.optional = optional; overloadArgList.arguments.push_back(std::move(argCopy)); } } else { if (errorReporter) { errorReporter(GetFullPath() + "(Arguments):\n" + arg_parser.getErrorMessage()); } overloadArgList.simple.assign(overload); } obj->overloads.push_back(std::move(overloadArgList)); } if (ReturnDescription != nullptr) { obj->returnDocumentation = ReturnDescription; } element = std::move(obj); break; } case 'v': { std::unique_ptr obj(new DocumentationElementProperty()); obj->type = ElementType::Property; const auto& typeNames = ade_manager::getInstance()->getTypeNames(); //***Type Name(ShortName) if (ReturnType != nullptr) { type_parser type_parser(typeNames); if (type_parser.parse(ReturnType)) { obj->getterType = type_parser.getType(); } else { if (errorReporter) { errorReporter(GetFullPath() + "(Getter Type):\n" + type_parser.getErrorMessage()); } obj->getterType = ade_type_info(); } } else { obj->getterType = ade_type_info(); } const SCP_string setterType = Arguments.overloads().front(); if (!setterType.empty()) { type_parser type_parser(typeNames); if (type_parser.parse(setterType)) { obj->setterType = type_parser.getType(); } else { if (errorReporter) { errorReporter(GetFullPath() + "(Setter Type):\n" + type_parser.getErrorMessage()); } obj->setterType = ade_type_info(); } } else { obj->setterType = ade_type_info(); } if (ReturnDescription != nullptr) { obj->returnDocumentation = ReturnDescription; } element = std::move(obj); break; } case 'b': case 'd': case 'f': case 'i': case 's': case 'x': default: Assertion(false, "Unknown Type %c was used used!", Type); return {}; } if (Name != nullptr) { element->name = Name; } if (ShortName != nullptr) { element->shortName = ShortName; } if (Description != nullptr) { element->description = Description; } if (DeprecationVersion.isValid()) { element->deprecationVersion = DeprecationVersion; if (DeprecationMessage != nullptr) { element->deprecationMessage = DeprecationMessage; } } for (uint32_t i = 0; i < Num_subentries; i++) { element->children.emplace_back(getTableEntry(Subentries[i]).ToDocumentationElement(errorReporter)); } return element; } const char* ade_table_entry::GetName() const { if (Name != nullptr) { return Name; } else { return ShortName; } } SCP_string ade_table_entry::GetFullPath() const { SCP_string path(GetName()); size_t currentIdx = ParentIdx; while (currentIdx != UINT_MAX) { const auto& entry = ade_manager::getInstance()->getEntry(currentIdx); SCP_string entryName(entry.GetName()); // Not very efficient but should not be used at runtime so this is fine path.insert(path.begin(), '.'); path.insert(path.begin(), entryName.cbegin(), entryName.cend()); currentIdx = entry.ParentIdx; } return path; } ade_lib::ade_lib(const char* in_name, const ade_lib_handle* parent, const char* in_shortname, const char* in_desc) { ade_table_entry ate; ate.Name = in_name; ate.ShortName = in_shortname; ate.Instanced = true; //WMC - Here's a little hack. //Lua did not work with __len on standard table objects. //So instead, all FS2 libraries are now userdata. //This means that no new functions can be added from //within the scripting environment, but I don't think //there will be any catastrophic consequences. ate.Type = 'o'; ate.Description = in_desc; if(parent != NULL) LibIdx = getTableEntry(parent->GetIdx()).AddSubentry(ate); else LibIdx = ade_manager::getInstance()->addTableEntry(ate); } const char *ade_lib::GetName() const { if(GetIdx() == UINT_MAX) return ""; return getTableEntry(GetIdx()).GetName(); } ade_func::ade_func(const char* name, lua_CFunction func, const ade_lib_handle& parent, ade_overload_list args, const char* desc, const char* ret_type, const char* ret_desc, const gameversion::version& deprecation_version, const char* deprecation_message) { Assertion(strcmp(name, "__gc") != 0, "__gc is a reserved function name! An API function may not use it!"); ade_table_entry ate; ate.Name = name; ate.Instanced = true; ate.Type = 'u'; ate.Function = func; ate.Arguments = std::move(args); ate.Description = desc; ate.ReturnType = ret_type; ate.ReturnDescription = ret_desc; ate.DeprecationVersion = deprecation_version; ate.DeprecationMessage = deprecation_message; LibIdx = ade_manager::getInstance()->getEntry(parent.GetIdx()).AddSubentry(ate); } ade_virtvar::ade_virtvar(const char* name, lua_CFunction func, const ade_lib_handle& parent, const char* args, const char* desc, const char* ret_type, const char* ret_desc, const gameversion::version& deprecation_version, const char* deprecation_message) { Assertion(strcmp(name, "__gc") != 0, "__gc is a reserved function name! An API function may not use it!"); ade_table_entry ate; ate.Name = name; ate.Instanced = true; ate.Type = 'v'; ate.Function = func; ate.Arguments = args; ate.Description = desc; ate.ReturnType = ret_type; ate.ReturnDescription = ret_desc; ate.DeprecationVersion = deprecation_version; ate.DeprecationMessage = deprecation_message; LibIdx = ade_manager::getInstance()->getEntry(parent.GetIdx()).AddSubentry(ate); } ade_indexer::ade_indexer(lua_CFunction func, const ade_lib_handle& parent, ade_overload_list overloads, const char* desc, const char* ret_type, const char* ret_desc) { // Add function for meta ade_table_entry ate; ate.Name = "__indexer"; ate.Instanced = true; ate.Type = 'u'; ate.Function = func; ate.Arguments = std::move(overloads); ate.Description = desc; ate.ReturnType = ret_type; ate.ReturnDescription = ret_desc; LibIdx = ade_manager::getInstance()->getEntry(parent.GetIdx()).AddSubentry(ate); } SCP_string ade_tostring(lua_State *L, int argnum, bool add_typeinfo) { SCP_string buf; //Lua temps double d; int b; const char *s; int type = lua_type(L, argnum); switch(type) { case LUA_TNIL: buf = "nil"; break; case LUA_TNUMBER: d = lua_tonumber(L, argnum); if (add_typeinfo) sprintf(buf, "Number [%f]", d); else sprintf(buf, "%f", d); break; case LUA_TBOOLEAN: b = lua_toboolean(L, argnum); if (add_typeinfo) sprintf(buf, "Boolean [%d]", b); else buf = b != 0 ? "true" : "false"; break; case LUA_TSTRING: s = lua_tostring_nullsafe(L, argnum); if (add_typeinfo) sprintf(buf, "String [%s]", s); else buf = s; break; case LUA_TTABLE: { if (lua_getmetatable(L, argnum)) { lua_pushstring(L, "__adeid"); lua_rawget(L, -2); if (lua_isnumber(L, -1)) sprintf(buf, "Table [%s]", getTableEntry((size_t)lua_tonumber(L, -1)).Name); else buf = "non-default Table"; lua_pop(L, 2); //metatable and nil/adeid } else buf = "Table w/ no metatable"; if (add_typeinfo) { //Maybe get first key? const char* firstkey = nullptr; lua_pushnil(L); if (lua_next(L, argnum)) { firstkey = lua_tostring_nullsafe(L, -2); buf += ", First key: ["; buf += firstkey; buf += "]"; lua_pop(L, 1); //Key } lua_pop(L, 1); //Nil } break; } case LUA_TFUNCTION: { buf = "Function"; auto upname = lua_getupvalue(L, argnum, ADE_FUNCNAME_UPVALUE_INDEX); if (upname != nullptr) { buf += " "; buf += lua_tostring_nullsafe(L, -1); buf += "()"; lua_pop(L, 1); } break; } case LUA_TUSERDATA: if (lua_getmetatable(L, argnum)) { lua_pushstring(L, "__adeid"); lua_rawget(L, -2); if (lua_isnumber(L, -1)) sprintf(buf, "Userdata [%s]", getTableEntry((size_t)lua_tonumber(L, -1)).Name); else buf = "non-default Userdata"; lua_pop(L, 2); //metatable and nil/adeid } else buf = "Userdata w/ no metatable"; break; case LUA_TTHREAD: buf = "Thread"; break; case LUA_TLIGHTUSERDATA: buf = "Light userdata"; break; default: if (add_typeinfo) sprintf(buf, ": %s (%f) (%s)", lua_typename(L, type), lua_tonumber(L, argnum), lua_tostring_nullsafe(L, argnum)); else buf = "Unknown Lua type"; break; } return buf; } //*************************Lua functions************************* //WMC - Spits out the current Lua stack to "stackdump" //This includes variable values, but not names void ade_stackdump(lua_State *L, char *stackdump) { SCP_string buf; int stacksize = lua_gettop(L); for(int argnum = 1; argnum <= stacksize; argnum++) { sprintf(buf, "\r\n%d: ", argnum); strcat(stackdump, buf.c_str()); strcat(stackdump, ade_tostring(L, argnum, true).c_str()); } } int ade_friendly_error(lua_State *L) { LuaError(L); //WMC - According to documentation, this will always be the error //if error handler is called return LUA_ERRRUN; } bool ade_is_internal_type(const char* typeName) { for (int i = 0; i < Lua_type_names_num; i++) { if (!stricmp(Lua_type_names[i], typeName)) { return true; } } return false; } //WMC - Gets type of object const char *ade_get_type_string(lua_State *L, int argnum) { int type = lua_type(L, argnum); if(type < 0 || type >= Lua_type_names_num) return "Unknown"; return Lua_type_names[type]; } ade_odata_setter ade_object_to_odata(int obj_idx) { using namespace scripting::api; if(obj_idx < 0 || obj_idx >= MAX_OBJECTS) return l_Object.Set(object_h()); object *objp = &Objects[obj_idx]; switch(objp->type) { case OBJ_SHIP: return l_Ship.Set(object_h(objp)); case OBJ_ASTEROID: return l_Asteroid.Set(object_h(objp)); case OBJ_DEBRIS: return l_Debris.Set(object_h(objp)); case OBJ_WAYPOINT: return l_Waypoint.Set(object_h(objp)); case OBJ_WEAPON: return l_Weapon.Set(object_h(objp)); case OBJ_BEAM: return l_Beam.Set(object_h(objp)); default: return l_Object.Set(object_h(objp)); } } int ade_set_object_with_breed(lua_State *L, int obj_idx) { return ade_set_args(L, "o", ade_object_to_odata(obj_idx)); } void load_default_script(lua_State* L, const char* name) { using namespace luacpp; SCP_string source; SCP_string source_name; if (Enable_external_default_scripts && cf_exists(name, CF_TYPE_SCRIPTS)) { // Load from disk (or built-in file) source_name = name; auto cfp = cfopen(name, "rb", CF_TYPE_SCRIPTS); Assertion(cfp != nullptr, "Failed to open default file!"); auto length = cfilelength(cfp); source.resize((size_t)length); cfread(&source[0], 1, length, cfp); cfclose(cfp); } else { // Load from default files source_name = SCP_string("default ") + name; auto def = defaults_get_file(name); auto c = reinterpret_cast(def.data); source.assign(c, c + def.size); } try { auto func = LuaFunction::createFromCode(L, source, source_name); func.setErrorFunction(LuaFunction::createFromCFunction(L, ade_friendly_error)); try { func(L); } catch (const LuaException&) { // The execution of the function may also throw an exception but that should have been handled by a LuaError // before } } catch (const LuaException& e) { LuaError(L, "Error while loading default script: %s", e.what()); } } const string_conv* ade_get_operator(const char *funcname) { for(auto iter = std::begin(ade_Operators); iter != std::end(ade_Operators); ++iter) { if(!strcmp(funcname, iter->src)) return &(*iter); } return nullptr; } ade_table_entry& internal::getTableEntry(size_t idx) { return ade_manager::getInstance()->getEntry(idx); } namespace internal { #define DEFINE_MULTI_SERIALIZERS_FUNDAMENTAL(type, multi_accessor) \ template<> \ void ade_multi_deserialize_fundamental(lua_State* /*L*/, const scripting::ade_table_entry& /*tableEntry*/, char* data_ptr, ubyte* data, int& offset) { \ type temp; \ GET_##multi_accessor(temp); \ new(data_ptr) type(std::move(temp)); \ } \ template<> \ void ade_multi_serialize_fundamental(lua_State* L, const scripting::ade_table_entry& /*tableEntry*/, const luacpp::LuaValue& value, ubyte* data, int& packet_size) { \ value.pushValue(L); \ type temp = *reinterpret_cast(lua_touserdata(L, -1)); \ lua_remove(L, -1); \ ADD_##multi_accessor(temp); \ } //These are all fundamental data types supported by our multi code. If you get an unresolved function error //for ade_multi_(de)serialize_fundamental, you need to add an endinaness-safe multi GET_/ADD_ function and //the corresponding DEFINE_MULTI_SERIALIZERS_FUNDAMENTAL macro DEFINE_MULTI_SERIALIZERS_FUNDAMENTAL(int8_t, DATA) DEFINE_MULTI_SERIALIZERS_FUNDAMENTAL(uint8_t, DATA) DEFINE_MULTI_SERIALIZERS_FUNDAMENTAL(int16_t, SHORT) DEFINE_MULTI_SERIALIZERS_FUNDAMENTAL(uint16_t, USHORT) DEFINE_MULTI_SERIALIZERS_FUNDAMENTAL(int32_t, INT) DEFINE_MULTI_SERIALIZERS_FUNDAMENTAL(uint32_t, UINT) DEFINE_MULTI_SERIALIZERS_FUNDAMENTAL(int64_t, LONG) DEFINE_MULTI_SERIALIZERS_FUNDAMENTAL(uint64_t, ULONG) DEFINE_MULTI_SERIALIZERS_FUNDAMENTAL(float, FLOAT) #undef DEFINE_MULTI_SERIALIZERS_FUNDAMENTAL } } lua_net_exception::lua_net_exception(const char* msg) : std::runtime_error(msg) { }