- changed action function interface so that callers can be identified directly, instead of guessing it from looking at the parameters.

With arbitrary PSP layers the old method was no longer safe because the layer ID was not available in the action function.
This commit is contained in:
Christoph Oelckers 2016-06-16 16:11:00 +02:00
parent 7ccdbf9b62
commit cfaa3e3fa9
9 changed files with 54 additions and 32 deletions

View File

@ -807,11 +807,12 @@ void SetDehParams(FState *state, int codepointer)
VMFunctionBuilder buildit;
// Allocate registers used to pass parameters in.
// self, stateowner, state (all are pointers)
buildit.Registers[REGT_POINTER].Get(3);
buildit.Registers[REGT_POINTER].Get(NAP);
// Emit code to pass the standard action function parameters.
buildit.Emit(OP_PARAM, 0, REGT_POINTER, 0);
buildit.Emit(OP_PARAM, 0, REGT_POINTER, 1);
buildit.Emit(OP_PARAM, 0, REGT_POINTER, 2);
for (int i = 0; i < NAP; i++)
{
buildit.Emit(OP_PARAM, 0, REGT_POINTER, i);
}
// Emit code for action parameters.
int argcount = MBFCodePointerFactories[codepointer](buildit, value1, value2);
buildit.Emit(OP_TAIL_K, buildit.GetConstantAddress(sym->Variants[0].Implementation, ATAG_OBJECT), NAP + argcount, 0);
@ -2114,7 +2115,7 @@ static int PatchCodePtrs (int dummy)
else
{
TArray<DWORD> &args = sym->Variants[0].ArgFlags;
if ((sym->Flags & (VARF_Method | VARF_Action)) != (VARF_Method | VARF_Action) || (args.Size() > 3 && !(args[3] & VARF_Optional)))
if ((sym->Flags & (VARF_Method | VARF_Action)) != (VARF_Method | VARF_Action) || (args.Size() > NAP && !(args[NAP] & VARF_Optional)))
{
Printf("Frame %d: Incompatible code pointer '%s'\n", frame, Line2);
sym = NULL;
@ -2726,7 +2727,7 @@ static bool LoadDehSupp ()
else
{
TArray<DWORD> &args = sym->Variants[0].ArgFlags;
if ((sym->Flags & (VARF_Method|VARF_Action)) != (VARF_Method | VARF_Action) || (args.Size() > 3 && !(args[3] & VARF_Optional)))
if ((sym->Flags & (VARF_Method|VARF_Action)) != (VARF_Method | VARF_Action) || (args.Size() > NAP && !(args[NAP] & VARF_Optional)))
{
sc.ScriptMessage("Incompatible code pointer '%s'", sc.String);
}

View File

@ -69,14 +69,14 @@ void FState::SetAction(const char *name)
ActionFunc = FindGlobalActionFunction(name)->Variants[0].Implementation;
}
bool FState::CallAction(AActor *self, AActor *stateowner, FState **stateret)
bool FState::CallAction(AActor *self, AActor *stateowner, FStateParamInfo *info, FState **stateret)
{
if (ActionFunc != NULL)
{
ActionCycles.Clock();
static VMFrameStack stack;
VMValue params[3] = { self, stateowner, VMValue(this, ATAG_STATE) };
VMValue params[3] = { self, stateowner, VMValue(info, ATAG_STATEINFO) };
// If the function returns a state, store it at *stateret.
// If it doesn't return a state but stateret is non-NULL, we need
// to set *stateret to NULL.

View File

@ -55,6 +55,21 @@ struct FActorInfo;
class FArchive;
class FIntCVar;
enum EStateType
{
STATE_Actor,
STATE_Psprite,
STATE_StateChain,
};
struct FStateParamInfo
{
FState *mCallingState;
EStateType mStateType;
int mPSPIndex;
};
// Sprites that are fixed in position because they can have special meanings.
enum
{
@ -129,7 +144,7 @@ struct FState
void SetAction(VMFunction *func) { ActionFunc = func; }
void ClearAction() { ActionFunc = NULL; }
void SetAction(const char *name);
bool CallAction(AActor *self, AActor *stateowner, FState **stateret);
bool CallAction(AActor *self, AActor *stateowner, FStateParamInfo *stateinfo, FState **stateret);
static PClassActor *StaticFindStateOwner (const FState *state);
static PClassActor *StaticFindStateOwner (const FState *state, PClassActor *info);
static FRandom pr_statetics;
@ -338,7 +353,7 @@ void AddStateLight(FState *state, const char *lname);
PARAM_PROLOGUE; \
PARAM_OBJECT (self, type); \
PARAM_OBJECT_OPT (stateowner, AActor) { stateowner = self; } \
PARAM_STATE_OPT (callingstate) { callingstate = NULL; } \
PARAM_STATEINFO_OPT (stateinfo) { stateinfo = nullptr; } \
#define PARAM_ACTION_PROLOGUE PARAM_ACTION_PROLOGUE_TYPE(AActor)

View File

@ -556,7 +556,8 @@ bool AActor::SetState (FState *newstate, bool nofunction)
if (!nofunction)
{
FState *returned_state;
if (newstate->CallAction(this, this, &returned_state))
FStateParamInfo stp = { newstate, STATE_Actor, PSP_WEAPON };
if (newstate->CallAction(this, this, &stp, &returned_state))
{
// Check whether the called action function resulted in destroying the actor
if (ObjectFlags & OF_EuthanizeMe)
@ -3866,7 +3867,8 @@ bool AActor::CheckNoDelay()
// For immediately spawned objects with the NoDelay flag set for their
// Spawn state, explicitly call the current state's function.
FState *newstate;
if (state->CallAction(this, this, &newstate))
FStateParamInfo stp = { state, STATE_Actor, PSP_WEAPON };
if (state->CallAction(this, this, &stp, &newstate))
{
if (ObjectFlags & OF_EuthanizeMe)
{

View File

@ -321,7 +321,8 @@ void DPSprite::SetState(FState *newstate, bool pending)
if (Owner->mo != nullptr)
{
FState *nextstate;
if (newstate->CallAction(Owner->mo, Caller, &nextstate))
FStateParamInfo stp = { newstate, STATE_Psprite, ID };
if (newstate->CallAction(Owner->mo, Caller, &stp, &nextstate))
{
// It's possible this call resulted in this very layer being replaced.
if (ObjectFlags & OF_EuthanizeMe)

View File

@ -350,7 +350,7 @@ int MatchString (const char *in, const char **strings);
//#define PUSH_PARAMINFO self, stateowner, CallingState, ParameterIndex, statecall
#define CALL_ACTION(name,self) { /*AF_##name(self, self, NULL, 0, NULL)*/ \
VMValue params[3] = { self, self, VMValue(NULL, ATAG_STATE) }; \
VMValue params[3] = { self, self, VMValue(NULL, ATAG_STATEINFO) }; \
stack->Call(name##_VMPtr, params, countof(params), NULL, 0, NULL); \
}
@ -360,7 +360,7 @@ int MatchString (const char *in, const char **strings);
#define ACTION_RETURN_BOOL(v) ACTION_RETURN_INT(v)
// Checks to see what called the current action function
#define ACTION_CALL_FROM_ACTOR() (callingstate == self->state)
#define ACTION_CALL_FROM_WEAPON() (self->player && callingstate != self->state && !(stateowner->flags5 & MF5_INSTATECALL))
#define ACTION_CALL_FROM_INVENTORY() (!(stateowner->flags5 & MF5_INSTATECALL))
#define ACTION_CALL_FROM_ACTOR() (stateinfo == nullptr || stateinfo->mStateType == STATE_Actor)
#define ACTION_CALL_FROM_WEAPON() (self->player && stateinfo != nullptr && stateinfo->mStateType == STATE_Psprite)
#define ACTION_CALL_FROM_INVENTORY() (stateinfo != nullptr && stateinfo->mStateType == STATE_StateChain)
#endif

View File

@ -133,8 +133,9 @@ bool ACustomInventory::CallStateChain (AActor *actor, FState *state)
VMFrameStack stack;
PPrototype *proto = state->ActionFunc->Proto;
VMReturn *wantret;
FStateParamInfo stp = { state, STATE_StateChain, PSP_WEAPON };
params[2] = VMValue(state, ATAG_STATE);
params[2] = VMValue(&stp, ATAG_STATEINFO);
retval = true; // assume success
wantret = NULL; // assume no return value wanted
numret = 0;
@ -5839,22 +5840,21 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTics)
PARAM_ACTION_PROLOGUE;
PARAM_INT(tics_to_set);
if (self->player != nullptr)
{ // Need to check psp states for a match, then. Blah.
DPSprite *pspr = self->player->psprites;
while (pspr)
if (ACTION_CALL_FROM_WEAPON())
{
DPSprite *pspr = self->player->FindPSprite(stateinfo->mPSPIndex);
if (pspr != nullptr)
{
if (pspr->GetState() == callingstate)
{
pspr->Tics = tics_to_set;
return 0;
}
pspr = pspr->GetNext();
pspr->Tics = tics_to_set;
return 0;
}
}
// Just set tics for self.
self->tics = tics_to_set;
else if (ACTION_CALL_FROM_ACTOR())
{
// Just set tics for self.
self->tics = tics_to_set;
}
// for inventory state chains this needs to be ignored.
return 0;
}

View File

@ -2916,7 +2916,7 @@ ExpEmit FxDamage::Emit(VMFunctionBuilder *build)
build->Emit(OP_LO, dmgfunc.RegNum, 0/*self*/, build->GetConstantInt(myoffsetof(AActor, Damage)));
// If it's non-null...
build->Emit(OP_EQA_K, 1, dmgfunc.RegNum, build->GetConstantAddress(0, ATAG_GENERIC));
build->Emit(OP_EQA_K, 1, dmgfunc.RegNum, build->GetConstantAddress(nullptr, ATAG_GENERIC));
size_t nulljump = build->Emit(OP_JMP, 0);
// ...call it

View File

@ -152,6 +152,7 @@ enum
ATAG_AREGISTER, // pointer to an address register
ATAG_STATE, // pointer to FState
ATAG_STATEINFO, // FState plus some info.
ATAG_RNG, // pointer to FRandom
};
@ -914,6 +915,7 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction
#define PARAM_ANGLE_OPT_AT(p,x) DAngle x; if ((p) < numparam && param[p].Type != REGT_NIL) { assert(param[p].Type == REGT_FLOAT); x = param[p].f; } else
#define PARAM_STRING_OPT_AT(p,x) FString x; if ((p) < numparam && param[p].Type != REGT_NIL) { assert(param[p].Type == REGT_STRING); x = param[p].s(); } else
#define PARAM_STATE_OPT_AT(p,x) FState *x; if ((p) < numparam && param[p].Type != REGT_NIL) { assert(param[p].Type == REGT_POINTER && (param[p].atag == ATAG_STATE || param[p].a == NULL)); x = (FState *)param[p].a; } else
#define PARAM_STATEINFO_OPT_AT(p,x) FStateParamInfo *x; if ((p) < numparam && param[p].Type != REGT_NIL) { assert(param[p].Type == REGT_POINTER && (param[p].atag == ATAG_STATEINFO || param[p].a == NULL)); x = (FStateParamInfo *)param[p].a; } else
#define PARAM_POINTER_OPT_AT(p,x,type) type *x; if ((p) < numparam && param[p].Type != REGT_NIL) { assert(param[p].Type == REGT_POINTER); x = (type *)param[p].a; } else
#define PARAM_OBJECT_OPT_AT(p,x,type) type *x; if ((p) < numparam && param[p].Type != REGT_NIL) { assert(param[p].Type == REGT_POINTER && (param[p].atag == ATAG_OBJECT || param[p].a == NULL)); x = (type *)param[p].a; assert(x == NULL || x->IsKindOf(RUNTIME_CLASS(type))); } else
#define PARAM_CLASS_OPT_AT(p,x,base) base::MetaClass *x; if ((p) < numparam && param[p].Type != REGT_NIL) { assert(param[p].Type == REGT_POINTER && (param[p].atag == ATAG_OBJECT || param[p].a == NULL)); x = (base::MetaClass *)param[p].a; assert(x == NULL || x->IsDescendantOf(RUNTIME_CLASS(base))); } else
@ -943,6 +945,7 @@ void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction
#define PARAM_ANGLE_OPT(x) ++paramnum; PARAM_ANGLE_OPT_AT(paramnum,x)
#define PARAM_STRING_OPT(x) ++paramnum; PARAM_STRING_OPT_AT(paramnum,x)
#define PARAM_STATE_OPT(x) ++paramnum; PARAM_STATE_OPT_AT(paramnum,x)
#define PARAM_STATEINFO_OPT(x) ++paramnum; PARAM_STATEINFO_OPT_AT(paramnum,x)
#define PARAM_POINTER_OPT(x,type) ++paramnum; PARAM_POINTER_OPT_AT(paramnum,x,type)
#define PARAM_OBJECT_OPT(x,type) ++paramnum; PARAM_OBJECT_OPT_AT(paramnum,x,type)
#define PARAM_CLASS_OPT(x,base) ++paramnum; PARAM_CLASS_OPT_AT(paramnum,x,base)