From f4eebd1ceddb6a3fc7e2de80140fb5c7430bf9e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Lu=C3=ADs=20Vaz=20Silva?= Date: Wed, 18 Jun 2025 03:43:30 -0300 Subject: [PATCH] CallVM API, plus multi-return and vector support --- src/common/scripting/vm/vm.h | 238 ++++++++++++++++++++++++++-- src/common/scripting/vm/vmframe.cpp | 48 ++++++ src/g_game.cpp | 16 +- src/playsim/p_pspr.cpp | 37 ++--- 4 files changed, 294 insertions(+), 45 deletions(-) diff --git a/src/common/scripting/vm/vm.h b/src/common/scripting/vm/vm.h index d4a6b1fa9..1828799af 100644 --- a/src/common/scripting/vm/vm.h +++ b/src/common/scripting/vm/vm.h @@ -534,13 +534,37 @@ inline int VMCallAction(VMFunction *func, VMValue *params, int numparams, VMRetu return VMCall(func, params, numparams, results, numresults); } +template struct VMArgTypeTrait { typedef T type; static const int ArgCount = 1; }; +template<> struct VMArgTypeTrait { typedef double type; static const int ArgCount = 2; }; +template<> struct VMArgTypeTrait { typedef double type; static const int ArgCount = 3; }; +template<> struct VMArgTypeTrait { typedef double type; static const int ArgCount = 4; }; +template<> struct VMArgTypeTrait { typedef double type; static const int ArgCount = 4; }; + template struct VMReturnTypeTrait { typedef T type; static const int ReturnCount = 1; }; template<> struct VMReturnTypeTrait { typedef void type; static const int ReturnCount = 0; }; +template +struct FirstTemplateValue +{ + using type = T; +}; + + + void VMCheckParamCount(VMFunction* func, int retcount, int argcount); -template -void VMCheckParamCount(VMFunction* func, int argcount) { return VMCheckParamCount(func, VMReturnTypeTrait::ReturnCount, argcount); } +template +void VMCheckParamCount(VMFunction* func, int argcount) +{ + if constexpr (sizeof...(Rets) == 1) + { + return VMCheckParamCount(func, VMReturnTypeTrait::type>::ReturnCount, argcount); + } + else + { + return VMCheckParamCount(func, sizeof...(Rets), argcount); + } +} // The type can't be mapped to ZScript automatically: @@ -551,12 +575,20 @@ template void VMCheckReturn(VMFunction* func, int index) = template<> void VMCheckParam(VMFunction* func, int index); template<> void VMCheckParam(VMFunction* func, int index); +template<> void VMCheckParam(VMFunction* func, int index); +template<> void VMCheckParam(VMFunction* func, int index); +template<> void VMCheckParam(VMFunction* func, int index); +template<> void VMCheckParam(VMFunction* func, int index); template<> void VMCheckParam(VMFunction* func, int index); template<> void VMCheckParam(VMFunction* func, int index); template<> void VMCheckReturn(VMFunction* func, int index); template<> void VMCheckReturn(VMFunction* func, int index); template<> void VMCheckReturn(VMFunction* func, int index); +template<> void VMCheckReturn(VMFunction* func, int index); +template<> void VMCheckReturn(VMFunction* func, int index); +template<> void VMCheckReturn(VMFunction* func, int index); +template<> void VMCheckReturn(VMFunction* func, int index); template<> void VMCheckReturn(VMFunction* func, int index); template<> void VMCheckReturn(VMFunction* func, int index); template<> void VMCheckReturn(VMFunction* func, int index); @@ -595,26 +627,206 @@ void VMValidateSignatureSingle(VMFunction* func, std::index_sequence) (VMCheckParam>(func, I), ...); } +template +void VMValidateSignatureMulti(VMFunction* func, std::index_sequence, std::index_sequence, Args... args) +{ + VMCheckParamCount(func, sizeof...(Args)); + (VMCheckReturn>(func, IRets), ...); + (VMCheckParam>(func, IArgs), ...); +} + void VMCallCheckResult(VMFunction* func, VMValue* params, int numparams, VMReturn* results, int numresults); -template -typename VMReturnTypeTrait::type VMCallSingle(VMFunction* func, Args... args) -{ - VMValidateSignatureSingle(func, std::make_index_sequence{}); - VMValue params[] = { args... }; - vm_pointer_decay_void resultval; // convert any pointer to void - VMReturn results(&resultval); - VMCallCheckResult(func, params, sizeof...(Args), &results, 1); - return (RetVal)resultval; +struct VMValueMulti +{ + VMValue vals[4]; + int count; + + template + VMValueMulti(T val) + { + vals[0] = val; + count = 1; + } + + VMValueMulti(DVector2 val) + { + vals[0] = val.X; + vals[1] = val.Y; + count = 2; + } + + VMValueMulti(DVector3 val) + { + vals[0] = val.X; + vals[1] = val.Y; + vals[2] = val.Z; + count = 3; + } + + VMValueMulti(DVector4 val) + { + vals[0] = val.X; + vals[1] = val.Y; + vals[2] = val.Z; + vals[3] = val.W; + count = 4; + } + + VMValueMulti(DQuaternion val) + { + vals[0] = val.X; + vals[1] = val.Y; + vals[2] = val.Z; + vals[3] = val.W; + count = 4; + } +}; + +template +constexpr int numArgs() +{ + return (VMArgTypeTrait::ArgCount + ...); } +template +constexpr bool hasVector() +{ + return (VMArgTypeTrait::ArgCount + ...) != sizeof...(Args); +} + + template typename VMReturnTypeTrait::type VMCallVoid(VMFunction* func, Args... args) { VMValidateSignatureSingle(func, std::make_index_sequence{}); - VMValue params[] = { args... }; - VMCallCheckResult(func, params, sizeof...(Args), nullptr, 0); + if constexpr(hasVector()) + { + VMValueMulti arglist[] = { args... }; + + constexpr int argCount = numArgs(); + + VMValue params[argCount]; + + for(int i = 0, j = 0; i < sizeof...(Args); i++) + { + for(int k = 0; k < arglist[i].count; k++, j++) + { + params[j] = arglist[i].vals[k]; + } + } + + VMCallCheckResult(func, params, argCount, nullptr, 0); + } + else + { + VMValue params[] = { args... }; + VMCallCheckResult(func, params, sizeof...(Args), nullptr, 0); + } +} + +template +typename VMReturnTypeTrait::type VMCallSingle(VMFunction* func, Args... args) +{ + VMValidateSignatureSingle(func, std::make_index_sequence{}); + if constexpr(hasVector()) + { + VMValueMulti arglist[] = { args... }; + + constexpr int argCount = numArgs(); + + VMValue params[argCount]; + + for(int i = 0, j = 0; i < sizeof...(Args); i++) + { + for(int k = 0; k < arglist[i].count; k++, j++) + { + params[j] = arglist[i].vals[k]; + } + } + + vm_pointer_decay_void resultval; // convert any pointer to void + VMReturn results(&resultval); + VMCallCheckResult(func, params, argCount, &results, 1); + return (RetVal)resultval; + } + else + { + VMValue params[] = { args... }; + + vm_pointer_decay_void resultval; // convert any pointer to void + VMReturn results(&resultval); + VMCallCheckResult(func, params, sizeof...(Args), &results, 1); + return (RetVal)resultval; + } +} + +template +std::tuple::type...> VMCallMultiImpl(VMFunction* func, std::index_sequence retsSeq, Args... args) +{ + VMValidateSignatureMulti(func, retsSeq, std::make_index_sequence{}, std::forward(args)...); + if constexpr(hasVector()) + { + VMValueMulti arglist[] = { args... }; + + constexpr int argCount = numArgs(); + + VMValue params[argCount]; + + for(int i = 0, j = 0; i < sizeof...(Args); i++) + { + for(int k = 0; k < arglist[i].count; k++, j++) + { + params[j] = arglist[i].vals[k]; + } + } + + std::tuple>::type...> resultval; // convert any pointer to void + VMReturn results[sizeof...(Rets)] { &std::get(resultval)... }; + VMCallCheckResult(func, params, argCount, results, sizeof...(Rets)); + return (std::tuple::type...>)resultval; + } + else + { + VMValue params[] = { args... }; + + std::tuple>::type...> resultval; // convert any pointer to void + VMReturn results[sizeof...(Rets)] { &std::get(resultval)... }; + VMCallCheckResult(func, params, sizeof...(Args), results, sizeof...(Rets)); + return (std::tuple::type...>)resultval; + } +} + +template +std::tuple::type...> VMCallMulti(VMFunction* func, Args... args) +{ + return VMCallMultiImpl(func, std::make_index_sequence{}, std::forward(args)...); +} + +template +using MultiVMReturn = std::conditional< + (sizeof...(Rets) > 1), + std::tuple::type...>, + typename VMReturnTypeTrait::type>::type + >; + +template +typename MultiVMReturn::type CallVM(VMFunction* func, Args... args) +{ + static_assert(sizeof...(Rets) > 0, "missing return type in VMCall"); + if constexpr(sizeof...(Rets) == 1 && std::is_same_v::type, void>) + { + return VMCallVoid(func, std::forward(args)...); + } + else if constexpr(sizeof...(Rets) == 1) + { + return VMCallSingle(func, std::forward(args)...); + } + else + { + return VMCallMulti(func, std::forward(args)...); + } } diff --git a/src/common/scripting/vm/vmframe.cpp b/src/common/scripting/vm/vmframe.cpp index 40b3dbef8..c72fc2a7e 100644 --- a/src/common/scripting/vm/vmframe.cpp +++ b/src/common/scripting/vm/vmframe.cpp @@ -575,6 +575,30 @@ template<> void VMCheckParam(VMFunction* func, int index) I_FatalError("%s argument %d is not a double", func->PrintableName, index); } +template<> void VMCheckParam(VMFunction* func, int index) +{ + if (func->Proto->ArgumentTypes[index] != TypeVector2) + I_FatalError("%s argument %d is not a vector2", func->PrintableName, index); +} + +template<> void VMCheckParam(VMFunction* func, int index) +{ + if (func->Proto->ArgumentTypes[index] != TypeVector3) + I_FatalError("%s argument %d is not a vector3", func->PrintableName, index); +} + +template<> void VMCheckParam(VMFunction* func, int index) +{ + if (func->Proto->ArgumentTypes[index] != TypeVector4) + I_FatalError("%s argument %d is not a vector4", func->PrintableName, index); +} + +template<> void VMCheckParam(VMFunction* func, int index) +{ + if (func->Proto->ArgumentTypes[index] != TypeQuaternion) + I_FatalError("%s argument %d is not a quat", func->PrintableName, index); +} + template<> void VMCheckParam(VMFunction* func, int index) { if (func->Proto->ArgumentTypes[index] != TypeString) @@ -603,6 +627,30 @@ template<> void VMCheckReturn(VMFunction* func, int index) I_FatalError("%s return value %d is not a double", func->PrintableName, index); } +template<> void VMCheckReturn(VMFunction* func, int index) +{ + if (func->Proto->ReturnTypes[index] != TypeVector2) + I_FatalError("%s return value %d is not a vector2", func->PrintableName, index); +} + +template<> void VMCheckReturn(VMFunction* func, int index) +{ + if (func->Proto->ReturnTypes[index] != TypeVector3) + I_FatalError("%s return value %d is not a vector3", func->PrintableName, index); +} + +template<> void VMCheckReturn(VMFunction* func, int index) +{ + if (func->Proto->ReturnTypes[index] != TypeVector4) + I_FatalError("%s return value %d is not a vector4", func->PrintableName, index); +} + +template<> void VMCheckReturn(VMFunction* func, int index) +{ + if (func->Proto->ReturnTypes[index] != TypeQuaternion) + I_FatalError("%s return value %d is not a quat", func->PrintableName, index); +} + template<> void VMCheckReturn(VMFunction* func, int index) { if (func->Proto->ReturnTypes[index] != TypeString) diff --git a/src/g_game.cpp b/src/g_game.cpp index 8e86d86db..703cac8bb 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -317,7 +317,7 @@ CCMD (slot) // Needs to be redone IFVIRTUALPTRNAME(mo, NAME_PlayerPawn, PickWeapon) { - SendItemUse = VMCallSingle(func, mo, slot, (int)!(dmflags2 & DF2_DONTCHECKAMMO)); + SendItemUse = CallVM(func, mo, slot, (int)!(dmflags2 & DF2_DONTCHECKAMMO)); } } @@ -373,7 +373,7 @@ CCMD (weapnext) // Needs to be redone IFVIRTUALPTRNAME(mo, NAME_PlayerPawn, PickNextWeapon) { - SendItemUse = VMCallSingle(func, mo); + SendItemUse = CallVM(func, mo); } } @@ -398,7 +398,7 @@ CCMD (weapprev) // Needs to be redone IFVIRTUALPTRNAME(mo, NAME_PlayerPawn, PickPrevWeapon) { - SendItemUse = VMCallSingle(func, mo); + SendItemUse = CallVM(func, mo); } } @@ -437,7 +437,7 @@ CCMD (invnext) { IFVM(PlayerPawn, InvNext) { - VMCallVoid(func, players[consoleplayer].mo); + CallVM(func, players[consoleplayer].mo); } } } @@ -448,7 +448,7 @@ CCMD(invprev) { IFVM(PlayerPawn, InvPrev) { - VMCallVoid(func, players[consoleplayer].mo); + CallVM(func, players[consoleplayer].mo); } } } @@ -523,7 +523,7 @@ CCMD (useflechette) if (players[consoleplayer].mo == nullptr) return; IFVIRTUALPTRNAME(players[consoleplayer].mo, NAME_PlayerPawn, GetFlechetteItem) { - AActor * cls = VMCallSingle(func, players[consoleplayer].mo); + AActor * cls = CallVM(func, players[consoleplayer].mo); if (cls != nullptr) SendItemUse = cls; } @@ -1289,7 +1289,7 @@ void G_PlayerFinishLevel (int player, EFinishLevelType mode, int flags) { IFVM(PlayerPawn, PlayerFinishLevel) { - VMCallVoid(func, players[player].mo, (int)mode, flags); + CallVM(func, players[player].mo, (int)mode, flags); } } @@ -1362,7 +1362,7 @@ void FLevelLocals::PlayerReborn (int player) IFVIRTUALPTRNAME(actor, NAME_PlayerPawn, GiveDefaultInventory) { - VMCallVoid(func, actor); + CallVM(func, actor); } p->ReadyWeapon = p->PendingWeapon; } diff --git a/src/playsim/p_pspr.cpp b/src/playsim/p_pspr.cpp index df09b58c3..54ae4b6cf 100644 --- a/src/playsim/p_pspr.cpp +++ b/src/playsim/p_pspr.cpp @@ -637,19 +637,16 @@ void P_BobWeapon (player_t *player, float *x, float *y, double ticfrac) { IFVIRTUALPTRNAME(player->mo, NAME_PlayerPawn, BobWeapon) { - VMValue param[] = { player->mo, ticfrac }; - DVector2 result; - VMReturn ret(&result); - VMCall(func, param, 2, &ret, 1); + DVector2 result = CallVM(func, player->mo, ticfrac); - auto inv = player->mo->Inventory; + auto inv = player->mo->Inventory.Get(); while(inv != nullptr && !(inv->ObjectFlags & OF_EuthanizeMe)) // same loop as ModifyDamage, except it actually checks if it's overriden before calling { - auto nextinv = inv->Inventory; + auto nextinv = inv->Inventory.Get(); IFOVERRIDENVIRTUALPTRNAME(inv, NAME_Inventory, ModifyBob) { - VMValue param[] = { (DObject*)inv, result.X, result.Y, ticfrac }; - VMCall(func, param, 4, &ret, 1); + DVector2 r2 = CallVM(func, inv, result, ticfrac); + result = r2; } inv = nextinv; } @@ -665,31 +662,23 @@ void P_BobWeapon3D (player_t *player, FVector3 *translation, FVector3 *rotation, { IFVIRTUALPTRNAME(player->mo, NAME_PlayerPawn, BobWeapon3D) { - VMValue param[] = { player->mo, ticfrac }; - DVector3 t, r; - VMReturn returns[2]; - returns[0].Vec3At(&t); - returns[1].Vec3At(&r); - VMCall(func, param, 2, returns, 2); + auto [t, r] = CallVM(func, player->mo, ticfrac); - auto inv = player->mo->Inventory; + auto inv = player->mo->Inventory.Get(); while(inv != nullptr && !(inv->ObjectFlags & OF_EuthanizeMe)) { - auto nextinv = inv->Inventory; + auto nextinv = inv->Inventory.Get(); IFOVERRIDENVIRTUALPTRNAME(inv, NAME_Inventory, ModifyBob3D) { - VMValue param[] = { (DObject*)inv, t.X, t.Y, t.Z, r.X, r.Y, r.Z, ticfrac }; - VMCall(func, param, 8, returns, 2); + auto [t2, r2] = CallVM(func, inv, t, r, ticfrac); + t = t2; + r = r2; } inv = nextinv; } - translation->X = (float)t.X; - translation->Y = (float)t.Y; - translation->Z = (float)t.Z; - rotation->X = (float)r.X; - rotation->Y = (float)r.Y; - rotation->Z = (float)r.Z; + *translation = FVector3(t); + *rotation = FVector3(r); return; } *translation = *rotation = {};