mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2024-11-25 13:35:12 +00:00
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:
parent
6bfdcbcb7b
commit
b6959dc7ea
7 changed files with 236 additions and 6 deletions
|
@ -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 &
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
214
src/pc/network/packets/packet_level_respawn_info.c
Normal file
214
src/pc/network/packets/packet_level_respawn_info.c
Normal 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
|
||||
? ¯oOffset
|
||||
: &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!)");
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue