Skip to content

Commit

Permalink
Allow external handling of internal ACS functions from other ports
Browse files Browse the repository at this point in the history
For ACS functions that should not be baked into the engine, allow hooking into them externally so that ZScript mods can still make use of them.
  • Loading branch information
Boondorl committed Feb 26, 2025
1 parent 52a5452 commit 52913f5
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/d_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ void I_ShutdownInput();
void SetConsoleNotifyBuffer();
void I_UpdateDiscordPresence(bool SendPresence, const char* curstatus, const char* appid, const char* steamappid);
bool M_SetSpecialMenu(FName& menu, int param); // game specific checks
void SetHandledACSFunctions();

const FIWADInfo *D_FindIWAD(TArray<FString> &wadfiles, const char *iwad, const char *basewad);
void InitWidgetResources(const char* basewad);
Expand Down Expand Up @@ -3086,6 +3087,7 @@ static int FileSystemPrintf(FSMessageLevel level, const char* fmt, ...)
static int D_InitGame(const FIWADInfo* iwad_info, std::vector<std::string>& allwads, std::vector<std::string>& pwads)
{
NetworkEntityManager::InitializeNetworkEntities();
SetHandledACSFunctions();

if (!restart)
{
Expand Down
29 changes: 29 additions & 0 deletions src/events.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -975,6 +975,18 @@ void EventManager::NetCommand(FNetworkCommand& cmd)
handler->NetCommandProcess(cmd);
}

int EventManager::ProcessACSFunction(int func, const TArray<int>* args)
{
int res = 0;
if (ShouldCallStatic(false))
res = staticEventManager.ProcessACSFunction(func, args);

for (DStaticEventHandler* handler = FirstEventHandler; handler; handler = handler->next)
handler->ACSFunctionProcess(func, args, res);

return res;
}

void EventManager::Console(int player, FString name, int arg1, int arg2, int arg3, bool manual, bool ui)
{
if (ShouldCallStatic(false)) staticEventManager.Console(player, name, arg1, arg2, arg3, manual, ui);
Expand Down Expand Up @@ -2274,6 +2286,23 @@ void DStaticEventHandler::NetCommandProcess(FNetworkCommand& cmd)
}
}

void DStaticEventHandler::ACSFunctionProcess(int code, const TArray<int>* args, int& res)
{
IFVIRTUAL(DStaticEventHandler, ACSFunctionProcess)
{
if (isEmpty(func))
return;

TArray<int> funcArgs = {};
if (args != nullptr)
funcArgs = *args;

VMValue params[] = { this, code, &funcArgs };
VMReturn returns[] = { &res };
VMCall(func, params, 3, returns, 1);
}
}

void DStaticEventHandler::ConsoleProcess(int player, FString name, int arg1, int arg2, int arg3, bool manual, bool ui)
{
if (player < 0)
Expand Down
3 changes: 3 additions & 0 deletions src/events.h
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ class DStaticEventHandler : public DObject // make it a part of normal GC proces
//
void ConsoleProcess(int player, FString name, int arg1, int arg2, int arg3, bool manual, bool ui);
void NetCommandProcess(FNetworkCommand& cmd);
void ACSFunctionProcess(int func, const TArray<int>* args, int& res);

//
void CheckReplacement(PClassActor* replacee, PClassActor** replacement, bool* final);
Expand Down Expand Up @@ -539,6 +540,8 @@ struct EventManager
void Console(int player, FString name, int arg1, int arg2, int arg3, bool manual, bool ui);
// This reads from ZScript network commands.
void NetCommand(FNetworkCommand& cmd);
// Custom handling for known but unsupported ACS events.
int ProcessACSFunction(int func, const TArray<int>* args);

// called when looking up the replacement for an actor class
bool CheckReplacement(PClassActor* replacee, PClassActor** replacement);
Expand Down
96 changes: 94 additions & 2 deletions src/playsim/p_acs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
#include "s_music.h"
#include "v_video.h"
#include "texturemanager.h"
#include "events.h"

// P-codes for ACS scripts
enum
Expand Down Expand Up @@ -4823,6 +4824,77 @@ enum EACSFunctions
ACSF_SetTeamScore, // (int team, int value
};

// Op code -> minimum arg count
static TMap<int, int> HandledACSFunctions = {};

