mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2026-07-06 14:36:46 +00:00
264 lines
9.1 KiB
C
264 lines
9.1 KiB
C
#include <stdio.h>
|
|
#include "../network.h"
|
|
#include "object_fields.h"
|
|
#include "object_constants.h"
|
|
#include "game/object_helpers.h"
|
|
#include "game/area.h"
|
|
#include "behavior_data.h"
|
|
#include "behavior_table.h"
|
|
#include "pc/lua/smlua.h"
|
|
#include "pc/lua/utils/smlua_model_utils.h"
|
|
#include "pc/lua/utils/smlua_obj_utils.h"
|
|
#include "pc/crash_handler.h"
|
|
//#define DISABLE_MODULE_LOG 1
|
|
#include "pc/debuglog.h"
|
|
|
|
#define MAX_SPAWN_OBJECTS_PER_PACKET 8
|
|
|
|
#pragma pack(1)
|
|
struct SpawnObjectData {
|
|
u8 ctx;
|
|
u32 parentId;
|
|
u32 model;
|
|
u32 behaviorId;
|
|
s16 activeFlags;
|
|
s32 rawData[OBJECT_NUM_FIELDS];
|
|
u8 setHome;
|
|
u8 globalPlayerIndex;
|
|
u16 extendedModelId;
|
|
u8 extraFieldCount;
|
|
};
|
|
#pragma pack()
|
|
|
|
static u32 generate_parent_id(struct Object* objects[], u8 onIndex, bool sanitize) {
|
|
struct Object* o = objects[onIndex];
|
|
if (!o) { return (u32)-1; }
|
|
|
|
// special case if the parent is itself
|
|
if (o->parentObj == o) { return (u32)-1; }
|
|
|
|
if (onIndex == 0) {
|
|
if (!o->parentObj) {
|
|
return (u32)-1;
|
|
}
|
|
if (sanitize && o->parentObj->oSyncID == 0) {
|
|
return (u32)-1;
|
|
}
|
|
SOFT_ASSERT_RETURN(o->parentObj->oSyncID != 0, (u32)-1);
|
|
return (u32)o->parentObj->oSyncID;
|
|
}
|
|
|
|
for (u8 i = onIndex; i != (u8)-1; i--) {
|
|
if (o->parentObj == objects[i]) { return i; }
|
|
}
|
|
|
|
SOFT_ASSERT_RETURN(false, (u32)-1);
|
|
}
|
|
|
|
void network_send_spawn_objects(struct Object* objects[], u32 models[], u8 objectCount) {
|
|
network_send_spawn_objects_to(PACKET_DESTINATION_BROADCAST, objects, models, objectCount);
|
|
}
|
|
|
|
void network_send_spawn_objects_to(u8 sendToLocalIndex, struct Object* objects[], u32 models[], u8 objectCount) {
|
|
if (gNetworkPlayerLocal == NULL || !gNetworkPlayerLocal->currAreaSyncValid) {
|
|
LOG_ERROR("failed: area sync invalid");
|
|
return;
|
|
}
|
|
|
|
if (objectCount == 0) {
|
|
LOG_ERROR("Tried to send 0 objects");
|
|
return;
|
|
}
|
|
|
|
SOFT_ASSERT(objectCount < MAX_SPAWN_OBJECTS_PER_PACKET);
|
|
// prevent sending spawn objects during credits
|
|
if (gCurrActStarNum == 99) {
|
|
LOG_ERROR("failed: in credits");
|
|
return;
|
|
}
|
|
|
|
struct Packet p = { 0 };
|
|
packet_init(&p, PACKET_SPAWN_OBJECTS, true, PLMT_AREA);
|
|
|
|
// objects
|
|
packet_write(&p, &objectCount, sizeof(u8));
|
|
|
|
for (u8 i = 0; i < objectCount; i++) {
|
|
struct Object* o = objects[i];
|
|
if (!o || !o->ctx) {
|
|
LOG_ERROR("Tried to send null object");
|
|
return;
|
|
}
|
|
|
|
u32 model = models[i];
|
|
u32 parentId = generate_parent_id(objects, i, true);
|
|
u32 behaviorId = get_id_from_behavior(o->behavior);
|
|
struct SyncObject* so = sync_object_get(o->oSyncID);
|
|
u16 extendedModelId = (so && so->o == o)
|
|
? so->extendedModelId
|
|
: 0xFFFF;
|
|
packet_write(&p, &o->ctx, sizeof(u8));
|
|
packet_write(&p, &parentId, sizeof(u32));
|
|
packet_write(&p, &model, sizeof(u32));
|
|
packet_write(&p, &behaviorId, sizeof(u32));
|
|
packet_write(&p, &o->activeFlags, sizeof(s16));
|
|
packet_write(&p, o->rawData.asU32, sizeof(u32) * OBJECT_NUM_FIELDS);
|
|
packet_write(&p, &o->header.gfx.scale[0], sizeof(f32));
|
|
packet_write(&p, &o->header.gfx.scale[1], sizeof(f32));
|
|
packet_write(&p, &o->header.gfx.scale[2], sizeof(f32));
|
|
packet_write(&p, &o->setHome, sizeof(u8));
|
|
packet_write(&p, &o->globalPlayerIndex, sizeof(u8));
|
|
packet_write(&p, &extendedModelId, sizeof(u16));
|
|
}
|
|
|
|
if (sendToLocalIndex == PACKET_DESTINATION_BROADCAST) {
|
|
network_send(&p);
|
|
if (objects[0] && objects[0]->behavior) {
|
|
LOG_INFO("tx spawn objects (BROADCAST) | %u", get_id_from_behavior(objects[0]->behavior));
|
|
}
|
|
} else {
|
|
network_send_to(sendToLocalIndex, &p);
|
|
if (objects[0] && objects[0]->behavior) {
|
|
LOG_INFO("tx spawn objects to %d | %u", gNetworkPlayers[sendToLocalIndex].globalIndex, get_id_from_behavior(objects[0]->behavior));
|
|
}
|
|
}
|
|
}
|
|
|
|
void network_receive_spawn_objects(struct Packet* p) {
|
|
// prevent receiving spawn objects during credits
|
|
if (gCurrActStarNum == 99) {
|
|
LOG_ERROR("rx failed: in credits");
|
|
return;
|
|
}
|
|
|
|
u8 objectCount = 0;
|
|
packet_read(p, &objectCount, sizeof(u8));
|
|
|
|
struct Object* spawned[MAX_SPAWN_OBJECTS_PER_PACKET] = { 0 };
|
|
for (u8 i = 0; i < objectCount; i++) {
|
|
struct SpawnObjectData data = { 0 };
|
|
Vec3f scale = { 0 };
|
|
u8 ctx = 0;
|
|
packet_read(p, &ctx, sizeof(u8));
|
|
packet_read(p, &data.parentId, sizeof(u32));
|
|
packet_read(p, &data.model, sizeof(u32));
|
|
packet_read(p, &data.behaviorId, sizeof(u32));
|
|
packet_read(p, &data.activeFlags, sizeof(s16));
|
|
packet_read(p, &data.rawData, sizeof(u32) * OBJECT_NUM_FIELDS);
|
|
packet_read(p, &scale[0], sizeof(f32));
|
|
packet_read(p, &scale[1], sizeof(f32));
|
|
packet_read(p, &scale[2], sizeof(f32));
|
|
packet_read(p, &data.setHome, sizeof(u8));
|
|
packet_read(p, &data.globalPlayerIndex, sizeof(u8));
|
|
packet_read(p, &data.extendedModelId, sizeof(u16));
|
|
|
|
char* id = "unknown";
|
|
char* name = "unknown";
|
|
if (gNetworkSystem && p->localIndex) {
|
|
id = gNetworkSystem->get_id_str(p->localIndex);
|
|
name = gNetworkPlayers[p->localIndex].name;
|
|
}
|
|
|
|
const char *bhvName = get_behavior_name_from_id(data.behaviorId);
|
|
|
|
// Don't overwrite existing sync objects
|
|
{
|
|
u32 syncID = data.rawData[0x04]; // o->oSyncID
|
|
struct SyncObject *so = sync_object_get(syncID);
|
|
if (so && so->o) {
|
|
if (so->o->behavior == get_behavior_from_id(data.behaviorId)) {
|
|
LOG_ERROR("recieved duplicate sync object with id %d from %s (%s)", syncID, name, id);
|
|
} else {
|
|
LOG_ERROR("recieved duplicate sync object with id %d with different behavior %s from %s (%s)", syncID, bhvName, name, id);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
LOG_INFO("rx spawn object %s from %s (%s)", bhvName, name, id);
|
|
LOG_CONSOLE("rx spawn object %s from %s\\#\\ (%s)", bhvName, name, id);
|
|
snprintf(gLastRemoteBhv, 256, "%s %s (%s)", bhvName, name, id);
|
|
|
|
struct Object* parentObj = NULL;
|
|
if (data.parentId == (u32)-1) {
|
|
// this object is it's own parent, set it to a known object temporarily
|
|
parentObj = gMarioStates[0].marioObj;
|
|
} else {
|
|
|
|
// this object has a known parent
|
|
struct SyncObject* parentSo = sync_object_get(data.parentId);
|
|
if (i == 0 && !parentSo) {
|
|
LOG_ERROR("Invalid spawn object parentId: %u", data.parentId);
|
|
return;
|
|
}
|
|
|
|
parentObj = (i == 0)
|
|
? parentSo->o
|
|
: spawned[data.parentId];
|
|
|
|
if (parentObj == NULL) {
|
|
// failed to find parent, make it it's own parent
|
|
// may cause issues, but we want it to spawn!
|
|
LOG_ERROR("ERROR: failed to find spawn object's parent (%d)!", data.parentId);
|
|
parentObj = gMarioStates[0].marioObj;
|
|
data.parentId = (u32)-1;
|
|
}
|
|
}
|
|
|
|
if (parentObj == NULL) {
|
|
LOG_ERROR("ERROR: failed to attach to mario!");
|
|
return;
|
|
}
|
|
|
|
// load extended model
|
|
if (data.extendedModelId != 0xFFFF) {
|
|
u16 loadedModelId = smlua_model_util_load(data.extendedModelId);
|
|
data.model = loadedModelId;
|
|
}
|
|
|
|
void* behavior = (void*)get_behavior_from_id(data.behaviorId);
|
|
struct Object* o = NULL;
|
|
if (ctx) { o = spawn_object(parentObj, data.model, behavior); }
|
|
if (o == NULL) {
|
|
LOG_ERROR("ERROR: failed to allocate object!");
|
|
return;
|
|
}
|
|
|
|
o->ctx = ctx;
|
|
o->globalPlayerIndex = data.globalPlayerIndex;
|
|
o->coopFlags |= COOP_OBJ_FLAG_NETWORK;
|
|
o->setHome = data.setHome;
|
|
|
|
memcpy(o->rawData.asU32, data.rawData, sizeof(u32) * OBJECT_NUM_FIELDS);
|
|
|
|
o->header.gfx.scale[0] = scale[0];
|
|
o->header.gfx.scale[1] = scale[1];
|
|
o->header.gfx.scale[2] = scale[2];
|
|
|
|
// correct the temporary parent with the object itself
|
|
if (data.parentId == (u32)-1) { o->parentObj = o; }
|
|
|
|
if (o->oSyncID != 0) {
|
|
// check if they've allocated one of their reserved sync objects
|
|
struct SyncObject* so = sync_object_get(o->oSyncID);
|
|
if (!so) {
|
|
sync_object_set_id(o);
|
|
so = sync_object_get(o->oSyncID);
|
|
}
|
|
if (so) {
|
|
so->o = o;
|
|
so->behavior = behavior;
|
|
so->extendedModelId = data.extendedModelId;
|
|
so->txEventId = 0;
|
|
for (s32 j = 0; j < MAX_PLAYERS; j++) {
|
|
so->rxEventId[j] = 0;
|
|
}
|
|
} else {
|
|
LOG_ERROR("Invalid spawn object sync id: %u", o->oSyncID);
|
|
return;
|
|
}
|
|
}
|
|
|
|
spawned[i] = o;
|
|
}
|
|
}
|