sm64coopdx/data/dynos_mgr_actor.cpp
djoslin0 24b92ecc2a
Some checks are pending
Build coop / build-linux (push) Waiting to run
Build coop / build-steamos (push) Waiting to run
Build coop / build-windows-opengl (push) Waiting to run
Build coop / build-windows-directx (push) Waiting to run
Build coop / build-macos-arm (push) Waiting to run
Build coop / build-macos-intel (push) Waiting to run
Add a safer version of Lua's require() (#847)
I didn't add standard Lua require() because I've always been
afraid of it. I'm not sure we can guarantee which files it
will read (or not read).

Instead, here is a custom implementation. It should work more
or less the same and allow for more modular code.

For backwards compatibility reasons, all of the lua files in
the base mod folder will be loaded as in the past. Aka one at
a time and alphabetically.

However, now coop will look for Lua files in subdirectories
and will load them in when another Lua file calls require().

The file search order is more reasonable than normal Lua
require(). It will first look for files relative to the
currently running script. If there is no matching relative
file, it will pick from any Lua file that is in any of the
mod's subdirectories.

---------

Co-authored-by: MysterD <myster@d>
2025-06-14 19:49:07 +10:00

317 lines
11 KiB
C++

#include <map>
#include <algorithm>
#include <unordered_map>
#include "dynos.cpp.h"
extern "C" {
#include "object_fields.h"
#include "game/level_update.h"
#include "game/object_list_processor.h"
#include "pc/configfile.h"
#include "pc/lua/smlua_hooks.h"
}
// Static maps/arrays
static std::map<const void*, ActorGfx>& DynosValidActors() {
static std::map<const void*, ActorGfx> sDynosValidActors;
return sDynosValidActors;
}
static Array<Pair<const char*, void *>>& DynosCustomActors() {
static Array<Pair<const char*, void *>> sDynosCustomActors;
return sDynosCustomActors;
}
static std::map<struct GraphNode *, struct GraphNode *> sModifiedGraphNodes;
// TODO: the cleanup/refactor didn't really go as planned.
// clean up the actor management code more
std::map<const void *, ActorGfx> &DynOS_Actor_GetValidActors() {
return DynosValidActors();
}
void DynOS_Actor_AddCustom(s32 aModIndex, s32 aModFileIndex, const SysPath &aFilename, const char *aActorName) {
const void* georef = DynOS_Builtin_Actor_GetFromName(aActorName);
u16 actorLen = strlen(aActorName);
char* actorName = (char*)calloc(1, sizeof(char) * (actorLen + 1));
strcpy(actorName, aActorName);
GfxData *_GfxData = DynOS_Actor_LoadFromBinary(aFilename, actorName, aFilename, false);
if (!_GfxData) {
PrintError(" ERROR: Couldn't load Actor Binary \"%s\" from \"%s\"", actorName, aFilename.c_str());
free(actorName);
return;
}
_GfxData->mModIndex = aModIndex;
_GfxData->mModFileIndex = aModFileIndex;
void* geoLayout = (*(_GfxData->mGeoLayouts.end() - 1))->mData;
if (!geoLayout) {
PrintError(" ERROR: Couldn't load geo layout for \"%s\"", actorName);
free(actorName);
return;
}
// Alloc and init the actors gfx list
u32 id = 0;
ActorGfx actorGfx = { };
actorGfx.mGfxData = _GfxData;
actorGfx.mPackIndex = MOD_PACK_INDEX;
actorGfx.mGraphNode = (GraphNode *) DynOS_Model_LoadGeo(&id, MODEL_POOL_SESSION, geoLayout, true);
if (!actorGfx.mGraphNode) {
PrintError(" ERROR: Couldn't load graph node for \"%s\"", actorName);
free(actorName);
return;
}
actorGfx.mGraphNode->georef = georef;
// Add to custom actors
if (georef == NULL) {
DynosCustomActors().Add({ strdup(actorName), geoLayout });
georef = geoLayout;
}
// Add to list
DynOS_Actor_Valid(georef, actorGfx);
free(actorName);
}
const void *DynOS_Actor_GetLayoutFromName(const char *aActorName) {
if (aActorName == NULL) { return NULL; }
// check levels
auto& levelsArray = DynOS_Lvl_GetArray();
for (auto& lvl : levelsArray) {
for (auto& geo : lvl.second->mGeoLayouts) {
if (geo->mName == aActorName) {
return geo->mData;
}
}
}
// check custom actors
for (auto& pair : DynosCustomActors()) {
if (!strcmp(aActorName, pair.first)) {
return pair.second;
}
}
// check loaded actors
for (auto& pair : DynosValidActors()) {
for (auto& geo : pair.second.mGfxData->mGeoLayouts) {
if (!strcmp(aActorName, geo->mName.begin())) {
return geo->mData;
}
}
}
// check built in actors
for (s32 i = 0; i < DynOS_Builtin_Actor_GetCount(); ++i) {
auto name = DynOS_Builtin_Actor_GetNameFromIndex(i);
if (!strcmp(aActorName, name)) {
return DynOS_Builtin_Actor_GetFromIndex(i);
}
}
return NULL;
}
bool DynOS_Actor_GetModIndexAndToken(const GraphNode *aGraphNode, u32 aTokenIndex, s32 *outModIndex, s32 *outModFileIndex, const char **outToken) {
ActorGfx *_ActorGfx = DynOS_Actor_GetActorGfx(aGraphNode);
if (_ActorGfx) {
GfxData *_GfxData = _ActorGfx->mGfxData;
if (_GfxData) {
if (outModIndex) {
*outModIndex = _GfxData->mModIndex;
}
if (outModFileIndex) {
*outModFileIndex = _GfxData->mModFileIndex;
}
if (outToken) {
if (!aTokenIndex || aTokenIndex > _GfxData->mLuaTokenList.Count()) {
return false;
}
*outToken = _GfxData->mLuaTokenList[aTokenIndex - 1].begin(); // token index is 1-indexed
}
return true;
}
} else { // try the active level
GfxData *_GfxData = DynOS_Lvl_GetActiveGfx();
if (_GfxData) {
if (outModIndex) {
*outModIndex = _GfxData->mModIndex;
}
if (outModFileIndex) {
*outModFileIndex = _GfxData->mModFileIndex;
}
if (outToken) {
if (!aTokenIndex || aTokenIndex > _GfxData->mLuaTokenList.Count()) {
return false;
}
*outToken = _GfxData->mLuaTokenList[aTokenIndex - 1].begin(); // token index is 1-indexed
}
return true;
}
}
return false;
}
ActorGfx* DynOS_Actor_GetActorGfx(const GraphNode* aGraphNode) {
if (aGraphNode == NULL) { return NULL; }
auto& _ValidActors = DynosValidActors();
// If georef is not NULL, check georef
if (aGraphNode->georef != NULL) {
auto it = _ValidActors.find(aGraphNode->georef);
if (it != _ValidActors.end()) {
return &it->second;
}
return NULL;
}
// Check graph node
for (const auto& _Actor : _ValidActors) {
if (_Actor.second.mGraphNode == aGraphNode) {
return (ActorGfx*)&_Actor.second;
}
}
// No actor found
return NULL;
}
void DynOS_Actor_Valid(const void* aGeoref, ActorGfx& aActorGfx) {
if (aGeoref == NULL) { return; }
auto& _ValidActors = DynosValidActors();
_ValidActors[aGeoref] = aActorGfx;
DynOS_Tex_Valid(aActorGfx.mGfxData);
}
void DynOS_Actor_Invalid(const void* aGeoref, s32 aPackIndex) {
if (aGeoref == NULL) { return; }
auto& _ValidActors = DynosValidActors();
auto it = _ValidActors.find(aGeoref);
if (it == _ValidActors.end()) { return; }
if (it->second.mPackIndex != aPackIndex) { return; }
DynOS_Tex_Invalid(it->second.mGfxData);
_ValidActors.erase(aGeoref);
}
void DynOS_Actor_Override(struct Object* obj, void** aSharedChild) {
if ((aSharedChild == NULL) || (*aSharedChild == NULL)) { return; }
const void* georef = (*(GraphNode**)aSharedChild)->georef;
if (georef == NULL) { return; }
auto& _ValidActors = DynosValidActors();
auto it = _ValidActors.find(georef);
if (it == _ValidActors.end()) { return; }
// Check if the behavior uses a character specific model
if (obj && (obj->behavior == bhvMario ||
obj->behavior == smlua_override_behavior(bhvNormalCap) ||
obj->behavior == smlua_override_behavior(bhvWingCap) ||
obj->behavior == smlua_override_behavior(bhvMetalCap) ||
obj->behavior == smlua_override_behavior(bhvVanishCap))) {
struct NetworkPlayer* np = network_player_from_global_index(obj->globalPlayerIndex);
if (np && np->localIndex > 0 && configDynosLocalPlayerModelOnly) {
return;
}
}
*aSharedChild = (void*)it->second.mGraphNode;
}
void DynOS_Actor_Override_All(void) {
if (!gObjectLists) { return; }
// Loop through all object lists
for (s32 list : { OBJ_LIST_PLAYER, OBJ_LIST_DESTRUCTIVE, OBJ_LIST_GENACTOR, OBJ_LIST_PUSHABLE, OBJ_LIST_LEVEL, OBJ_LIST_DEFAULT, OBJ_LIST_SURFACE, OBJ_LIST_POLELIKE, OBJ_LIST_UNIMPORTANT }) {
struct Object *_Head = (struct Object *) &gObjectLists[list];
for (struct Object *_Object = (struct Object *) _Head->header.next; _Object != _Head; _Object = (struct Object *) _Object->header.next) {
if (_Object->activeFlags && _Object->header.gfx.sharedChild != NULL) {
if (_Object->header.gfx.sharedChild->georef != NULL) {
GraphNode* georef = (GraphNode*)_Object->header.gfx.sharedChild->georef;
u32 id = 0;
_Object->header.gfx.sharedChild = DynOS_Model_LoadGeo(&id, MODEL_POOL_PERMANENT, georef, true);
}
DynOS_Actor_Override(_Object, (void**)&_Object->header.gfx.sharedChild);
}
}
}
}
static std::unordered_map<s16, size_t> sGraphNodeSizeMap = {
{ GRAPH_NODE_TYPE_ROOT, sizeof(GraphNodeRoot) },
{ GRAPH_NODE_TYPE_ORTHO_PROJECTION, sizeof(GraphNodeOrthoProjection) },
{ GRAPH_NODE_TYPE_PERSPECTIVE, sizeof(GraphNodePerspective) },
{ GRAPH_NODE_TYPE_START, sizeof(GraphNodeStart) },
{ GRAPH_NODE_TYPE_MASTER_LIST, sizeof(GraphNodeMasterList) },
{ GRAPH_NODE_TYPE_LEVEL_OF_DETAIL, sizeof(GraphNodeLevelOfDetail) },
{ GRAPH_NODE_TYPE_SWITCH_CASE, sizeof(GraphNodeSwitchCase) },
{ GRAPH_NODE_TYPE_CAMERA, sizeof(GraphNodeCamera) },
{ GRAPH_NODE_TYPE_TRANSLATION_ROTATION, sizeof(GraphNodeTranslationRotation) },
{ GRAPH_NODE_TYPE_TRANSLATION, sizeof(GraphNodeTranslation) },
{ GRAPH_NODE_TYPE_ROTATION, sizeof(GraphNodeRotation) },
{ GRAPH_NODE_TYPE_SCALE, sizeof(GraphNodeScale) },
{ GRAPH_NODE_TYPE_OBJECT, sizeof(GraphNodeObject) },
{ GRAPH_NODE_TYPE_CULLING_RADIUS, sizeof(GraphNodeCullingRadius) },
{ GRAPH_NODE_TYPE_ANIMATED_PART, sizeof(GraphNodeAnimatedPart) },
{ GRAPH_NODE_TYPE_BILLBOARD, sizeof(GraphNodeBillboard) },
{ GRAPH_NODE_TYPE_DISPLAY_LIST, sizeof(GraphNodeDisplayList) },
{ GRAPH_NODE_TYPE_SHADOW, sizeof(GraphNodeShadow) },
{ GRAPH_NODE_TYPE_OBJECT_PARENT, sizeof(GraphNodeObjectParent) },
{ GRAPH_NODE_TYPE_GENERATED_LIST, sizeof(GraphNodeGenerated) },
{ GRAPH_NODE_TYPE_BACKGROUND, sizeof(GraphNodeBackground) },
{ GRAPH_NODE_TYPE_HELD_OBJ, sizeof(GraphNodeHeldObject) },
};
size_t get_graph_node_size(s16 nodeType) {
auto it = sGraphNodeSizeMap.find(nodeType);
return it != sGraphNodeSizeMap.end() ? it->second : 0;
}
void DynOS_Actor_RegisterModifiedGraphNode(GraphNode *aNode) {
if (sModifiedGraphNodes.find(aNode) == sModifiedGraphNodes.end()) {
size_t size = get_graph_node_size(aNode->type);
if (size == 0) { return; } // Unexpected
GraphNode *graphNodeCopy = (GraphNode *) malloc(size);
memcpy(graphNodeCopy, aNode, size);
sModifiedGraphNodes[aNode] = graphNodeCopy;
}
}
void DynOS_Actor_ModShutdown() {
auto& _DynosCustomActors = DynosCustomActors();
while (_DynosCustomActors.Count() > 0) {
auto& pair = _DynosCustomActors[0];
DynOS_Actor_Invalid(pair.second, MOD_PACK_INDEX);
free((void*)pair.first);
_DynosCustomActors.Remove(0);
}
auto& _ValidActors = DynosValidActors();
for (auto it = _ValidActors.cbegin(); it != _ValidActors.cend();) {
auto& actorGfx = it->second;
if (actorGfx.mPackIndex == MOD_PACK_INDEX) {
DynOS_Gfx_Free(actorGfx.mGfxData);
_ValidActors.erase(it++);
} else {
++it;
}
}
DynOS_Actor_Override_All();
// Reset modified graph nodes
for (auto& node : sModifiedGraphNodes) {
size_t size = get_graph_node_size(node.second->type);
if (size == 0) { continue; } // Unexpected
memcpy(node.first, node.second, size);
free(node.second);
}
sModifiedGraphNodes.clear();
}