Informed players of ent deletions in a different area (but same level)

Added packet_level_respawn_info to inform the players of entity
deletions in a different area of the same level. Currently it's
massively noisy due to sending out a new packet for each entity
destroyed. This can cause chaos when collecting a series of coins.

Ideally this information would be batched and sent in one big blob every
so often.
This commit is contained in:
MysterD 2021-06-11 16:19:24 -07:00
parent 6bfdcbcb7b
commit b6959dc7ea
7 changed files with 236 additions and 6 deletions

View File

@ -18,10 +18,10 @@ fi
#exit
# no debug, direct
#$FILE --server 27015 --configfile sm64config_server.txt &
#sleep 7
#$FILE --client 127.0.0.1 27015 --configfile sm64config_client.txt &
#exit
$FILE --server 27015 --configfile sm64config_server.txt &
sleep 7
$FILE --client 127.0.0.1 27015 --configfile sm64config_client.txt &
exit
# debug on server
#$FILE --client 127.0.0.1 27015 --configfile sm64config_client.txt &

View File

@ -424,18 +424,28 @@ s32 unload_deactivated_objects_in_list(struct ObjectNode *objList) {
void set_object_respawn_info_bits(struct Object *obj, u8 bits) {
u32 *info32;
u16 *info16;
u8 oldRespawnInfoBits = 0;
u8 newRespawnInfoBits = 0;
switch (obj->respawnInfoType) {
case RESPAWN_INFO_TYPE_32:
info32 = (u32 *) obj->respawnInfo;
oldRespawnInfoBits = (u8)(*info32 >> 8);
*info32 |= bits << 8;
newRespawnInfoBits = (u8)(*info32 >> 8);
break;
case RESPAWN_INFO_TYPE_16:
info16 = (u16 *) obj->respawnInfo;
oldRespawnInfoBits = (u8)(*info16 >> 8);
*info16 |= bits << 8;
newRespawnInfoBits = (u8)(*info16 >> 8);
break;
}
if (newRespawnInfoBits != oldRespawnInfoBits) {
network_send_level_respawn_info(obj, newRespawnInfoBits);
}
}
/**

View File

@ -72,6 +72,7 @@ void packet_receive(struct Packet* p) {
case PACKET_LEVEL_SPAWN_INFO: network_receive_level_spawn_info(p); break;
case PACKET_LEVEL_MACRO: network_receive_level_macro(p); break;
case PACKET_LEVEL_AREA_INFORM: network_receive_level_area_inform(p); break;
case PACKET_LEVEL_RESPAWN_INFO: network_receive_level_respawn_info(p); break;
// custom
case PACKET_CUSTOM: network_receive_custom(p); break;

View File

@ -45,6 +45,7 @@ enum PacketType {
PACKET_LEVEL_SPAWN_INFO,
PACKET_LEVEL_MACRO,
PACKET_LEVEL_AREA_INFORM,
PACKET_LEVEL_RESPAWN_INFO,
///
PACKET_CUSTOM = 255,
@ -224,4 +225,8 @@ void network_receive_level_macro(struct Packet* p);
void network_send_level_area_inform(struct NetworkPlayer* np);
void network_receive_level_area_inform(struct Packet* p);
// packet_level_respawn_info.c
void network_send_level_respawn_info(struct Object* o, u8 respawnInfoBits);
void network_receive_level_respawn_info(struct Packet* p);
#endif

View File

@ -14,12 +14,12 @@
#define DISABLE_MODULE_LOG 1
#include "pc/debuglog.h"
// TODO: move to common utility location
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;
}

View File

@ -0,0 +1,214 @@
#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 "object_constants.h"
#include "object_fields.h"
#include "behavior_table.h"
#include "model_ids.h"
//#define DISABLE_MODULE_LOG 1
#include "pc/debuglog.h"
#define ERR_COULD_NOT_FIND_OBJECT ((u16)-1)
// TODO: These respawn info changes REALLY need to be batched and sent on a timer instead of immediately.
// currently when collecting coins a flood of packets gets sent out when there is no immediate need
// to know when a coin is collected in a different area.
// Ideally this logic would be combined into packet_level_macro and packet_level_spawn_info without
// being bolted on top like this.
static s16* get_respawn_info_from_macro_offset(u16 areaIndex, u16 macroOffset) {
// loop through macro objects for santiziation
u16 maxOffset = 0;
s16* macroObjList = gAreaData[areaIndex].macroObjects;
while (macroObjList != NULL && *macroObjList != -1) {
macroObjList += 4;
s16* respawnInfo = macroObjList++;
maxOffset = respawnInfo - gAreaData[areaIndex].macroObjects;
}
// sanitize array
if (macroOffset > maxOffset) { return NULL; }
return gAreaData[areaIndex].macroObjects + macroOffset;
}
static u32* get_respawn_info_from_spawn_info_index(u16 areaIndex, u16 fromSpawnInfoIndex) {
struct SpawnInfo* spawnInfo = gAreaData[areaIndex].objectSpawnInfos;
u16 spawnInfoIndex = 0;
while (spawnInfo != NULL) {
if (spawnInfoIndex == fromSpawnInfoIndex) {
return &spawnInfo->behaviorArg;
}
spawnInfo = spawnInfo->next;
spawnInfoIndex++;
}
return NULL;
}
static u16 get_macro_offset_of_object(struct Object* o) {
// loop through macro objects to find object
s16* macroObjList = gCurrentArea->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 (o->respawnInfo == respawnInfo) {
return (respawnInfo - gCurrentArea->macroObjects);
}
}
return ERR_COULD_NOT_FIND_OBJECT;
}
static u16 get_spawn_info_index_of_object(struct Object* o) {
// loop through spawn infos to find object
struct SpawnInfo* spawnInfo = gCurrentArea->objectSpawnInfos;
u16 spawnInfoIndex = 0;
while (spawnInfo != NULL) {
// if a spawn info object was destroyed, send its spawn info index
if (&spawnInfo->behaviorArg == o->respawnInfo) {
return spawnInfoIndex;
}
spawnInfo = spawnInfo->next;
spawnInfoIndex++;
}
return ERR_COULD_NOT_FIND_OBJECT;
}
////
void network_send_level_respawn_info(struct Object* o, u8 respawnInfoBits) {
// make sure our area is valid
if (!gNetworkPlayerLocal->currAreaSyncValid) {
LOG_ERROR("my area is invalid");
return;
}
// make sure we can find the object
u16 macroOffset = get_macro_offset_of_object(o);
u16 spawnInfoIndex = get_spawn_info_index_of_object(o);
if (macroOffset == ERR_COULD_NOT_FIND_OBJECT && spawnInfoIndex == ERR_COULD_NOT_FIND_OBJECT) {
LOG_INFO("could not find object in macro or spawn info");
return;
}
bool isMacroObject = (macroOffset != ERR_COULD_NOT_FIND_OBJECT);
// write header
struct Packet p;
packet_init(&p, PACKET_LEVEL_RESPAWN_INFO, 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 object info
packet_write(&p, &isMacroObject, sizeof(u8));
packet_write(&p, isMacroObject
? &macroOffset
: &spawnInfoIndex, sizeof(u16));
packet_write(&p, &respawnInfoBits, sizeof(u8));
// send the packet
if (gNetworkType == NT_SERVER) {
// broadcast
for (int i = 0; i < MAX_PLAYERS; i++) {
struct NetworkPlayer* np = &gNetworkPlayers[i];
if (!np->connected) { continue; }
if (!np->currLevelSyncValid) { continue; }
if (np->currCourseNum != gCurrCourseNum) { continue; }
if (np->currActNum != gCurrActNum) { continue; }
if (np->currLevelNum != gCurrLevelNum) { continue; }
struct Packet p2;
packet_duplicate(&p, &p2);
network_send_to(np->localIndex, &p2);
}
} else {
network_send_to(gNetworkPlayerServer->localIndex, &p);
}
LOG_INFO("tx level respawn info");
}
void network_receive_level_respawn_info(struct Packet* p) {
LOG_INFO("rx level respawn info");
// read header
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));
// read object info
bool isMacroObject;
u16 offsetOrIndex;
u8 respawnInfoBits;
packet_read(p, &isMacroObject, sizeof(u8));
packet_read(p, &offsetOrIndex, sizeof(u16));
packet_read(p, &respawnInfoBits, sizeof(u8));
bool levelMismatch = (courseNum != gCurrCourseNum || actNum != gCurrActNum || levelNum != gCurrLevelNum);
if (gNetworkType == NT_SERVER) {
// ensure we got the info from a valid player
struct NetworkPlayer* npFrom = &gNetworkPlayers[p->localIndex];
if (npFrom == NULL || npFrom->localIndex == UNKNOWN_LOCAL_INDEX || !npFrom->connected) {
LOG_ERROR("Receiving 'level respawn info' from inactive player!");
return;
}
// broadcast this change to the other players in that level
for (int i = 0; i < MAX_PLAYERS; i++) {
struct NetworkPlayer* np = &gNetworkPlayers[i];
if (!np->connected) { continue; }
if (!np->currLevelSyncValid) { continue; }
if (np->currCourseNum != courseNum) { continue; }
if (np->currActNum != actNum) { continue; }
if (np->currLevelNum != levelNum) { continue; }
if (np == npFrom) { continue; }
struct Packet p2;
packet_duplicate(p, &p2);
network_send_to(np->localIndex, &p2);
}
// do not have the server apply the changes unless their level matches
if (levelMismatch) { return; }
} else if (levelMismatch) {
LOG_ERROR("Receiving 'level respawn info' with the wrong location!");
return;
}
if (isMacroObject) {
s16* respawnInfo = get_respawn_info_from_macro_offset(areaIndex, offsetOrIndex);
if (respawnInfo == NULL) {
LOG_ERROR("Could not find respawn info from macro offset");
return;
}
// apply the change
*respawnInfo |= respawnInfoBits << 8;
} else {
u32* respawnInfo = get_respawn_info_from_spawn_info_index(areaIndex, offsetOrIndex);
if (respawnInfo == NULL) {
LOG_ERROR("Could not find respawn info from spawn info index");
return;
}
// apply the change
*respawnInfo |= respawnInfoBits << 8;
}
LOG_INFO("rx level respawn info (success!)");
}

View File

@ -13,12 +13,12 @@
#define DISABLE_MODULE_LOG 1
#include "pc/debuglog.h"
// TODO: move to common utility location
static struct Object* get_object_matching_respawn_info(u32* respawnInfo) {
for (int i = 0; i < OBJECT_POOL_CAPACITY; i++) {
struct Object* o = &gObjectPool[i];
if (o->respawnInfo == respawnInfo) { return o; }
}
return NULL;
}