void SetHandledACSFunctions()
{
// Zandronum
HandledACSFunctions[100] = 0; // ResetMap
HandledACSFunctions[101] = 1; // PlayerIsSpectator
// ConsolePlayerNumber will be intentionally left out until proper client-side
// ACS scripts are implemented. Right now it'd just leave the game prone to desyncs.
HandledACSFunctions[103] = 2; // GetTeamProperty
HandledACSFunctions[104] = 1; // GetPlayersLivesLeft
HandledACSFunctions[105] = 2; // SetPlayerLivesLeft
HandledACSFunctions[106] = 2; // KickFromGame
HandledACSFunctions[107] = 0; // GetGamemodeState
HandledACSFunctions[108] = 3; // SetDBEntry
HandledACSFunctions[109] = 2; // GetDBEntry
HandledACSFunctions[110] = 3; // SetDBEntryString
HandledACSFunctions[111] = 2; // GetDBEntryString
HandledACSFunctions[112] = 3; // IncrementDBEntry
HandledACSFunctions[113] = 1; // PlayerIsLoggedIn
HandledACSFunctions[114] = 1; // GetPlayerAccountName
HandledACSFunctions[115] = 4; // SortDBEntries
HandledACSFunctions[116] = 1; // CountDBResults
HandledACSFunctions[117] = 1; // FreeDBResults
HandledACSFunctions[118] = 2; // GetDBResultKeyString
HandledACSFunctions[119] = 2; // GetDBResultValueString
HandledACSFunctions[120] = 2; // GetDBResultValue
HandledACSFunctions[121] = 3; // GetDBEntryRank
HandledACSFunctions[122] = 4; // RequestScriptPuke
HandledACSFunctions[123] = 0; // BeginDBTransaction
HandledACSFunctions[124] = 0; // EndDBTransaction
HandledACSFunctions[125] = 1; // GetDBEntries
HandledACSFunctions[126] = 1; // NamedRequestScriptPuke
// System time functions are intentionally left out since they're prone to causing desyncs. Can be added
// in if client/server ever becomes a thing.
HandledACSFunctions[130] = 2; // SetDeadSpectator
HandledACSFunctions[131] = 1; // SetActivatorToPlayer
HandledACSFunctions[132] = 1; // SetCurrentGamemode
HandledACSFunctions[133] = 0; // GetCurrentGamemode
HandledACSFunctions[134] = 2; // SetGamemodeLimit
// Player class handling isn't implemented yet.
HandledACSFunctions[136] = 2; // SetPlayerChasecam
HandledACSFunctions[137] = 1; // GetPlayerChasecam
HandledACSFunctions[138] = 3; // SetPlayerScore
HandledACSFunctions[139] = 2; // GetPlayerScore
HandledACSFunctions[140] = 0; // InDemoMode
// Client-side scripts aren't implemented yet for ClientScript functions.
HandledACSFunctions[146] = 2; // SendNetworkString
HandledACSFunctions[147] = 2; // NamedSendNetworkString
HandledACSFunctions[148] = 2; // GetChatMessage
HandledACSFunctions[149] = 0; // GetMapRotationSize
HandledACSFunctions[150] = 2; // GetMapRotationInfo
HandledACSFunctions[151] = 0; // GetCurrentMapPosition
HandledACSFunctions[152] = 0; // GetEventResult
HandledACSFunctions[153] = 2; // GetActorSectorLocation
HandledACSFunctions[154] = 3; // ChangeTeamScore
HandledACSFunctions[155] = 2; // SetGameplaySettings
HandledACSFunctions[156] = 3; // SetCustomPlayerValue
HandledACSFunctions[157] = 2; // GetCustomPlayerValue
HandledACSFunctions[158] = 2; // ResetCustomDataToDefault
HandledACSFunctions[159] = 1; // LumpOpen
HandledACSFunctions[160] = 2; // LumpRead
HandledACSFunctions[161] = 2; // LumpReadString
HandledACSFunctions[166] = 2; // LumpGetInfo
HandledACSFunctions[167] = 1; // LumpClose

// Eternity
HandledACSFunctions[302] = 1; // SetAirFriction
}

int DLevelScript::SideFromID(int id, int side)
{
if (side != 0 && side != 1) return -1;
Expand Down Expand Up @@ -7249,8 +7321,28 @@ int DLevelScript::RunScript()
int argCount = NEXTBYTE;
int funcIndex = NEXTSHORT;

int retval, minCount = 0;
retval = CallFunction(argCount, funcIndex, &STACK(argCount), minCount);
int retval = 0, minCount = 0;
auto undefined = HandledACSFunctions.CheckKey(funcIndex);
if (undefined != nullptr)
{
if (argCount >= *undefined || (Level->i_compatflags2 & COMPATF2_NOACSARGCHECK))
{
TArray<int> args = {};
auto argStart = &STACK(argCount);
for (size_t p = 0u; p < argCount; ++p)
args.Push(argStart[p]);

retval = primaryLevel->localEventManager->ProcessACSFunction(funcIndex, &args);
}
else
{
minCount = *undefined;
}
}
else
{
retval = CallFunction(argCount, funcIndex, &STACK(argCount), minCount);
}
if (minCount != 0)
{
Printf("Called ACS function index %d with too few args: %d (need %d)\n", funcIndex, argCount, minCount);
Expand Down
66 changes: 66 additions & 0 deletions wadsrc/static/zscript/events.zs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,71 @@ struct ReplacedEvent native version("3.7")
native bool IsFinal;
}

enum EACSFunction
{
ACSF_ResetMap = 100,
ACSF_PlayerIsSpectator,

ACSF_GetTeamProperty = 103,
ACSF_GetPlayerLivesLeft,
ACSF_SetPlayerLivesLeft,
ACSF_KickFromGame,
ACSF_GetGamemodeState,
ACSF_SetDBEntry,
ACSF_GetDBEntry,
ACSF_SetDBEntryString,
ACSF_GetDBEntryString,
ACSF_IncrementDBEntry,
ACSF_PlayerIsLoggedIn,
ACSF_GetPlayerAccountName,
ACSF_SortDBEntries,
ACSF_CountDBResults,
ACSF_FreeDBResults,
ACSF_GetDBResultKeyString,
ACSF_GetDBResultValueString,
ACSF_GetDBResultValue,
ACSF_GetDBEntryRank,
ACSF_RequestScriptPuke,
ACSF_BeginDBTransaction,
ACSF_EndDBTransaction,
ACSF_GetDBEntries,
ACSF_NamedRequestScriptPuke,

ACSF_SetDeadSpectator = 130,
ACSF_SetActivatorToPlayer,
ACSF_SetCurrentGamemode,
ACSF_GetCurrentGamemode,
ACSF_SetGamemodeLimit,

ACSF_SetPlayerChasecam = 136,
ACSF_GetPlayerChasecam,
ACSF_SetPlayerScore,
ACSF_GetPlayerScore,
ACSF_InDemoMode,

ACSF_SendNetworkString = 146,
ACSF_NamedSendNetworkString,
ACSF_GetChatMessage,
ACSF_GetMapRotationSize,
ACSF_GetMapRotationInfo,
ACSF_GetCurrentMapPosition,
ACSF_GetEventResult,
ACSF_GetActorSectorLocation,
ACSF_ChangeTeamScore,
ACSF_SetGameplaySettings,
ACSF_SetCustomPlayerValue,
ACSF_GetCustomPlayerValue,
ACSF_ResetCustomDataToDefault,
ACSF_LumpOpen,
ACSF_LumpRead,
ACSF_LumpReadString,

ACSF_LumpGetInfo = 166,
ACSF_LumpClose,

ACSF_SetAirFriction = 302,
}

class StaticEventHandler : Object native play version("2.4")
{
// static event handlers CAN register other static event handlers.
Expand Down Expand Up @@ -200,6 +265,7 @@ class StaticEventHandler : Object native play version("2.4")
virtual ui void InterfaceProcess(ConsoleEvent e) {}
virtual void NetworkProcess(ConsoleEvent e) {}
version("4.12") virtual void NetworkCommandProcess(NetworkCommand cmd) {}
version("4.15") virtual int ACSFunctionProcess(EACSFunction func, Array<int> args) { return 0; }

//
virtual void CheckReplacement(ReplaceEvent e) {}
Expand Down

0 comments on commit 52913f5

Please sign in to comment.