mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-08-05 00:59:14 -04:00
Review cleanup
This commit is contained in:
@@ -373,6 +373,15 @@ buffer contract as `get_blob`. Pass a `NULL` buffer to either read function to q
|
||||
are cleared. Observers are removed automatically when the mod is detached, so the output handle is only needed for
|
||||
manual unregistration. Save callbacks run on the game thread.
|
||||
|
||||
### StageService (`mods/svc/stage.h`)
|
||||
|
||||
Patches, removes, or adds serialized stage actor records as rooms load. ACTR records are 0x20 bytes and TGSC records
|
||||
are 0x23 bytes. `record_crc` is the CRC-32 of the unmodified record.
|
||||
|
||||
Stage names may contain up to 8 characters. For patches and deletions, room `0xff` and layer `-1` match any room or
|
||||
layer; additions require a specific room. Edits are removed when the mod is detached. If multiple mods edit the same
|
||||
record, the later-loaded mod wins.
|
||||
|
||||
### UiService (`mods/svc/ui.h`)
|
||||
|
||||
Integrate seamlessly with Dusklight's UI system: add controls and buttons to your mod's detail pane in the Mods window,
|
||||
|
||||
@@ -10,24 +10,30 @@
|
||||
#define STAGE_SERVICE_MAJOR 1u
|
||||
#define STAGE_SERVICE_MINOR 0u
|
||||
|
||||
/* 0 is never a valid handle. */
|
||||
typedef uint64_t StageActorHandle;
|
||||
|
||||
/*
|
||||
* Runtime edits to ACTR (0x20-byte) and TGSC (0x23-byte) stage records.
|
||||
*
|
||||
* stage must be a non-empty name of at most 8 characters. room 0xff and layer -1 match any room
|
||||
* or layer for patch and delete operations; add_actor requires a specific room. Later-loaded mods
|
||||
* win conflicts. record_crc is the CRC-32 of the unmodified record. Registrations are removed when
|
||||
* the calling mod is detached. out_handle may be NULL.
|
||||
*/
|
||||
|
||||
typedef struct StageService {
|
||||
ServiceHeader header;
|
||||
|
||||
/* Replace actor record that matches record_crc with given record */
|
||||
ModResult (*patch_actor)(ModContext* ctx, const char* stage, uint8_t room, int8_t layer,
|
||||
uint32_t record_crc, const void* record, size_t record_size, StageActorHandle* out_handle);
|
||||
|
||||
/* Remove actor record matching record_crc */
|
||||
ModResult (*delete_actor)(ModContext* ctx, const char* stage, uint8_t room, int8_t layer,
|
||||
uint32_t record_crc, StageActorHandle* out_handle);
|
||||
|
||||
/* Add new actor record */
|
||||
ModResult (*add_actor)(ModContext* ctx, const char* stage, uint8_t room, int8_t layer,
|
||||
const void* record, size_t record_size, StageActorHandle* out_handle);
|
||||
|
||||
/* Remove an edit previously registered by the calling mod */
|
||||
ModResult (*remove_actor_edit)(ModContext* ctx, StageActorHandle handle);
|
||||
} StageService;
|
||||
|
||||
|
||||
+27
-8
@@ -1593,13 +1593,17 @@ DUSK_GAME_DATA dStage_roomControl_c::roomDzs_c dStage_roomControl_c::m_roomDzs;
|
||||
u8 dStage_roomControl_c::mNoArcBank;
|
||||
#endif
|
||||
|
||||
static void dStage_actorCreate(stage_actor_data_class* i_actorData, fopAcM_prm_class* i_actorPrm) {
|
||||
#if TARGET_PC
|
||||
if (!dusk::mods::svc::stage_apply_actor_edits(i_actorData, i_actorPrm, i_actorPrm->room_no)) {
|
||||
DuskLog.error("Failed to apply stage actor edits!");
|
||||
static void dStage_actorCreate(stage_actor_data_class* i_actorData, fopAcM_prm_class* i_actorPrm,
|
||||
size_t recordSize = sizeof(stage_actor_data_class)) {
|
||||
if (!dusk::mods::svc::stage_apply_actor_edits(i_actorData, i_actorPrm, recordSize,
|
||||
i_actorPrm->room_no))
|
||||
{
|
||||
JKRFree(i_actorPrm);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
static void dStage_actorCreate(stage_actor_data_class* i_actorData, fopAcM_prm_class* i_actorPrm) {
|
||||
#endif
|
||||
dStage_objectNameInf* actorInf = dStage_searchName(i_actorData->name);
|
||||
|
||||
@@ -2000,7 +2004,12 @@ static int dStage_tgscCommonLayerInit(dStage_dt_c* i_stage, void* i_data, int en
|
||||
appen->base = tgsc_data->base;
|
||||
appen->room_no = (int)i_stage->getRoomNo();
|
||||
appen->scale = tgsc_data->scale;
|
||||
#if TARGET_PC
|
||||
dStage_actorCreate(actor_data, appen,
|
||||
sizeof(stage_actor_data_class) + sizeof(fopAcM_prmScale_class));
|
||||
#else
|
||||
dStage_actorCreate(actor_data, appen);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
tgsc_data++;
|
||||
@@ -2071,7 +2080,12 @@ static int dStage_tgscInfoInit(dStage_dt_c* i_stage, void* i_data, int entryNum,
|
||||
appen->base = actor_data->base;
|
||||
appen->room_no = (int)i_stage->getRoomNo();
|
||||
appen->scale = tgsc_data->scale;
|
||||
#if TARGET_PC
|
||||
dStage_actorCreate(actor_data, appen,
|
||||
sizeof(stage_actor_data_class) + sizeof(fopAcM_prmScale_class));
|
||||
#else
|
||||
dStage_actorCreate(actor_data, appen);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
tgsc_data++;
|
||||
@@ -2094,7 +2108,12 @@ static int dStage_doorInfoInit(dStage_dt_c* i_stage, void* i_data, int entryNum,
|
||||
appen->base = actor_data->base;
|
||||
appen->room_no = (int)i_stage->getRoomNo();
|
||||
appen->scale = tgsc_data->scale;
|
||||
#if TARGET_PC
|
||||
dStage_actorCreate(actor_data, appen,
|
||||
sizeof(stage_actor_data_class) + sizeof(fopAcM_prmScale_class));
|
||||
#else
|
||||
dStage_actorCreate(actor_data, appen);
|
||||
#endif
|
||||
}
|
||||
tgsc_data++;
|
||||
}
|
||||
@@ -2520,7 +2539,7 @@ static void dKankyo_create() {
|
||||
}
|
||||
|
||||
#if TARGET_PC
|
||||
static void duskStageSvc_newActorCreate(dStage_dt_c* i_stage) {
|
||||
static void dusk_stage_svc_new_actor_create(dStage_dt_c* i_stage) {
|
||||
dusk::mods::svc::stage_create_new_actors(i_stage->getRoomNo(),
|
||||
[](void* user, const void* record, size_t size) {
|
||||
auto* stage = static_cast<dStage_dt_c*>(user);
|
||||
@@ -2531,11 +2550,11 @@ static void duskStageSvc_newActorCreate(dStage_dt_c* i_stage) {
|
||||
if (appen != nullptr) {
|
||||
appen->base = object.base;
|
||||
appen->room_no = static_cast<int>(stage->getRoomNo());
|
||||
// if size is greater than 0x20, must be a TGSC actor
|
||||
if (size > 0x20) {
|
||||
if (size > sizeof(stage_actor_data_class)) {
|
||||
appen->scale = object.scale;
|
||||
}
|
||||
dStage_actorCreate(reinterpret_cast<stage_actor_data_class*>(&object), appen);
|
||||
dStage_actorCreate(
|
||||
reinterpret_cast<stage_actor_data_class*>(&object), appen, size);
|
||||
}
|
||||
},
|
||||
i_stage);
|
||||
@@ -2739,7 +2758,7 @@ void dStage_dt_c_roomReLoader(void* i_data, dStage_dt_c* i_stage, int param_2) {
|
||||
|
||||
dStage_dt_c_decode(i_data, i_stage, l_funcTable, ARRAY_SIZEU(l_funcTable));
|
||||
#if TARGET_PC
|
||||
duskStageSvc_newActorCreate(i_stage);
|
||||
dusk_stage_svc_new_actor_create(i_stage);
|
||||
#endif
|
||||
layerActorLoader(i_data, i_stage, param_2);
|
||||
}
|
||||
|
||||
+16
-22
@@ -10,18 +10,18 @@
|
||||
#include "dusk/utilities.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include <string_view>
|
||||
|
||||
namespace dusk::mods::svc {
|
||||
namespace {
|
||||
|
||||
aurora::Module Log("dusk::mods::stage");
|
||||
aurora::Module Log{"dusk::mods::stage"};
|
||||
|
||||
constexpr size_t kACTREntrySize = 0x20;
|
||||
constexpr size_t kTGSCEntrySize = 0x23;
|
||||
constexpr size_t kActrEntrySize = 0x20;
|
||||
constexpr size_t kTgscEntrySize = 0x23;
|
||||
constexpr uint8_t kRoomDefault = 0xFF;
|
||||
constexpr int8_t kLayerDefault = -1;
|
||||
|
||||
@@ -34,16 +34,15 @@ struct ActorEditRecord {
|
||||
uint8_t room = 0;
|
||||
int8_t layer = kLayerDefault;
|
||||
EditKind kind = EditKind::Patch;
|
||||
uint32_t crc = 0; // Patch/Delete
|
||||
std::vector<uint8_t> record; // Patch/Add
|
||||
uint32_t crc = 0;
|
||||
std::vector<uint8_t> record;
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, std::vector<ActorEditRecord>> s_edits;
|
||||
uint64_t s_nextHandle = 1;
|
||||
uint64_t s_nextSeq = 1;
|
||||
std::unordered_set<uint64_t> s_warnedRecords; // (crc | seq-of-stage-hash) collision warnings
|
||||
std::unordered_set<uint32_t> s_warnedRecords;
|
||||
|
||||
// Position in m_mods (dependency-sorted load order) + 1; later-loaded mods win.
|
||||
int32_t compute_mod_priority(const LoadedMod& mod) {
|
||||
int32_t index = 0;
|
||||
for (const auto& other : ModLoader::instance().mods()) {
|
||||
@@ -77,21 +76,19 @@ bool layer_matches(const ActorEditRecord& record, int8_t layer) {
|
||||
|
||||
} // namespace
|
||||
|
||||
bool stage_apply_actor_edits(void* actorData, void* actorPrm, int8_t roomNo) {
|
||||
bool stage_apply_actor_edits(void* actorData, void* actorPrm, size_t recordSize, int8_t roomNo) {
|
||||
const auto* edits = current_stage_edits();
|
||||
if (edits == nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// PLYR spawns use room -1 to signify using the start room ID
|
||||
uint8_t room = static_cast<uint8_t>(roomNo);
|
||||
if (roomNo == -1) {
|
||||
room = static_cast<uint8_t>(dComIfGp_getStartStageRoomNo());
|
||||
}
|
||||
|
||||
const auto layer = static_cast<int8_t>(dComIfG_play_c::getLayerNo(0));
|
||||
const auto crcActr = utils::crc32(actorData, kACTREntrySize);
|
||||
const auto crcTgsc = utils::crc32(actorData, kTGSCEntrySize);
|
||||
const auto crc = utils::crc32(actorData, recordSize);
|
||||
|
||||
const ActorEditRecord* winner = nullptr;
|
||||
int32_t winnerPriority = 0;
|
||||
@@ -103,10 +100,8 @@ bool stage_apply_actor_edits(void* actorData, void* actorPrm, int8_t roomNo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check record type based on size
|
||||
const bool matches = record.kind == EditKind::Delete
|
||||
? record.crc == crcActr || record.crc == crcTgsc
|
||||
: record.crc == (record.record.size() == kTGSCEntrySize ? crcTgsc : crcActr);
|
||||
const bool matches = record.crc == crc && (record.kind == EditKind::Delete ||
|
||||
record.record.size() == recordSize);
|
||||
if (!matches) {
|
||||
continue;
|
||||
}
|
||||
@@ -158,8 +153,8 @@ void stage_create_new_actors(
|
||||
|
||||
namespace {
|
||||
|
||||
ModResult register_edit(LoadedMod& mod, const char* stage, ActorEditRecord&& record,
|
||||
uint64_t& outHandle) {
|
||||
ModResult register_edit(
|
||||
LoadedMod& mod, const char* stage, ActorEditRecord&& record, uint64_t& outHandle) {
|
||||
record.handle = s_nextHandle++;
|
||||
record.mod = &mod;
|
||||
record.seq = s_nextSeq++;
|
||||
@@ -202,9 +197,8 @@ ModResult stage_add_actor(LoadedMod& mod, const char* stage, uint8_t room, int8_
|
||||
|
||||
ModResult stage_remove_actor_edit(LoadedMod& mod, uint64_t handle) {
|
||||
for (auto it = s_edits.begin(); it != s_edits.end(); ++it) {
|
||||
const auto removed = std::erase_if(it->second, [&](const auto& record) {
|
||||
return record.handle == handle && record.mod == &mod;
|
||||
});
|
||||
const auto removed = std::erase_if(it->second,
|
||||
[&](const auto& record) { return record.handle == handle && record.mod == &mod; });
|
||||
if (removed != 0) {
|
||||
if (it->second.empty()) {
|
||||
s_edits.erase(it);
|
||||
@@ -234,7 +228,7 @@ bool is_valid_stage_name(const char* stage) {
|
||||
}
|
||||
|
||||
bool is_valid_record_size(size_t size) {
|
||||
return size == kACTREntrySize || size == kTGSCEntrySize;
|
||||
return size == kActrEntrySize || size == kTgscEntrySize;
|
||||
}
|
||||
|
||||
ModResult stage_patch_actor_(ModContext* context, const char* stage, uint8_t room, int8_t layer,
|
||||
|
||||
@@ -5,11 +5,9 @@
|
||||
|
||||
namespace dusk::mods::svc {
|
||||
|
||||
// Apply registered patch/delete edits for the current stage/layer to the record about to spawn
|
||||
bool stage_apply_actor_edits(void* actorData, void* actorPrm, int8_t roomNo);
|
||||
bool stage_apply_actor_edits(void* actorData, void* actorPrm, size_t recordSize, int8_t roomNo);
|
||||
|
||||
// Creates registered new actors from their record's on room load
|
||||
void stage_create_new_actors(
|
||||
int8_t roomNo, void (*createFn)(void* user, const void* record, size_t size), void* user);
|
||||
|
||||
} // namespace dusk::mods
|
||||
} // namespace dusk::mods::svc
|
||||
|
||||
Reference in New Issue
Block a user