mirror of
				https://github.com/coop-deluxe/sm64coopdx.git
				synced 2025-10-30 08:01:01 +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…
	
	Add table
		
		Reference in a new issue