diff --git a/src/engine/behavior_script.c b/src/engine/behavior_script.c index 1537684e7..bc615156c 100644 --- a/src/engine/behavior_script.c +++ b/src/engine/behavior_script.c @@ -949,7 +949,7 @@ static BhvCommandProc BehaviorCmdTable[] = { bhv_cmd_set_int_unused, //36 bhv_cmd_spawn_water_droplet, //37 bhv_cmd_cylboard, //38 - bhv_cmd_id //38 + bhv_cmd_id //39 }; // Execute the behavior script of the current object, process the object flags, and other miscellaneous code for updating objects. diff --git a/src/pc/network/packets/packet_level_warp.c b/src/pc/network/packets/packet_level_warp.c index 7e9ea261d..6460b6f24 100644 --- a/src/pc/network/packets/packet_level_warp.c +++ b/src/pc/network/packets/packet_level_warp.c @@ -8,10 +8,10 @@ int matchCount = 0; void network_send_level_warp(void) { struct Packet p; packet_init(&p, PACKET_LEVEL_WARP, false); - packet_write(&p, &sCurrPlayMode, 2); - packet_write(&p, &gCurrLevelNum, 2); - packet_write(&p, &sDelayedWarpArg, 4); - packet_write(&p, &sSourceWarpNodeId, 2); + packet_write(&p, &sCurrPlayMode, sizeof(s16)); + packet_write(&p, &gCurrLevelNum, sizeof(s16)); + packet_write(&p, &sDelayedWarpArg, sizeof(s32)); + packet_write(&p, &sSourceWarpNodeId, sizeof(s16)); network_send(&p); } @@ -22,10 +22,10 @@ void network_receive_level_warp(struct Packet* p) { s32 remoteWarpArg; s16 remoteWarpNodeId; - packet_read(p, &remotePlayMode, 2); - packet_read(p, &remoteLevelNum, 2); - packet_read(p, &remoteWarpArg, 4); - packet_read(p, &remoteWarpNodeId, 2); + packet_read(p, &remotePlayMode, sizeof(s16)); + packet_read(p, &remoteLevelNum, sizeof(s16)); + packet_read(p, &remoteWarpArg, sizeof(s32)); + packet_read(p, &remoteWarpNodeId, sizeof(s16)); bool matching = (remoteLevelNum == gCurrLevelNum) && (remoteWarpArg == sDelayedWarpArg) @@ -62,4 +62,4 @@ void network_receive_level_warp(struct Packet* p) { void network_update_level_warp(void) { network_send_level_warp(); -} \ No newline at end of file +} diff --git a/src/pc/network/packets/packet_object.c b/src/pc/network/packets/packet_object.c index 4f3463258..ebf3ac96d 100644 --- a/src/pc/network/packets/packet_object.c +++ b/src/pc/network/packets/packet_object.c @@ -9,7 +9,20 @@ u8 nextSyncID = 1; struct SyncObject syncObjects[MAX_SYNC_OBJECTS] = { 0 }; +// todo: move this to somewhere more general +float player_distance(struct MarioState* marioState, struct Object* o) { + if (marioState->marioObj == NULL) { return 0; } + f32 mx = marioState->marioObj->header.gfx.pos[0] - o->oPosX; + f32 my = marioState->marioObj->header.gfx.pos[1] - o->oPosY; + f32 mz = marioState->marioObj->header.gfx.pos[2] - o->oPosZ; + mx *= mx; + my *= my; + mz *= mz; + return sqrt(mx + my + mz); +} + void network_init_object(struct Object *o, float maxSyncDistance) { + // generate new sync ID if (o->oSyncID == 0) { for (int i = 0; i < MAX_SYNC_OBJECTS; i++) { if (syncObjects[nextSyncID].o == NULL) { break; } @@ -20,6 +33,8 @@ void network_init_object(struct Object *o, float maxSyncDistance) { nextSyncID = (nextSyncID + 1) % MAX_SYNC_OBJECTS; } assert(o->oSyncID < MAX_SYNC_OBJECTS); + + // set default values for sync object struct SyncObject* so = &syncObjects[o->oSyncID]; so->o = o; so->maxSyncDistance = maxSyncDistance; @@ -37,6 +52,7 @@ void network_init_object(struct Object *o, float maxSyncDistance) { void network_object_settings(struct Object *o, bool fullObjectSync, float maxUpdateRate, bool keepRandomSeed, u8 ignore_if_true(struct Object*)) { assert(o->oSyncID != 0); + // override default settings for sync object struct SyncObject* so = &syncObjects[o->oSyncID]; so->fullObjectSync = fullObjectSync; so->maxUpdateRate = maxUpdateRate; @@ -46,6 +62,7 @@ void network_object_settings(struct Object *o, bool fullObjectSync, float maxUpd void network_init_object_field(struct Object *o, void* field) { assert(o->oSyncID != 0); + // remember to synchronize this extra field struct SyncObject* so = &syncObjects[o->oSyncID]; int index = so->extraFieldCount++; so->extraFields[index] = field; @@ -57,84 +74,51 @@ bool network_owns_object(struct Object* o) { return so->owned; } -void network_send_object(struct Object* o) { +// ----- header ----- // + +static void packet_write_object_header(struct Packet* p, struct Object* o) { struct SyncObject* so = &syncObjects[o->oSyncID]; - if (so == NULL) { return; } - - so->onEventId++; - enum BehaviorId behaviorId = get_id_from_behavior(o->behavior); - bool reliable = (o->activeFlags == ACTIVE_FLAG_DEACTIVATED || so->maxSyncDistance == SYNC_DISTANCE_ONLY_EVENTS); - struct Packet p; - packet_init(&p, PACKET_OBJECT, reliable); - packet_write(&p, &o->oSyncID, 4); - packet_write(&p, &so->onEventId, sizeof(u16)); - packet_write(&p, &behaviorId, sizeof(enum BehaviorId)); - - if (so->maxSyncDistance != SYNC_DISTANCE_ONLY_EVENTS) { - packet_write(&p, &o->activeFlags, sizeof(s16)); - packet_write(&p, &o->header.gfx.node.flags, sizeof(s16)); - } - - if (so->fullObjectSync) { - packet_write(&p, o->rawData.asU32, sizeof(u32) * 80); - } else { - - if (so->maxSyncDistance != SYNC_DISTANCE_ONLY_EVENTS) { - packet_write(&p, &o->oPosX, sizeof(u32) * 7); - packet_write(&p, &o->oAction, sizeof(u32)); - packet_write(&p, &o->oSubAction, sizeof(u32)); - packet_write(&p, &o->oInteractStatus, sizeof(u32)); - packet_write(&p, &o->oHeldState, sizeof(u32)); - packet_write(&p, &o->oMoveAngleYaw, sizeof(u32)); - packet_write(&p, &o->oTimer, sizeof(u32)); - } - - packet_write(&p, &so->extraFieldCount, sizeof(u8)); - for (int i = 0; i < so->extraFieldCount; i++) { - assert(so->extraFields[i] != NULL); - packet_write(&p, so->extraFields[i], sizeof(u32)); - } - } - - so->clockSinceUpdate = clock(); - - if (o->activeFlags == ACTIVE_FLAG_DEACTIVATED) { forget_sync_object(so); } - - if (o->behavior != so->behavior) { - printf("network_send_object() BEHAVIOR MISMATCH!\n"); - forget_sync_object(so); - return; - } - - network_send(&p); + packet_write(p, &o->oSyncID, sizeof(u32)); + packet_write(p, &so->onEventId, sizeof(u16)); + packet_write(p, &behaviorId, sizeof(enum BehaviorId)); } -void network_receive_object(struct Packet* p) { - // get sync ID - u32 syncId; +static struct SyncObject* packet_read_object_header(struct Packet* p) { + // get sync ID, sanity check + u32 syncId = 0; packet_read(p, &syncId, sizeof(u32)); - assert(syncId < MAX_SYNC_OBJECTS); + if (syncId == 0 || syncId >= MAX_SYNC_OBJECTS) { + printf("%s invalid SyncID!\n", NETWORKTYPESTR, syncId); + return NULL; + } - // retrieve SyncObject + // retrieve SyncObject, check if we should update using callback struct SyncObject* so = &syncObjects[syncId]; + if (so->ignore_if_true != NULL && (*so->ignore_if_true)(so->o)) { + return NULL; + } so->clockSinceUpdate = clock(); - if (so->ignore_if_true != NULL && (*so->ignore_if_true)(so->o)) { return; } - // extract Object + // extract object, sanity check struct Object* o = syncObjects[syncId].o; - if (o == NULL) { printf("%s failed to receive object!\n", NETWORKTYPESTR); return; } + if (o == NULL) { + printf("%s invalid SyncObject!\n", NETWORKTYPESTR); + return NULL; + } // make sure it's active if (o->activeFlags == ACTIVE_FLAG_DEACTIVATED) { - return; + return NULL; } // make sure this is the newest event possible - volatile u16 eventId = 0; + u16 eventId = 0; packet_read(p, &eventId, sizeof(u16)); - if (so->onEventId > eventId && (u16)abs(eventId - so->onEventId) < USHRT_MAX / 2) { return; } + if (so->onEventId > eventId && (u16)abs(eventId - so->onEventId) < USHRT_MAX / 2) { + return NULL; + } so->onEventId = eventId; // make sure the behaviors match @@ -142,56 +126,175 @@ void network_receive_object(struct Packet* p) { packet_read(p, &behaviorId, sizeof(enum BehaviorId)); so->behavior = get_behavior_from_id(behaviorId); if (o->behavior != so->behavior) { - printf("network_receive_object() BEHAVIOR MISMATCH!\n"); + printf("network_receive_object() behavior mismatch!\n"); + forget_sync_object(so); + return NULL; + } + + return so; +} + +// ----- full sync ----- // + +static void packet_write_object_full_sync(struct Packet* p, struct Object* o) { + struct SyncObject* so = &syncObjects[o->oSyncID]; + if (!so->fullObjectSync) { return; } + + // write all of raw data + packet_write(p, o->rawData.asU32, sizeof(u32) * 80); +} + +static void packet_read_object_full_sync(struct Packet* p, struct Object* o) { + struct SyncObject* so = &syncObjects[o->oSyncID]; + if (!so->fullObjectSync) { return; } + + // read all of raw data + packet_read(p, o->rawData.asU32, sizeof(u32) * 80); +} + +// ----- standard fields ----- // + +static void packet_write_object_standard_fields(struct Packet* p, struct Object* o) { + struct SyncObject* so = &syncObjects[o->oSyncID]; + if (so->fullObjectSync) { return; } + if (so->maxSyncDistance == SYNC_DISTANCE_ONLY_DEATH) { return; } + if (so->maxSyncDistance == SYNC_DISTANCE_ONLY_EVENTS) { return; } + + // write the standard fields + packet_write(p, &o->oPosX, sizeof(u32) * 7); + packet_write(p, &o->oAction, sizeof(u32)); + packet_write(p, &o->oSubAction, sizeof(u32)); + packet_write(p, &o->oInteractStatus, sizeof(u32)); + packet_write(p, &o->oHeldState, sizeof(u32)); + packet_write(p, &o->oMoveAngleYaw, sizeof(u32)); + packet_write(p, &o->oTimer, sizeof(u32)); + packet_write(p, &o->activeFlags, sizeof(s16)); + packet_write(p, &o->header.gfx.node.flags, sizeof(s16)); +} + +static void packet_read_object_standard_fields(struct Packet* p, struct Object* o) { + struct SyncObject* so = &syncObjects[o->oSyncID]; + if (so->fullObjectSync) { return; } + if (so->maxSyncDistance == SYNC_DISTANCE_ONLY_DEATH) { return; } + if (so->maxSyncDistance == SYNC_DISTANCE_ONLY_EVENTS) { return; } + + // read the standard fields + packet_read(p, &o->oPosX, sizeof(u32) * 7); + packet_read(p, &o->oAction, sizeof(u32)); + packet_read(p, &o->oSubAction, sizeof(u32)); + packet_read(p, &o->oInteractStatus, sizeof(u32)); + packet_read(p, &o->oHeldState, sizeof(u32)); + packet_read(p, &o->oMoveAngleYaw, sizeof(u32)); + packet_read(p, &o->oTimer, sizeof(u32)); + packet_read(p, &o->activeFlags, sizeof(u16)); + packet_read(p, &o->header.gfx.node.flags, sizeof(s16)); +} + +// ----- extra fields ----- // + +static void packet_write_object_extra_fields(struct Packet* p, struct Object* o) { + struct SyncObject* so = &syncObjects[o->oSyncID]; + if (so->fullObjectSync) { return; } + if (so->maxSyncDistance == SYNC_DISTANCE_ONLY_DEATH) { return; } + + // write the count + packet_write(p, &so->extraFieldCount, sizeof(u8)); + + // write the extra field + for (int i = 0; i < so->extraFieldCount; i++) { + assert(so->extraFields[i] != NULL); + packet_write(p, so->extraFields[i], sizeof(u32)); + } +} + +static void packet_read_object_extra_fields(struct Packet* p, struct Object* o) { + struct SyncObject* so = &syncObjects[o->oSyncID]; + if (so->fullObjectSync) { return; } + if (so->maxSyncDistance == SYNC_DISTANCE_ONLY_DEATH) { return; } + + // read the count and sanity check + u8 extraFieldsCount = 0; + packet_read(p, &extraFieldsCount, sizeof(u8)); + assert(extraFieldsCount == so->extraFieldCount); + + // read the extra fields + for (int i = 0; i < extraFieldsCount; i++) { + assert(so->extraFields[i] != NULL); + packet_read(p, so->extraFields[i], sizeof(u32)); + } +} + +// ----- only death ----- // + +static void packet_write_object_only_death(struct Packet* p, struct Object* o) { + struct SyncObject* so = &syncObjects[o->oSyncID]; + if (so->maxSyncDistance != SYNC_DISTANCE_ONLY_DEATH) { return; } + packet_write(p, &o->activeFlags, sizeof(s16)); +} + +static void packet_read_object_only_death(struct Packet* p, struct Object* o) { + struct SyncObject* so = &syncObjects[o->oSyncID]; + if (so->maxSyncDistance != SYNC_DISTANCE_ONLY_DEATH) { return; } + s16 activeFlags; + packet_read(p, &activeFlags, sizeof(u16)); + if (activeFlags == ACTIVE_FLAG_DEACTIVATED) { + // flag the object as dead, the behavior is responsible for clean up + so->o->oSyncDeath = 1; + forget_sync_object(so); + } +} + +// ----- main send/receive ----- // + +void network_send_object(struct Object* o) { + // sanity check SyncObject + struct SyncObject* so = &syncObjects[o->oSyncID]; + if (so == NULL) { return; } + if (o->behavior != so->behavior) { + printf("network_send_object() BEHAVIOR MISMATCH!\n"); forget_sync_object(so); return; } - // sync only death - if (so->maxSyncDistance == SYNC_DISTANCE_ONLY_DEATH) { - s16 activeFlags; - packet_read(p, &activeFlags, sizeof(u16)); - if (activeFlags == ACTIVE_FLAG_DEACTIVATED) { - so->o->oSyncDeath = 1; - forget_sync_object(so); - } - return; + // always send a new event ID + so->onEventId++; + so->clockSinceUpdate = clock(); + + // reliable packets when sending a dead object or event + bool reliable = (o->activeFlags == ACTIVE_FLAG_DEACTIVATED || so->maxSyncDistance == SYNC_DISTANCE_ONLY_EVENTS); + + // write the packet data + struct Packet p; + packet_init(&p, PACKET_OBJECT, reliable); + packet_write_object_header(&p, o); + packet_write_object_full_sync(&p, o); + packet_write_object_standard_fields(&p, o); + packet_write_object_extra_fields(&p, o); + packet_write_object_only_death(&p, o); + + // check for object death + if (o->activeFlags == ACTIVE_FLAG_DEACTIVATED) { + forget_sync_object(so); } - if (gMarioStates[0].heldObj == o) { - return; - } + // send the packet out + network_send(&p); +} - // write object flags - if (so->maxSyncDistance != SYNC_DISTANCE_ONLY_EVENTS) { - packet_read(p, &o->activeFlags, sizeof(u16)); - packet_read(p, &o->header.gfx.node.flags, sizeof(s16)); - } +void network_receive_object(struct Packet* p) { + // read the header and sanity check the packet + struct SyncObject* so = packet_read_object_header(p); + if (so == NULL) { return; } + struct Object* o = so->o; - if (so->fullObjectSync) { - packet_read(p, o->rawData.asU32, sizeof(u32) * 80); - } else { + // make sure no one can update an object we're holding + if (gMarioStates[0].heldObj == o) { return; } - if (so->maxSyncDistance != SYNC_DISTANCE_ONLY_EVENTS) { - packet_read(p, &o->oPosX, sizeof(u32) * 7); - packet_read(p, &o->oAction, sizeof(u32)); - packet_read(p, &o->oSubAction, sizeof(u32)); - packet_read(p, &o->oInteractStatus, sizeof(u32)); - packet_read(p, &o->oHeldState, sizeof(u32)); - packet_read(p, &o->oMoveAngleYaw, sizeof(u32)); - packet_read(p, &o->oTimer, sizeof(u32)); - } - - // write extra fields - u8 extraFields = 0; - packet_read(p, &extraFields, sizeof(u8)); - assert(extraFields == so->extraFieldCount); - for (int i = 0; i < extraFields; i++) { - assert(so->extraFields[i] != NULL); - packet_read(p, so->extraFields[i], sizeof(u32)); - } - - } + // read the rest of the packet data + packet_read_object_full_sync(p, o); + packet_read_object_standard_fields(p, o); + packet_read_object_extra_fields(p, o); + packet_read_object_only_death(p, o); // deactivated if (o->activeFlags == ACTIVE_FLAG_DEACTIVATED) { @@ -199,17 +302,6 @@ void network_receive_object(struct Packet* p) { } } -float player_distance(struct MarioState* marioState, struct Object* o) { - if (marioState->marioObj == NULL) { return 0; } - f32 mx = marioState->marioObj->header.gfx.pos[0] - o->oPosX; - f32 my = marioState->marioObj->header.gfx.pos[1] - o->oPosY; - f32 mz = marioState->marioObj->header.gfx.pos[2] - o->oPosZ; - mx *= mx; - my *= my; - mz *= mz; - return sqrt(mx + my + mz); -} - bool should_own_object(struct SyncObject* so) { if (so->o->oHeldState == HELD_HELD && so->o->heldByPlayerIndex == 0) { return true; } if (player_distance(&gMarioStates[0], so->o) > player_distance(&gMarioStates[1], so->o)) { return false; } @@ -238,24 +330,28 @@ void network_update_objects(void) { so->owned = should_own_object(so); if (!so->owned) { continue; } - // check update rate + // check for 'only death' event if (so->maxSyncDistance == SYNC_DISTANCE_ONLY_DEATH) { if (so->o->activeFlags != ACTIVE_FLAG_DEACTIVATED) { continue; } network_send_object(syncObjects[i].o); continue; } + // calculate the update rate float dist = player_distance(&gMarioStates[0], so->o); if (so->maxSyncDistance != SYNC_DISTANCE_INFINITE && dist > so->maxSyncDistance) { continue; } float updateRate = dist / 1000.0f; if (gMarioStates[0].heldObj == so->o) { updateRate = 0; } + // set max and min update rate if (so->maxUpdateRate > 0 && updateRate < so->maxUpdateRate) { updateRate = so->maxUpdateRate; } if (updateRate < 0.33f) { updateRate = 0.33f; } + // see if we should update float timeSinceUpdate = ((float)clock() - (float)so->clockSinceUpdate) / (float)CLOCKS_PER_SEC; if (timeSinceUpdate < updateRate) { continue; } + // update! network_send_object(syncObjects[i].o); } diff --git a/src/pc/network/packets/packet_reliable.c b/src/pc/network/packets/packet_reliable.c index 893bc4bdc..b80a4f77c 100644 --- a/src/pc/network/packets/packet_reliable.c +++ b/src/pc/network/packets/packet_reliable.c @@ -37,14 +37,14 @@ void network_send_ack(struct Packet* p) { // send back the ACK struct Packet ack = { 0 }; packet_init(&ack, PACKET_ACK, false); - packet_write(&ack, &seqId, 2); + packet_write(&ack, &seqId, sizeof(u16)); network_send(&ack); } void network_receive_ack(struct Packet* p) { // grab seq num u16 seqId = 0; - packet_read(p, &seqId, 2); + packet_read(p, &seqId, sizeof(u16)); // find in list and remove struct PacketLinkedList* node = head; @@ -94,4 +94,4 @@ void network_update_reliable(void) { } node = node->next; } -} \ No newline at end of file +} diff --git a/src/pc/network/packets/packet_spawn_objects.c b/src/pc/network/packets/packet_spawn_objects.c index bcd00e225..dd880d263 100644 --- a/src/pc/network/packets/packet_spawn_objects.c +++ b/src/pc/network/packets/packet_spawn_objects.c @@ -72,7 +72,7 @@ void network_receive_spawn_objects(struct Packet* p) { packet_read(p, &remoteSpawnId, sizeof(u8)); packet_read(p, &objectCount, sizeof(u8)); - // check if remote coin id has already been seen + // check if remote spawn id has already been seen for (int i = 0; i < MAX_REMOTE_SPAWN_IDS; i++) { if (remoteSpawnIds[i] == remoteSpawnId) { // we already saw this event!