From cfaa3e3fa95cb1780eaa24d0418a9d568cfb03ad Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 16 Jun 2016 16:11:00 +0200 Subject: [PATCH] - 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. --- src/d_dehacked.cpp | 13 +++++++------ src/info.cpp | 4 ++-- src/info.h | 19 +++++++++++++++++-- src/p_mobj.cpp | 6 ++++-- src/p_pspr.cpp | 3 ++- src/thingdef/thingdef.h | 8 ++++---- src/thingdef/thingdef_codeptr.cpp | 28 ++++++++++++++-------------- src/thingdef/thingdef_expression.cpp | 2 +- src/zscript/vm.h | 3 +++ 9 files changed, 54 insertions(+), 32 deletions(-) diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index e4acc75a6..f79924b2b 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -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 &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 &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); } diff --git a/src/info.cpp b/src/info.cpp index 49f139ebe..7198a5de4 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -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. diff --git a/src/info.h b/src/info.h index 6d3de5015..6ced755a3 100644 --- a/src/info.h +++ b/src/info.h @@ -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) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 39b0f423e..be875b951 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -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) { diff --git a/src/p_pspr.cpp b/src/p_pspr.cpp index 36aa00aae..d91b7ab00 100644 --- a/src/p_pspr.cpp +++ b/src/p_pspr.cpp @@ -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) diff --git a/src/thingdef/thingdef.h b/src/thingdef/thingdef.h index d522ad573..21ab33dfb 100644 --- a/src/thingdef/thingdef.h +++ b/src/thingdef/thingdef.h @@ -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 diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index 1fbb67fe4..77c2fcbd1 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -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; } diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index d500458a2..04410189a 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -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 diff --git a/src/zscript/vm.h b/src/zscript/vm.h index 317a69846..52b354028 100644 --- a/src/zscript/vm.h +++ b/src/zscript/vm.h @@ -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)