mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 08:15:59 -10:00
* Automatically generate the isValid() Lua API function for handle types If an ADE object's stored type has an isValid() member, ade_obj now registers an isValid function for it automatically. An explicit ADE_FUNC(isValid, ...) declared after the ADE_OBJ replaces the generated one, so types with special semantics (e.g. sound) keep their own. The 67 hand-written functions that merely delegated to the member are removed, and 26 types that previously exposed no isValid() gain one. Co-Authored-By: Claude Fable 5.1 <[email protected]> * Add ADE_OBJ_VALIDATOR for index-based Lua handles Int-index handles (shipclass, weaponclass, team, etc.) can now declare their validity predicate with a single ADE_OBJ_VALIDATOR or ADE_OBJ_VALIDATOR_RANGE line after the ADE_OBJ. The registered validator drives the generated isValid() function and is also honored by ade_set_args when the mod table asks for nil instead of invalid objects, which previously never applied to these handles. The 19 hand-written range-check functions are replaced, and loadout_ship, default_primary, default_secondary, and support_rearm_pool_team gain isValid(). Add a validator to HudGauge while we're at it. Also fully qualify the scripting namespace in LuaConvert.h so its templates can be instantiated from the unit tests. Co-Authored-By: Claude Fable 5.1 <[email protected]> --------- Co-authored-by: Claude Fable 5.1 <[email protected]>
332 lines
8.0 KiB
C++
332 lines
8.0 KiB
C++
//
|
|
//
|
|
|
|
#ifndef FS2_OPEN_ADE_H
|
|
#define FS2_OPEN_ADE_H
|
|
|
|
#include "globalincs/pstypes.h"
|
|
#include "globalincs/version.h"
|
|
#include "globalincs/utility.h"
|
|
|
|
#include "object/object.h"
|
|
#include "scripting/ade_doc.h"
|
|
|
|
extern "C" {
|
|
#include <lauxlib.h>
|
|
#include <lualib.h>
|
|
}
|
|
|
|
#include <memory>
|
|
|
|
/**
|
|
* @defgroup ade_api ADE API functions
|
|
*
|
|
* @brief Functions and macros used in the ADE scripting API
|
|
*
|
|
* These functions enable the code to communicate with external scripts and expose an API for them to use
|
|
*/
|
|
|
|
// lua_tostring will return NULL if and only if it cannot convert to a string; nil values are converted to "nil"
|
|
#define lua_tostring_nullsafe(L,i) coalesce(lua_tostring(L,i), "<UNABLE TO CONVERT TO STRING>")
|
|
|
|
namespace scripting {
|
|
|
|
// Forward definition
|
|
struct DocumentationElement;
|
|
|
|
using DocumentationErrorReporter = std::function<void(const SCP_string& errorMessage)>;
|
|
|
|
/**
|
|
*
|
|
* @param L
|
|
* @param stackdump
|
|
*
|
|
* @ingroup ade_api
|
|
*/
|
|
void ade_stackdump(lua_State* L, char* stackdump);
|
|
|
|
/**
|
|
*
|
|
* @param L
|
|
* @return
|
|
*
|
|
* @ingroup ade_api
|
|
*/
|
|
int ade_friendly_error(lua_State* L);
|
|
|
|
//*************************Lua types*************************
|
|
// Value fo ade_odata::size for when buf contains a pointer
|
|
const size_t ODATA_PTR_SIZE = (size_t) -1;
|
|
|
|
const int ADE_FUNCNAME_UPVALUE_INDEX = 1;
|
|
const int ADE_SETTING_UPVALUE_INDEX = 2;
|
|
const int ADE_OBJ_UPVALUE_INDEX = 3; // Upvalue which stores the reference to the ade_obj for destructors and generated functions
|
|
#define ADE_SETTING_VAR lua_toboolean(L,lua_upvalueindex(ADE_SETTING_UPVALUE_INDEX))
|
|
|
|
template <typename T>
|
|
struct ade_odata_getter {
|
|
size_t idx;
|
|
T* value_ptr;
|
|
|
|
ade_odata_getter(size_t idx_in, T* ptr_in) : idx(idx_in), value_ptr(ptr_in) {}
|
|
};
|
|
|
|
template <typename T>
|
|
struct ade_odata_ptr_getter {
|
|
size_t idx;
|
|
T** value_ptr;
|
|
|
|
ade_odata_ptr_getter(size_t idx_in, T** ptr_in) : idx(idx_in), value_ptr(ptr_in) {}
|
|
};
|
|
|
|
template <typename T>
|
|
struct ade_odata_setter {
|
|
size_t idx;
|
|
T value;
|
|
|
|
ade_odata_setter(size_t idx_in, T value_in) : idx(idx_in), value(std::move(value_in)) {}
|
|
};
|
|
|
|
class ade_table_entry;
|
|
|
|
using ade_serialize_func = void(*)(lua_State*, const scripting::ade_table_entry&, const luacpp::LuaValue&, ubyte*, int&);
|
|
using ade_deserialize_func = void(*)(lua_State*, const scripting::ade_table_entry&, char*, ubyte*, int&);
|
|
|
|
//WMC - 'Type' is the same as ade_set_args,
|
|
//plus some extra
|
|
//b - boolean
|
|
//d - double
|
|
//f - float
|
|
//i - integer
|
|
//s - string
|
|
//x - fix
|
|
//o - object
|
|
//EXTRA:
|
|
//l - library //WMC - no longer exists
|
|
//u - function
|
|
//v - virtual variable
|
|
//
|
|
//u - oh wait...
|
|
|
|
/**
|
|
* @ingroup ade_api
|
|
*/
|
|
class ade_table_entry {
|
|
public:
|
|
const char* Name = nullptr;
|
|
const char* ShortName = nullptr;
|
|
|
|
//Important stuff
|
|
size_t Idx = UINT_MAX;
|
|
size_t ParentIdx = UINT_MAX;
|
|
size_t DerivatorIdx = UINT_MAX;
|
|
//ade_id AdeID;
|
|
//ade_id DerivatorID; //Who do we derive from
|
|
|
|
//Type-specific
|
|
bool Instanced = false; //Is this a single instance?
|
|
char Type = '\0';
|
|
|
|
//Functions/virtfuncs
|
|
lua_CFunction Function = nullptr;
|
|
|
|
// Reference to the ade_obj, passed as an upvalue to Function (for generated functions) and Destructor
|
|
void* Obj_upvalue = nullptr;
|
|
|
|
// For Objects, an optional validity predicate taking a pointer to the stored value. Set via ADE_OBJ_VALIDATOR;
|
|
// takes precedence over the stored type's isValid() member.
|
|
bool (*Validator)(const void*) = nullptr;
|
|
|
|
// For Objects, the destructor of the object
|
|
lua_CFunction Destructor = nullptr;
|
|
|
|
size_t Size = 0;
|
|
ade_serialize_func Serializer = nullptr;
|
|
ade_deserialize_func Deserializer = nullptr;
|
|
|
|
//Metadata
|
|
ade_overload_list Arguments;
|
|
const char* Description = nullptr;
|
|
const char* ReturnType;
|
|
const char* ReturnDescription = nullptr;
|
|
gameversion::version DeprecationVersion;
|
|
const char* DeprecationMessage = nullptr;
|
|
|
|
//Subentries, of course
|
|
//WMC - I have HAD it with these motherfriendly vectors
|
|
//on this motherfriendly class.
|
|
size_t Num_subentries = 0;
|
|
size_t Subentries[256];
|
|
|
|
private:
|
|
//*****Internal functions
|
|
//int IndexHandler(lua_State *L);
|
|
|
|
public:
|
|
//*****Constructors
|
|
ade_table_entry();
|
|
|
|
//*****Operators
|
|
//ade_table_entry &operator = (const ade_table_entry &ate);
|
|
|
|
//*****Functions
|
|
size_t AddSubentry(ade_table_entry& n_ate);
|
|
int SetTable(lua_State* L, int p_amt_ldx, int p_mtb_ldx);
|
|
std::unique_ptr<DocumentationElement> ToDocumentationElement(
|
|
const scripting::DocumentationErrorReporter& errorReporter);
|
|
|
|
//*****Get
|
|
const char* GetName() const;
|
|
|
|
SCP_string GetFullPath() const;
|
|
};
|
|
|
|
/**
|
|
* @ingroup ade_api
|
|
*/
|
|
class ade_manager {
|
|
SCP_vector<ade_table_entry> _table_entries;
|
|
|
|
SCP_vector<SCP_string> _type_names;
|
|
|
|
ade_manager();
|
|
public:
|
|
static ade_manager* getInstance();
|
|
|
|
// Disallow copying
|
|
ade_manager(const ade_manager&) = delete;
|
|
ade_manager& operator=(const ade_manager&) = delete;
|
|
|
|
// Disallow moving
|
|
ade_manager(ade_manager&&) = delete;
|
|
ade_manager& operator=(ade_manager&&) = delete;
|
|
|
|
size_t addTableEntry(const ade_table_entry& entry);
|
|
|
|
ade_table_entry& getEntry(size_t idx);
|
|
const ade_table_entry& getEntry(size_t idx) const;
|
|
|
|
size_t getNumEntries() const { return _table_entries.size(); }
|
|
|
|
const SCP_vector<SCP_string>& getTypeNames() const;
|
|
};
|
|
|
|
/**
|
|
* @ingroup ade_api
|
|
*/
|
|
void ade_stackdump(lua_State *L, char *stackdump);
|
|
|
|
/**
|
|
* @ingroup ade_api
|
|
*/
|
|
int ade_friendly_error(lua_State* L);
|
|
|
|
/**
|
|
* @ingroup ade_api
|
|
*/
|
|
const char* ade_get_type_string(lua_State* L, int argnum);
|
|
|
|
/**
|
|
* @ingroup ade_api
|
|
*/
|
|
bool ade_is_internal_type(const char* typeName);
|
|
|
|
template <typename T, typename = int>
|
|
struct ade_is_valid : std::false_type
|
|
{
|
|
static inline bool get(const T& /*t*/)
|
|
{
|
|
//Things without an isValid are always considered valid from this point of view.
|
|
return true;
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
struct ade_is_valid <T, decltype((void)(std::declval<T>().isValid()), 0)> : std::true_type
|
|
{
|
|
static inline bool get(const T& t)
|
|
{
|
|
//Things with an isValid return that.
|
|
return t.isValid();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief Determines whether a value stored in an API object is valid
|
|
*
|
|
* A validator registered (via ADE_OBJ_VALIDATOR) on the object's table entry, or on that of any object it derives
|
|
* from, takes precedence; otherwise the stored type's isValid() member is used, and types without one are always
|
|
* considered valid.
|
|
*
|
|
* @param idx The table entry index of the API object
|
|
* @param value The stored value
|
|
*/
|
|
template <typename T>
|
|
bool ade_object_is_valid(size_t idx, const T& value)
|
|
{
|
|
for (size_t i = idx; i != UINT_MAX; i = ade_manager::getInstance()->getEntry(i).DerivatorIdx)
|
|
{
|
|
const auto& entry = ade_manager::getInstance()->getEntry(i);
|
|
if (entry.Validator != nullptr)
|
|
return entry.Validator(&value);
|
|
}
|
|
|
|
return ade_is_valid<T>::get(value);
|
|
}
|
|
|
|
/**
|
|
* @brief Converts an object index to something that can be used with ade_set_args.
|
|
*
|
|
* This respects the actual type of the object so all appropriate functions are available in Lua.
|
|
*
|
|
* @warning This is only used internally and should not be used by API code. Use ade_set_object_with_breed instead.
|
|
*
|
|
* @param obj_idx The object index
|
|
* @return The ade odata
|
|
*/
|
|
ade_odata_setter<object_h> ade_object_to_odata(int obj_idx);
|
|
|
|
/**
|
|
* @brief Sets an object parameter with the right type
|
|
*
|
|
* This should be used everywhere where an object value is returned to make sure that the scripter has access to
|
|
* all API functions.
|
|
*
|
|
* @param L The lua state
|
|
* @param obj_idx The object number
|
|
* @return The return value of ade_set_args
|
|
*
|
|
* @author WMC
|
|
* @ingroup ade_api
|
|
*/
|
|
int ade_set_object_with_breed(lua_State* L, int obj_idx);
|
|
|
|
/**
|
|
* @brief Loads and executes a default lua script
|
|
*
|
|
* This uses the specified file name and either retrieves it from the default files or uses it as a file name if the mod
|
|
* option is enabled.
|
|
*
|
|
* @param L The lua state
|
|
* @param name The name of the script file
|
|
*
|
|
* @ingroup ade_api
|
|
*/
|
|
void load_default_script(lua_State* L, const char* name);
|
|
|
|
//Struct for converting one string for another. whee!
|
|
struct string_conv {
|
|
const char *src;
|
|
const char *dest;
|
|
};
|
|
|
|
const string_conv* ade_get_operator(const char *funcname);
|
|
|
|
namespace internal {
|
|
|
|
ade_table_entry& getTableEntry(size_t idx);
|
|
}
|
|
} // namespace scripting
|
|
|
|
#endif // FS2_OPEN_ADE_H
|