mirror of
				https://github.com/coop-deluxe/sm64coopdx.git
				synced 2025-10-30 08:01:01 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			353 lines
		
	
	
		
			No EOL
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			353 lines
		
	
	
		
			No EOL
		
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
#include <stdio.h>
 | 
						|
#include "../network.h"
 | 
						|
#include "menu/custom_menu_system.h"
 | 
						|
#include "game/interaction.h"
 | 
						|
#include "game/object_list_processor.h"
 | 
						|
#include "object_constants.h"
 | 
						|
#include "object_fields.h"
 | 
						|
#include "game/object_helpers.h"
 | 
						|
#include "behavior_table.h"
 | 
						|
#include "model_ids.h"
 | 
						|
//#define DISABLE_MODULE_LOG 1
 | 
						|
#include "pc/debuglog.h"
 | 
						|
 | 
						|
extern s16 gCurrCourseNum, gCurrActNum, gCurrLevelNum, gCurrAreaIndex;
 | 
						|
 | 
						|
#define MAX_STATIC_SPAWN_REMOVAL 256
 | 
						|
u8 sStaticSpawnRemoval[MAX_STATIC_SPAWN_REMOVAL] = { 0 };
 | 
						|
u8 sStaticSpawnRemovalIndex = 0;
 | 
						|
 | 
						|
#define MAX_COIN_COLLECTION 128
 | 
						|
u8 sCoinCollection[MAX_STATIC_SPAWN_REMOVAL] = { 0 };
 | 
						|
u8 sCoinCollectionIndex = 0;
 | 
						|
 | 
						|
void coin_collection_remember(u8 coinId) {
 | 
						|
    sCoinCollection[sCoinCollectionIndex++] = coinId;
 | 
						|
    if (sStaticSpawnRemovalIndex >= MAX_COIN_COLLECTION) { sStaticSpawnRemovalIndex = MAX_COIN_COLLECTION - 1; }
 | 
						|
}
 | 
						|
 | 
						|
void coin_collection_clear(void) {
 | 
						|
    sCoinCollectionIndex = 0;
 | 
						|
}
 | 
						|
 | 
						|
void static_spawn_removal_remember(u8 syncId) {
 | 
						|
    sStaticSpawnRemoval[sStaticSpawnRemovalIndex++] = syncId;
 | 
						|
    if (sStaticSpawnRemovalIndex == 0) { sStaticSpawnRemovalIndex = MAX_STATIC_SPAWN_REMOVAL - 1; }
 | 
						|
}
 | 
						|
 | 
						|
void static_spawn_removal_clear(void) {
 | 
						|
    sStaticSpawnRemovalIndex = 0;
 | 
						|
}
 | 
						|
 | 
						|
