Moved argument validation to opcodeMetaTable. Now old-style opcode and sfall_funcX scripting functions work almost identical and their handlers can be interchanged.

Fixed some bugs with sfall_funcX.
This commit is contained in:
phobos2077
2016-11-06 17:37:25 +07:00
parent 4cc9cb0b83
commit 4ef88959e3
4 changed files with 73 additions and 39 deletions
+7 -7
View File
@@ -189,10 +189,10 @@
#define party_member_list_all party_member_list(1) #define party_member_list_all party_member_list(1)
#define spatial_radius(obj) sfall_func1("spatial_radius", obj) #define spatial_radius(obj) sfall_func1("spatial_radius", obj)
#define critter_inven_obj2(obj, y) sfall_func2("critter_inven_obj2", obj, type) #define critter_inven_obj2(obj, type) sfall_func2("critter_inven_obj2", obj, type)
#define intface_redraw sfall_func0("intface_redraw") #define intface_redraw sfall_func0("intface_redraw")
#define intface_hide sfall_func0("intface_hide") #define intface_hide sfall_func0("intface_hide")
#define intface_show sfall_func0("intface_show") #define intface_show sfall_func0("intface_show")
#define intface_is_hidden sfall_func0("intface_is_hidden") #define intface_is_hidden sfall_func0("intface_is_hidden")
#define exec_map_update_scripts sfall_func0("exec_map_update_scripts") #define exec_map_update_scripts sfall_func0("exec_map_update_scripts")
+39 -9
View File
@@ -49,7 +49,13 @@ void _stdcall HandleMapUpdateForScripts(DWORD procId);
#define DATATYPE_MASK_VALID_OBJ (DATATYPE_MASK_INT | DATATYPE_MASK_NOT_NULL) #define DATATYPE_MASK_VALID_OBJ (DATATYPE_MASK_INT | DATATYPE_MASK_NOT_NULL)
struct SfallOpcodeMetadata { struct SfallOpcodeMetadata {
// opcode handler, will be used as key
void (*handler)(); void (*handler)();
// opcode name, only used for logging
const char* name;
// argument validation masks
int argTypeMasks[OP_MAX_ARGUMENTS]; int argTypeMasks[OP_MAX_ARGUMENTS];
}; };
@@ -216,14 +222,14 @@ public:
_ret = val; _ret = val;
} }
// resets the state of handler // resets the state of handler for new opcode invocation
void resetState(TProgram* program = nullptr) { void resetState(TProgram* program, int argNum) {
_program = program; _program = program;
// reset return value // reset return value
_ret = ScriptValue(); _ret = ScriptValue();
// reset argument list // reset argument list
_args.resize(0); _args.resize(argNum);
// reset arg shift // reset arg shift
_argShift = 0; _argShift = 0;
} }
@@ -276,7 +282,7 @@ public:
assert(argNum < OP_MAX_ARGUMENTS); assert(argNum < OP_MAX_ARGUMENTS);
// reset state after previous // reset state after previous
resetState(program); resetState(program, argNum);
// process arguments on stack (reverse order) // process arguments on stack (reverse order)
for (int i = argNum - 1; i >= 0; i--) { for (int i = argNum - 1; i >= 0; i--) {
@@ -287,14 +293,27 @@ public:
// retrieve string argument // retrieve string argument
if (type == DATATYPE_STR) { if (type == DATATYPE_STR) {
_args.push_back(InterpretGetString(program, rawValue, rawValueType)); _args.at(i) = InterpretGetString(program, rawValue, rawValueType);
} else { } else {
_args.push_back(ScriptValue(type, rawValue)); _args.at(i) = ScriptValue(type, rawValue);
} }
} }
// flag that arguments passed are valid
bool argumentsValid = true;
// call opcode handler // check if metadata is available
func(); OpcodeMetaTableType::iterator it = opcodeMetaTable.find(func);
if (it != opcodeMetaTable.end()) {
const SfallOpcodeMetadata* meta = it->second;
// automatically validate argument types
argumentsValid = validateArguments(meta->argTypeMasks, argNum, meta->name);
}
// call opcode handler if arguments are valid (or no automatic validation was done)
if (argumentsValid) {
func();
}
// process return value // process return value
if (hasReturn) { if (hasReturn) {
@@ -339,9 +358,20 @@ static OpcodeHandler opHandler;
#include "ScriptOps\MiscOps.hpp" #include "ScriptOps\MiscOps.hpp"
#include "ScriptOps\MetaruleOp.hpp" #include "ScriptOps\MetaruleOp.hpp"
/*
Array for opcodes metadata.
This is completely optional, added for convenience only.
By adding opcode to this array, Sfall will automatically validate it's arguments using provided info.
On fail, errors will be printed to debug.log and opcode will not be executed.
If you don't include opcode in this array, you should take care of all argument validation inside handler itself.
*/
static const SfallOpcodeMetadata opcodeMetaArray[] = { static const SfallOpcodeMetadata opcodeMetaArray[] = {
{op_message_str_game, {}} {sf_test, "validate_test", {DATATYPE_MASK_INT, DATATYPE_MASK_INT | DATATYPE_MASK_FLOAT, DATATYPE_MASK_STR, DATATYPE_NONE}},
{sf_spatial_radius, "spatial_radius", {DATATYPE_MASK_VALID_OBJ}},
{sf_critter_inven_obj2, "critter_inven_obj2", {DATATYPE_MASK_VALID_OBJ, DATATYPE_MASK_INT}},
//{op_message_str_game, {}}
}; };
static void InitOpcodeMetaTable() { static void InitOpcodeMetaTable() {
+24 -20
View File
@@ -28,8 +28,8 @@
// Metarule is a universal opcode(s) for all kinds of new sfall scripting functions. // Metarule is a universal opcode(s) for all kinds of new sfall scripting functions.
// Prefix all function handlers with sf_ and add them to sfall_metarule_table. // Prefix all function handlers with sf_ and add them to sfall_metarule_table.
// DO NOT add arguments and/or return values to function handlers! // DO NOT add arguments and/or return values to function handlers!
// Use functions GetOpArgXXX(), IsOpArgXXX() inside handler function to read arguments, but argument 0 is always function name. // Use opHandler.arg(i), inside handler function to access arguments.
// Use opHandler.setReturn() to set return value. // Use opHandler.setReturn(x) to set return value.
// If you want to call user-defined procedures in your handler, use RunScriptProc(). // If you want to call user-defined procedures in your handler, use RunScriptProc().
struct SfallMetarule { struct SfallMetarule {
@@ -41,8 +41,6 @@ struct SfallMetarule {
int minArgs; int minArgs;
// maximum number of arguments // maximum number of arguments
int maxArgs; int maxArgs;
// type masks of arguments DATATYPE_MASK_* (use bitwise OR to combine, use 0 to disable type validation)
int argTypeMasks[6];
}; };
typedef std::tr1::unordered_map<std::string, const SfallMetarule*> MetaruleTableType; typedef std::tr1::unordered_map<std::string, const SfallMetarule*> MetaruleTableType;
@@ -58,7 +56,7 @@ static std::string sf_test_stringBuf;
static void sf_test() { static void sf_test() {
std::ostringstream sstream; std::ostringstream sstream;
sstream << "sfall_funcX(\"test\""; sstream << "sfall_funcX(\"test\"";
for (int i = 1; i < opHandler.numArgs(); i++) { for (int i = 0; i < opHandler.numArgs(); i++) {
const ScriptValue &arg = opHandler.arg(i); const ScriptValue &arg = opHandler.arg(i);
sstream << ", "; sstream << ", ";
switch (arg.type()) { switch (arg.type()) {
@@ -99,23 +97,21 @@ static void sf_get_metarule_table() {
Add your custom scripting functions here. Add your custom scripting functions here.
Format is as follows: Format is as follows:
{ name, handler, minArgs, maxArgs, {MASK1, MASK2, ...} } { name, handler, minArgs, maxArgs }
- name - name of function that will be used to call it from scripts, - name - name of function that will be used to call it from scripts,
- handler - pointer to handler function (see examples above), - handler - pointer to handler function (see examples below),
- minArgs/maxArgs - minimum and maximum number of arguments allowed for this function, - minArgs/maxArgs - minimum and maximum number of arguments allowed for this function
- MASK1, MASK2, ... - validation parameters for each argument as bit masks (see DATATYPE_MASK_* defines)
*/ */
static const SfallMetarule metaruleArray[] = { static const SfallMetarule metaruleArray[] = {
{"test", sf_test, 0, 6, {}}, {"get_metarule_table", sf_get_metarule_table, 0, 0},
{"get_metarule_table", sf_get_metarule_table, 0, 0, {}}, {"validate_test", sf_test, 2, 5},
{"validate_test", sf_test, 2, 5, {DATATYPE_MASK_INT, DATATYPE_MASK_INT | DATATYPE_MASK_FLOAT, DATATYPE_MASK_STR, DATATYPE_NONE}}, {"spatial_radius", sf_spatial_radius, 1, 1},
{"spatial_radius", sf_spatial_radius, 1, 1, {DATATYPE_MASK_VALID_OBJ}}, {"critter_inven_obj2", sf_critter_inven_obj2, 2, 2},
{"critter_inven_obj2", sf_critter_inven_obj2, 2, 2, {DATATYPE_MASK_VALID_OBJ, DATATYPE_MASK_INT}}, {"intface_redraw", sf_intface_redraw, 0, 0},
{"intface_redraw", sf_intface_redraw, 0, 0, {}}, {"intface_show", sf_intface_show, 0, 0},
{"intface_show", sf_intface_show, 0, 0, {}}, {"intface_hide", sf_intface_hide, 0, 0},
{"intface_hide", sf_intface_hide, 0, 0, {}}, {"intface_is_hidden", sf_intface_is_hidden, 0, 0},
{"intface_is_hidden", sf_intface_is_hidden, 0, 0, {}}, {"exec_map_update_scripts", sf_exec_map_update_scripts, 0, 0},
{"exec_map_update_scripts", sf_exec_map_update_scripts, 0, 0, {}},
}; };
static void InitMetaruleTable() { static void InitMetaruleTable() {
@@ -139,8 +135,16 @@ static bool ValidateMetaruleArguments(const SfallMetarule* metaruleInfo) {
return false; return false;
} else { } else {
return opHandler.validateArguments(metaruleInfo->argTypeMasks, argCount, metaruleInfo->name); // check if metadata is available for this handler
OpcodeMetaTableType::iterator it = opcodeMetaTable.find(metaruleInfo->func);
if (it != opcodeMetaTable.end()) {
const SfallOpcodeMetadata* meta = it->second;
// automatically validate argument types
return opHandler.validateArguments(meta->argTypeMasks, argCount, metaruleInfo->name);
}
} }
return true;
} }
static void _stdcall op_sfall_metarule_handler() { static void _stdcall op_sfall_metarule_handler() {
+3 -3
View File
@@ -159,7 +159,7 @@ static void __declspec(naked) op_create_spatial() {
} }
static void sf_spatial_radius() { static void sf_spatial_radius() {
TGameObj* spatialObj = opHandler.arg(1).asObject(); TGameObj* spatialObj = opHandler.arg(0).asObject();
TScript* script; TScript* script;
if (ScrPtr(spatialObj->scriptID, &script) != -1) { if (ScrPtr(spatialObj->scriptID, &script) != -1) {
opHandler.setReturn(script->spatial_radius); opHandler.setReturn(script->spatial_radius);
@@ -536,8 +536,8 @@ static void __declspec(naked) op_obj_is_carrying_obj() {
} }
static void sf_critter_inven_obj2() { static void sf_critter_inven_obj2() {
TGameObj* critter = opHandler.arg(1).asObject(); TGameObj* critter = opHandler.arg(0).asObject();
int slot = opHandler.arg(2).asInt(); int slot = opHandler.arg(1).asInt();
switch (slot) { switch (slot) {
case 0: case 0:
opHandler.setReturn(InvenWorn(critter)); opHandler.setReturn(InvenWorn(critter));