mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2025-10-30 08:01:01 +00:00
Now level changes and area changes are separated. Entity deletions are properly tracked on level change or area change. Late entity synchronization is now triggered on any area change.
202 lines
No EOL
7.1 KiB
C
202 lines
No EOL
7.1 KiB
C
#include <stdio.h>
|
|
#include "../network.h"
|
|
#include "game/interaction.h"
|
|
#include "game/object_list_processor.h"
|
|
#include "game/object_helpers.h"
|
|
#include "game/interaction.h"
|
|
#include "game/level_update.h"
|
|
#include "game/macro_special_objects.h"
|
|
#include "macro_presets.h"
|
|
#include "object_constants.h"
|
|
#include "object_fields.h"
|
|
#include "behavior_table.h"
|
|
#include "model_ids.h"
|
|
#define DISABLE_MODULE_LOG 1
|
|
#include "pc/debuglog.h"
|
|
|
|
static struct Object* get_object_matching_respawn_info(s16* respawnInfo) {
|
|
for (int i = 0; i < OBJECT_POOL_CAPACITY; i++) {
|
|
struct Object* o = &gObjectPool[i];
|
|
if (o->respawnInfo == respawnInfo) { return o; }
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
////
|
|
|
|
static void network_send_level_macro_area(struct NetworkPlayer* destNp, u8 areaIndex) {
|
|
// check that the area is active
|
|
struct Area* area = &gAreaData[areaIndex];
|
|
if (area->unk04 == NULL) { return; }
|
|
|
|
if (destNp == NULL || !destNp->connected) {
|
|
LOG_ERROR("network_send_level_macro: dest np is invalid");
|
|
return;
|
|
}
|
|
|
|
// write header
|
|
struct Packet p;
|
|
packet_init(&p, PACKET_LEVEL_MACRO, true, false);
|
|
packet_write(&p, &gCurrCourseNum, sizeof(s16));
|
|
packet_write(&p, &gCurrActNum, sizeof(s16));
|
|
packet_write(&p, &gCurrLevelNum, sizeof(s16));
|
|
packet_write(&p, &gCurrAreaIndex, sizeof(s16));
|
|
|
|
// write this area's index
|
|
packet_write(&p, &areaIndex, sizeof(u8));
|
|
|
|
// write the amount of deletions
|
|
u8 zero = 0;
|
|
u8* macroDeletionCount = &p.buffer[p.cursor];
|
|
packet_write(&p, &zero, sizeof(u8));
|
|
|
|
// loop through macro objects for deletions
|
|
s16* macroObjList = area->macroObjects;
|
|
while (macroObjList != NULL && *macroObjList != -1) {
|
|
// grab preset ID
|
|
s32 presetID = (*macroObjList & 0x1FF) - 31; // Preset identifier for MacroObjectPresets array
|
|
if (presetID < 0) { break; }
|
|
|
|
// parse respawn info
|
|
macroObjList += 4;
|
|
s16* respawnInfo = macroObjList++;
|
|
|
|
// if a macro object was destroyed, send its respawnInfo offset
|
|
if (((*respawnInfo >> 8) & RESPAWN_INFO_DONT_RESPAWN) == RESPAWN_INFO_DONT_RESPAWN) {
|
|
*macroDeletionCount = *macroDeletionCount + 1;
|
|
u16 offset = respawnInfo - area->macroObjects;
|
|
packet_write(&p, &offset, sizeof(u16));
|
|
LOG_INFO("tx macro: offset %d", offset);
|
|
}
|
|
}
|
|
|
|
// write the amount of special cases
|
|
u8* macroSpecialCount = &p.buffer[p.cursor];
|
|
packet_write(&p, &zero, sizeof(u8));
|
|
|
|
// loop through macro objects for special cases
|
|
macroObjList = area->macroObjects;
|
|
while (macroObjList != NULL && *macroObjList != -1) {
|
|
// grab preset ID
|
|
s32 presetID = (*macroObjList & 0x1FF) - 31; // Preset identifier for MacroObjectPresets array
|
|
if (presetID < 0) { break; }
|
|
|
|
// parse respawn info
|
|
macroObjList += 4;
|
|
s16* respawnInfo = macroObjList++;
|
|
|
|
// check for special cases
|
|
const BehaviorScript* behavior = MacroObjectPresets[presetID].behavior;
|
|
if (behavior == bhvCoinFormation && *respawnInfo != 0) {
|
|
*macroSpecialCount = *macroSpecialCount + 1;
|
|
u16 offset = respawnInfo - area->macroObjects;
|
|
packet_write(&p, &offset, sizeof(u16));
|
|
packet_write(&p, respawnInfo, sizeof(s16));
|
|
LOG_INFO("tx macro special: offset %d, respawnInfo %d", offset, *respawnInfo);
|
|
}
|
|
}
|
|
|
|
// send the packet if there are deletions
|
|
if (*macroDeletionCount > 0 || *macroSpecialCount > 0) {
|
|
network_send_to(destNp->localIndex, &p);
|
|
LOG_INFO("tx macro for area %d (count %d, %d)", areaIndex, *macroDeletionCount, *macroSpecialCount);
|
|
}
|
|
}
|
|
|
|
void network_send_level_macro(struct NetworkPlayer* destNp) {
|
|
if (!gNetworkPlayerLocal->currLevelSyncValid) {
|
|
LOG_ERROR("my area is invalid");
|
|
return;
|
|
}
|
|
|
|
if (destNp == NULL || !destNp->connected) {
|
|
LOG_ERROR("network_send_level_macro: dest np is invalid");
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < 8; i++) {
|
|
network_send_level_macro_area(destNp, i);
|
|
}
|
|
}
|
|
|
|
void network_receive_level_macro(struct Packet* p) {
|
|
s16 courseNum, actNum, levelNum, areaIndex;
|
|
packet_read(p, &courseNum, sizeof(s16));
|
|
packet_read(p, &actNum, sizeof(s16));
|
|
packet_read(p, &levelNum, sizeof(s16));
|
|
packet_read(p, &areaIndex, sizeof(s16));
|
|
|
|
if (courseNum != gCurrCourseNum || actNum != gCurrActNum || levelNum != gCurrLevelNum) {
|
|
LOG_ERROR("Receiving 'location response' with the wrong location!");
|
|
return;
|
|
}
|
|
|
|
u8 thisAreaIndex;
|
|
packet_read(p, &thisAreaIndex, sizeof(u8));
|
|
|
|
// read and execute macro deletions
|
|
u8 macroDeletionCount;
|
|
packet_read(p, ¯oDeletionCount, sizeof(u8));
|
|
LOG_INFO("rx macro (count %d)", macroDeletionCount);
|
|
|
|
while (macroDeletionCount-- > 0) {
|
|
u16 offset;
|
|
packet_read(p, &offset, sizeof(u16));
|
|
LOG_INFO("rx macro deletion: offset %d", offset);
|
|
|
|
// mark respawninfo as dont respawn
|
|
s16* respawnInfo = gAreaData[thisAreaIndex].macroObjects + offset;
|
|
*respawnInfo |= RESPAWN_INFO_DONT_RESPAWN << 8;
|
|
|
|
struct Object* o = get_object_matching_respawn_info(respawnInfo);
|
|
if (o != NULL) {
|
|
obj_mark_for_deletion(o);
|
|
LOG_INFO("rx macro deletion: object");
|
|
if (o->oSyncID != 0) {
|
|
struct SyncObject* so = &gSyncObjects[o->oSyncID];
|
|
if (so->o == o) {
|
|
network_forget_sync_object(so);
|
|
LOG_INFO("rx macro deletion: sync object");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// read and execute macro specials
|
|
u8 macroSpecialCount;
|
|
packet_read(p, ¯oSpecialCount, sizeof(u8));
|
|
while (macroSpecialCount-- > 0) {
|
|
u16 offset;
|
|
packet_read(p, &offset, sizeof(u16));
|
|
|
|
s16* respawnInfo = gAreaData[thisAreaIndex].macroObjects + offset;
|
|
packet_read(p, respawnInfo, sizeof(s16));
|
|
LOG_INFO("rx macro special: offset %d, respawnInfo %d", offset, *respawnInfo);
|
|
|
|
s32 presetID = (*(respawnInfo - 4) & 0x1FF) - 31;
|
|
const BehaviorScript* behavior = MacroObjectPresets[presetID].behavior;
|
|
|
|
struct Object* o = get_object_matching_respawn_info(respawnInfo);
|
|
if (o != NULL) {
|
|
LOG_INFO("rx macro special: object");
|
|
// coin formation
|
|
if (behavior == bhvCoinFormation) {
|
|
o->oBehParams = *respawnInfo;
|
|
o->oCoinUnkF4 = (o->oBehParams >> 8) & 0xFF;
|
|
|
|
u8 childIndex = 0;
|
|
for (int i = 0; i < OBJECT_POOL_CAPACITY; i++) {
|
|
struct Object* o2 = &gObjectPool[i];
|
|
if (o2->parentObj != o) { continue; }
|
|
if (o2 == o) { continue; }
|
|
if (o2->behavior != bhvCoinFormationSpawn && o2->behavior != bhvYellowCoin) { continue; }
|
|
if (o->oCoinUnkF4 & (1 << childIndex++)) {
|
|
obj_mark_for_deletion(o2);
|
|
}
|
|
}
|
|
LOG_INFO("rx macro special: coin formation");
|
|
}
|
|
}
|
|
}
|
|
} |