struct NetworkPlayer* get_network_player_from_valid_location(s16 courseNum, s16 actNum, s16 levelNum, s16 areaIndex) {
 | 
						|
    for (int i = 0; i < MAX_PLAYERS; i++) {
 | 
						|
        struct NetworkPlayer* np = &gNetworkPlayers[i];
 | 
						|
        if (!np->connected)                 { continue; }
 | 
						|
        if (!np->currAreaSyncValid)         { continue; }
 | 
						|
        if (np->currCourseNum != courseNum) { continue; }
 | 
						|
        if (np->currActNum    != actNum)    { continue; }
 | 
						|
        if (np->currLevelNum  != levelNum)  { continue; }
 | 
						|
        if (np->currAreaIndex != areaIndex) { continue; }
 | 
						|
        return np;
 | 
						|
    }
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
void network_send_location_request(void) {
 | 
						|
    if (gNetworkType == NT_SERVER) {
 | 
						|
        struct NetworkPlayer* np = get_network_player_from_valid_location(gCurrCourseNum, gCurrActNum, gCurrLevelNum, gCurrAreaIndex);
 | 
						|
        if (np == NULL) {
 | 
						|
            gNetworkPlayerLocal->currAreaSyncValid = true;
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        network_send_client_location_request(gNetworkPlayerLocal->globalIndex, np->globalIndex);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    struct Packet p;
 | 
						|
    packet_init(&p, PACKET_LOCATION_REQUEST, 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));
 | 
						|
    network_send_to(0, &p);
 | 
						|
}
 | 
						|
 | 
						|
void network_receive_location_request(struct Packet* p) {
 | 
						|
    if (gNetworkType != NT_SERVER) {
 | 
						|
        LOG_ERROR("non-server is receiving a location request!");
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    struct NetworkPlayer* np = &gNetworkPlayers[p->localIndex];
 | 
						|
    if (np == NULL || np->localIndex == UNKNOWN_LOCAL_INDEX || !np->connected) {
 | 
						|
        LOG_ERROR("Receiving location request from inactive player!");
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    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));
 | 
						|
 | 
						|
    np->currCourseNum     = courseNum;
 | 
						|
    np->currActNum        = actNum;
 | 
						|
    np->currLevelNum      = levelNum;
 | 
						|
    np->currAreaIndex     = areaIndex;
 | 
						|
    np->currAreaSyncValid = false;
 | 
						|
 | 
						|
    struct NetworkPlayer* np2 = get_network_player_from_valid_location(courseNum, actNum, levelNum, areaIndex);
 | 
						|
    if (np2 == NULL) {
 | 
						|
        network_send_level_area_valid(np->globalIndex);
 | 
						|
    } else if (np2 == gNetworkPlayerLocal) {
 | 
						|
        network_send_location_response(np->globalIndex);
 | 
						|
    } else {
 | 
						|
        network_send_client_location_request(np->globalIndex, np2->globalIndex);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
void network_send_client_location_request(u8 destGlobalIndex, u8 srcGlobalIndex) {
 | 
						|
    if (gNetworkType != NT_SERVER) {
 | 
						|
        LOG_ERROR("client can't send a 'client location request'");
 | 
						|
        return;
 | 
						|
    }
 | 
						|
    
 | 
						|
    struct NetworkPlayer* destNp = network_player_from_global_index(destGlobalIndex);
 | 
						|
    if (destNp == NULL || !destNp->connected) {
 | 
						|
        LOG_ERROR("network_send_client_location_request: dest np is invalid (global %d)", destGlobalIndex);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    struct Packet p;
 | 
						|
    packet_init(&p, PACKET_CLIENT_LOCATION_REQUEST, true, false);
 | 
						|
    packet_write(&p, &destGlobalIndex,       sizeof(u8));
 | 
						|
    packet_write(&p, &destNp->currCourseNum, sizeof(s16));
 | 
						|
    packet_write(&p, &destNp->currActNum,    sizeof(s16));
 | 
						|
    packet_write(&p, &destNp->currLevelNum,  sizeof(s16));
 | 
						|
    packet_write(&p, &destNp->currAreaIndex, sizeof(s16));
 | 
						|
 | 
						|
    struct NetworkPlayer* srcNp = network_player_from_global_index(srcGlobalIndex);
 | 
						|
    if (srcNp == NULL || !srcNp->connected || !srcNp->currAreaSyncValid) {
 | 
						|
        LOG_ERROR("network_send_client_location_request: source np is invalid (global %d)", srcGlobalIndex);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    network_send_to(srcNp->localIndex, &p);
 | 
						|
}
 | 
						|
 | 
						|
void network_receive_client_location_request(struct Packet* p) {
 | 
						|
    if (gNetworkType == NT_SERVER) {
 | 
						|
        LOG_ERROR("server is receiving a 'client location request'!");
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    u8 destGlobalIndex;
 | 
						|
    s16 courseNum, actNum, levelNum, areaIndex;
 | 
						|
    packet_read(p, &destGlobalIndex, sizeof(u8));
 | 
						|
    packet_read(p, &courseNum,       sizeof(s16));
 | 
						|
    packet_read(p, &actNum,          sizeof(s16));
 | 
						|
    packet_read(p, &levelNum,        sizeof(s16));
 | 
						|
    packet_read(p, &areaIndex,       sizeof(s16));
 | 
						|
 | 
						|
    if (courseNum != gCurrCourseNum || actNum != gCurrActNum || levelNum != gCurrLevelNum || areaIndex != gCurrAreaIndex) {
 | 
						|
        LOG_ERROR("Receiving 'client location request' with the wrong location!");
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    network_send_location_response(destGlobalIndex);
 | 
						|
}
 | 
						|
 | 
						|
///////////////////////////////////////////////////////////////////////////////
 | 
						|
 | 
						|
void network_send_location_response(u8 destGlobalIndex) {
 | 
						|
    if (!gNetworkPlayerLocal->currAreaSyncValid) {
 | 
						|
        LOG_ERROR("my area is invalid");
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    struct NetworkPlayer* destNp = network_player_from_global_index(destGlobalIndex);
 | 
						|
    if (destNp == NULL || !destNp->connected) {
 | 
						|
        LOG_ERROR("network_send_location_response: dest np is invalid");
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    struct Packet p;
 | 
						|
    packet_init(&p, PACKET_LOCATION_RESPONSE, 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));
 | 
						|
 | 
						|
    packet_write(&p, &gMarioStates[0].numCoins, sizeof(s16));
 | 
						|
 | 
						|
    // static spawn removal
 | 
						|
    packet_write(&p, &sStaticSpawnRemovalIndex, sizeof(u8));
 | 
						|
    for (int i = 0; i < sStaticSpawnRemovalIndex; i++) {
 | 
						|
        packet_write(&p, &sStaticSpawnRemoval[i], sizeof(u8));
 | 
						|
    }
 | 
						|
 | 
						|
    // coin collection
 | 
						|
    packet_write(&p, &sCoinCollectionIndex, sizeof(u8));
 | 
						|
    for (int i = 0; i < sCoinCollectionIndex; i++) {
 | 
						|
        packet_write(&p, &sCoinCollection[i], sizeof(u8));
 | 
						|
    }
 | 
						|
 | 
						|
    // respawners
 | 
						|
    u8 respawnerCount = 0;
 | 
						|
    for (int i = 0; i < MAX_SYNC_OBJECTS; i++) {
 | 
						|
        struct SyncObject* so = &gSyncObjects[i];
 | 
						|
        if (so == NULL || so->o == NULL || so->behavior != bhvRespawner) { continue; }
 | 
						|
        respawnerCount++;
 | 
						|
    }
 | 
						|
 | 
						|
    packet_write(&p, &respawnerCount, sizeof(u8));
 | 
						|
    for (int i = 0; i < MAX_SYNC_OBJECTS; i++) {
 | 
						|
        struct SyncObject* so = &gSyncObjects[i];
 | 
						|
        if (so == NULL || so->o == NULL || so->behavior != bhvRespawner) { continue; }
 | 
						|
        u32 behaviorToRespawn = get_id_from_behavior(so->o->oRespawnerBehaviorToRespawn);
 | 
						|
        packet_write(&p, &so->o->oPosX, sizeof(f32));
 | 
						|
        packet_write(&p, &so->o->oPosY, sizeof(f32));
 | 
						|
        packet_write(&p, &so->o->oPosZ, sizeof(f32));
 | 
						|
        packet_write(&p, &so->o->oBehParams, sizeof(s32));
 | 
						|
        packet_write(&p, &so->o->oRespawnerModelToRespawn, sizeof(s32));
 | 
						|
        packet_write(&p, &so->o->oRespawnerMinSpawnDist, sizeof(f32));
 | 
						|
        packet_write(&p, &behaviorToRespawn, sizeof(s32));
 | 
						|
        packet_write(&p, &so->o->oSyncID, sizeof(u32));
 | 
						|
    }
 | 
						|
 | 
						|
    network_send_to(destNp->localIndex, &p);
 | 
						|
 | 
						|
    // send non-static objects
 | 
						|
    for (int i = 0; i < MAX_SYNC_OBJECTS; i++) {
 | 
						|
        struct SyncObject* so = &gSyncObjects[i];
 | 
						|
        if (so == NULL || so->o == NULL || so->o->oSyncID != i) { continue; }
 | 
						|
        if (so->staticLevelSpawn) { continue; }
 | 
						|
        if (so->o->behavior == bhvRespawner) { continue; }
 | 
						|
        struct Object* spawn_objects[] = { so->o };
 | 
						|
 | 
						|
        // TODO: move find model to a utility file/function
 | 
						|
        // find model
 | 
						|
        u32 model = 0;
 | 
						|
        for (int j = 0; j < 256; j++) {
 | 
						|
            if (so->o->header.gfx.sharedChild == gLoadedGraphNodes[j]) {
 | 
						|
                model = j;
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        u32 models[] = { model };
 | 
						|
        network_send_spawn_objects_to(destNp->localIndex, spawn_objects, models, 1);
 | 
						|
    }
 | 
						|
 | 
						|
    // send last reliable ent packet
 | 
						|
    for (int i = 0; i < MAX_SYNC_OBJECTS; i++) {
 | 
						|
        struct SyncObject* so = &gSyncObjects[i];
 | 
						|
        if (so == NULL || so->o == NULL) { continue; }
 | 
						|
        struct Packet* entPacket = get_last_sync_ent_reliable_packet(i);
 | 
						|
        if (entPacket->error) { continue; }
 | 
						|
        struct Packet p2 = { 0 };
 | 
						|
        packet_duplicate(entPacket, &p2);
 | 
						|
        network_send_to(destNp->localIndex, &p2);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void network_receive_location_response(struct Packet* p) {
 | 
						|
    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));
 | 
						|
 | 
						|
    if (courseNum != gCurrCourseNum || actNum != gCurrActNum || levelNum != gCurrLevelNum || areaIndex != gCurrAreaIndex) {
 | 
						|
        LOG_ERROR("Receiving 'location response' with the wrong location!");
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (gNetworkPlayerLocal->currAreaSyncValid) {
 | 
						|
        LOG_ERROR("Receiving 'location response' when our location is already valid!");
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    s16 numCoins;
 | 
						|
    packet_read(p, &numCoins, sizeof(s16));
 | 
						|
 | 
						|
    u8 staticSpawnRemovals;
 | 
						|
    static_spawn_removal_clear();
 | 
						|
 | 
						|
    // read static spawn removals
 | 
						|
    packet_read(p, &staticSpawnRemovals, sizeof(u8));
 | 
						|
    for (int i = 0; i < staticSpawnRemovals; i++) {
 | 
						|
        u8 syncId;
 | 
						|
        packet_read(p, &syncId, sizeof(u8));
 | 
						|
        struct SyncObject* so = &gSyncObjects[syncId];
 | 
						|
        if (so != NULL) {
 | 
						|
            if (so->o != NULL) {
 | 
						|
                obj_mark_for_deletion(so->o);
 | 
						|
            }
 | 
						|
            network_forget_sync_object(so);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // read coin collections
 | 
						|
    packet_read(p, &sCoinCollectionIndex, sizeof(u8));
 | 
						|
    for (int i = 0; i < sCoinCollectionIndex; i++) {
 | 
						|
        packet_read(p, &sCoinCollection[i], sizeof(u8));
 | 
						|
    }
 | 
						|
 | 
						|
    // collect the coins
 | 
						|
    for (int i = 0; i < OBJECT_POOL_CAPACITY; i++) {
 | 
						|
        struct Object* o = &gObjectPool[i];
 | 
						|
        if (o->activeFlags & ACTIVE_FLAG_DEACTIVATED) { continue; }
 | 
						|
        if (!is_behavior_a_coin(o->behavior))         { continue; }
 | 
						|
        for (int j = 0; j < sCoinCollectionIndex; j++) {
 | 
						|
            if (o->oCoinID == sCoinCollection[j]) {
 | 
						|
                o->oInteractStatus = INT_STATUS_INTERACTED;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // read respawners
 | 
						|
    u8 respawnerCount = 0;
 | 
						|
    packet_read(p, &respawnerCount, sizeof(u8));
 | 
						|
 | 
						|
    for (int i = 0; i < respawnerCount; i++) {
 | 
						|
        f32 posX, posY, posZ;
 | 
						|
        packet_read(p, &posX, sizeof(f32));
 | 
						|
        packet_read(p, &posY, sizeof(f32));
 | 
						|
        packet_read(p, &posZ, sizeof(f32));
 | 
						|
 | 
						|
        s32 behParams, respawnerModelToRespawn;
 | 
						|
        packet_read(p, &behParams, sizeof(s32));
 | 
						|
        packet_read(p, &respawnerModelToRespawn, sizeof(s32));
 | 
						|
 | 
						|
        f32 respawnerMinSpawnDist;
 | 
						|
        packet_read(p, &respawnerMinSpawnDist, sizeof(f32));
 | 
						|
 | 
						|
        u32 behaviorToRespawn, syncId;
 | 
						|
        packet_read(p, &behaviorToRespawn, sizeof(u32));
 | 
						|
        packet_read(p, &syncId, sizeof(u32));
 | 
						|
 | 
						|
        struct Object* respawner = spawn_object_abs_with_rot(gMarioStates[0].marioObj, 0, MODEL_NONE, bhvRespawner, posX, posY, posZ, 0, 0, 0);
 | 
						|
        respawner->parentObj = respawner;
 | 
						|
        respawner->oBehParams = behParams;
 | 
						|
        respawner->oRespawnerModelToRespawn = respawnerModelToRespawn;
 | 
						|
        respawner->oRespawnerMinSpawnDist = respawnerMinSpawnDist;
 | 
						|
        respawner->oRespawnerBehaviorToRespawn = get_behavior_from_id(behaviorToRespawn);
 | 
						|
        respawner->oSyncID = syncId;
 | 
						|
 | 
						|
        network_forget_sync_object(&gSyncObjects[syncId]);
 | 
						|
        network_init_object(respawner, SYNC_DISTANCE_ONLY_EVENTS);
 | 
						|
    }
 | 
						|
 | 
						|
    gMarioStates[0].numCoins = numCoins;
 | 
						|
    gNetworkPlayerLocal->currAreaSyncValid = true;
 | 
						|
 | 
						|
    if (gNetworkType != NT_SERVER) {
 | 
						|
        network_send_level_area_valid(0);
 | 
						|
    }
 | 
						|
} |