diff --git a/build-windows-visual-studio/sm64ex.vcxproj b/build-windows-visual-studio/sm64ex.vcxproj index 3b2ac0e54..e7ea48955 100644 --- a/build-windows-visual-studio/sm64ex.vcxproj +++ b/build-windows-visual-studio/sm64ex.vcxproj @@ -3859,6 +3859,7 @@ + @@ -3962,31 +3963,41 @@ + + + + + + + - + - - - - + + + - + + + - + + + @@ -4348,6 +4359,7 @@ + diff --git a/build-windows-visual-studio/sm64ex.vcxproj.filters b/build-windows-visual-studio/sm64ex.vcxproj.filters index 5689ffd79..fc59e122d 100644 --- a/build-windows-visual-studio/sm64ex.vcxproj.filters +++ b/build-windows-visual-studio/sm64ex.vcxproj.filters @@ -3436,6 +3436,15 @@ {451cd85d-8a2c-4aa5-87c7-d1415974ba96} + + {8503d4ed-8d6c-4888-a861-c50644924389} + + + {2ffc6d25-bc31-400f-b587-58a915c565e1} + + + {9ddfaa87-399e-4f61-aae3-f91af79e14cc} + @@ -14964,12 +14973,6 @@ Source Files\src\pc\network\packets - - Source Files\src\pc\network\packets - - - Source Files\src\pc\network\packets - Source Files\src\pc\network\packets @@ -15006,9 +15009,6 @@ Source Files - - Source Files\src\pc\network\packets - Source Files\src\pc\network\packets @@ -15051,9 +15051,6 @@ Source Files\src\pc\network\packets - - Source Files\src\pc\network\packets - Source Files\src\pc\network\packets @@ -15081,6 +15078,69 @@ Source Files\src\pc\network\packets + + Source Files\src\game + + + Source Files\src\pc\network\packets\location + + + Source Files\src\pc\network\packets\location + + + Source Files\src\pc\network\packets\location + + + Source Files\src\pc\network\packets\location + + + Source Files\src\pc\network\packets\location + + + Source Files\src\pc\network\packets\location + + + Source Files\src\pc\network\packets\location + + + Source Files\src\pc\network\packets\location + + + Source Files\src\pc\network\packets\location + + + Source Files\src\pc\network\packets\location + + + Source Files\src\pc\network\packets\location + + + Source Files\src\pc\network\packets\location + + + Source Files\src\pc\network\packets\fundamental + + + Source Files\src\pc\network\packets\fundamental + + + Source Files\src\pc\network\packets\fundamental + + + Source Files\src\pc\network\packets\fundamental + + + Source Files\src\pc\network\packets\reservation-area + + + Source Files\src\pc\network\packets\reservation-area + + + Source Files\src\pc\network\packets\reservation-area + + + Source Files\src\pc\network + @@ -16051,5 +16111,8 @@ Header Files\include + + Header Files\src\pc\network + \ No newline at end of file diff --git a/src/pc/controller/controller_keyboard_debug.c b/src/pc/controller/controller_keyboard_debug.c index 573a9bb56..97415e78c 100644 --- a/src/pc/controller/controller_keyboard_debug.c +++ b/src/pc/controller/controller_keyboard_debug.c @@ -5,6 +5,12 @@ #include "game/mario.h" #include "sm64.h" +#include "object_fields.h" +#include "object_constants.h" +#include "src/game/object_helpers.h" +#include "behavior_data.h" +#include "behavior_table.h" + #ifdef DEBUG static u8 warpToLevel = LEVEL_CCM; @@ -107,6 +113,15 @@ static void debug_suicide(void) { gMarioStates[0].hurtCounter = 31; } +static void debug_spawn_object(void) { + struct Object* box = spawn_object(gMarioStates[0].marioObj, MODEL_BREAKABLE_BOX_SMALL, bhvBreakableBoxSmall); + network_set_sync_id(box); + + struct Object* spawn_objects[] = { box }; + u32 models[] = { MODEL_BREAKABLE_BOX_SMALL }; + network_send_spawn_objects(spawn_objects, models, 1); +} + void debug_keyboard_on_key_down(int scancode) { scancode = scancode; switch (scancode & 0xFF) { @@ -114,6 +129,7 @@ void debug_keyboard_on_key_down(int scancode) { #ifdef DEVELOPMENT case SCANCODE_6: debug_warp_level(warpToLevel); break; case SCANCODE_7: debug_warp_area(); break; + case SCANCODE_8: debug_spawn_object(); break; case SCANCODE_9: debug_warp_to(); break; case SCANCODE_0: debug_suicide(); break; #endif diff --git a/src/pc/network/network.c b/src/pc/network/network.c index c7a298d4c..d5015d267 100644 --- a/src/pc/network/network.c +++ b/src/pc/network/network.c @@ -112,11 +112,6 @@ void network_on_loaded_level(void) { network_send_change_level(); } } - - // request my chunk of reserved sync ids - if (gNetworkType == NT_CLIENT) { - network_send_reservation_request(); - } } void network_send_to(u8 localIndex, struct Packet* p) { diff --git a/src/pc/network/network.h b/src/pc/network/network.h index 118ca9b03..bd5c2289e 100644 --- a/src/pc/network/network.h +++ b/src/pc/network/network.h @@ -43,7 +43,6 @@ struct NetworkSystem { struct SyncObject { struct Object* o; - u16 reserved; float maxSyncDistance; bool owned; bool staticLevelSpawn; diff --git a/src/pc/network/network_player.c b/src/pc/network/network_player.c index d87865191..e75919322 100644 --- a/src/pc/network/network_player.c +++ b/src/pc/network/network_player.c @@ -2,6 +2,7 @@ #include "network_player.h" #include "game/chat.h" #include "game/mario_misc.h" +#include "reservation_area.h" #include "pc/debuglog.h" struct NetworkPlayer gNetworkPlayers[MAX_PLAYERS] = { 0 }; @@ -190,6 +191,7 @@ u8 network_player_disconnected(u8 globalIndex) { LOG_INFO("player disconnected, local %d, global %d", i, globalIndex); chat_add_message_ext("player disconnected", CMT_SYSTEM, get_player_color(globalIndex, 0)); packet_ordered_clear(globalIndex); + reservation_area_change(np); return i; } return UNKNOWN_GLOBAL_INDEX; diff --git a/src/pc/network/packets/packet.c b/src/pc/network/packets/packet.c index 97473a23a..a847c900e 100644 --- a/src/pc/network/packets/packet.c +++ b/src/pc/network/packets/packet.c @@ -13,8 +13,8 @@ void packet_process(struct Packet* p) { case PACKET_COLLECT_STAR: network_receive_collect_star(p); break; case PACKET_COLLECT_COIN: network_receive_collect_coin(p); break; case PACKET_COLLECT_ITEM: network_receive_collect_item(p); break; - case PACKET_RESERVATION_REQUEST: network_receive_reservation_request(p); break; - case PACKET_RESERVATION: network_receive_reservation(p); break; + case PACKET_UNUSED1: break; + case PACKET_UNUSED2: break; case PACKET_JOIN_REQUEST: network_receive_join_request(p); break; case PACKET_JOIN: network_receive_join(p); break; case PACKET_CHAT: network_receive_chat(p); break; @@ -25,7 +25,12 @@ void packet_process(struct Packet* p) { case PACKET_NETWORK_PLAYERS: network_receive_network_players(p); break; case PACKET_DEATH: network_receive_death(p); break; - // location + // reservation area + case PACKET_RESERVATION_LIST: network_receive_reservation_list(p); break; + case PACKET_RESERVATION_USE: network_receive_reservation_use(p); break; + case PACKET_RESERVATION_RELEASE: network_receive_reservation_release(p); break; + + // location case PACKET_CHANGE_LEVEL: network_receive_change_level(p); break; case PACKET_CHANGE_AREA: network_receive_change_area(p); break; case PACKET_LEVEL_AREA_REQUEST: network_receive_level_area_request(p); break; @@ -39,7 +44,7 @@ void packet_process(struct Packet* p) { 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 + // custom case PACKET_CUSTOM: network_receive_custom(p); break; default: LOG_ERROR("received unknown packet: %d", p->buffer[0]); } diff --git a/src/pc/network/packets/packet.h b/src/pc/network/packets/packet.h index bcec47864..fcd1a4195 100644 --- a/src/pc/network/packets/packet.h +++ b/src/pc/network/packets/packet.h @@ -22,8 +22,8 @@ enum PacketType { PACKET_COLLECT_STAR, PACKET_COLLECT_COIN, PACKET_COLLECT_ITEM, - PACKET_RESERVATION_REQUEST, - PACKET_RESERVATION, + PACKET_UNUSED1, + PACKET_UNUSED2, PACKET_JOIN_REQUEST, PACKET_JOIN, PACKET_CHAT, @@ -34,6 +34,10 @@ enum PacketType { PACKET_NETWORK_PLAYERS, PACKET_DEATH, + PACKET_RESERVATION_LIST, + PACKET_RESERVATION_USE, + PACKET_RESERVATION_RELEASE, + PACKET_CHANGE_LEVEL, PACKET_CHANGE_AREA, PACKET_LEVEL_AREA_REQUEST, @@ -148,12 +152,6 @@ void network_receive_collect_coin(struct Packet* p); void network_send_collect_item(struct Object* o); void network_receive_collect_item(struct Packet* p); -// packet_reservation.c -void network_send_reservation_request(void); -void network_receive_reservation_request(struct Packet* p); -void network_send_reservation(u8 toLocalIndex); -void network_receive_reservation(struct Packet* p); - // packet_join.c void network_send_join_request(void); void network_receive_join_request(struct Packet* p); @@ -241,4 +239,16 @@ void network_receive_level_area_inform(struct Packet* p); void network_send_level_respawn_info(struct Object* o, u8 respawnInfoBits); void network_receive_level_respawn_info(struct Packet* p); +// packet_reservation_list.c +void network_send_reservation_list(struct NetworkPlayer* np, u8 syncIds[]); +void network_receive_reservation_list(struct Packet* p); + +// packet_reservation_use.c +void network_send_reservation_use(u8 syncId); +void network_receive_reservation_use(struct Packet* p); + +// packet_reservation_release.c +void network_send_reservation_release(u8 syncId); +void network_receive_reservation_release(struct Packet* p); + #endif diff --git a/src/pc/network/packets/packet_change_area.c b/src/pc/network/packets/packet_change_area.c index 213f3d345..921cb19b8 100644 --- a/src/pc/network/packets/packet_change_area.c +++ b/src/pc/network/packets/packet_change_area.c @@ -1,5 +1,6 @@ #include #include "../network.h" +#include "../reservation_area.h" #include "level_table.h" //#define DISABLE_MODULE_LOG 1 #include "pc/debuglog.h" @@ -12,6 +13,7 @@ static void player_changed_area(struct NetworkPlayer* np, s16 courseNum, s16 act np->currAreaIndex = areaIndex; np->currAreaSyncValid = false; network_send_level_area_inform(np); + reservation_area_change(np); // find a NetworkPlayer at that area struct NetworkPlayer* npLevelAreaMatch = get_network_player_from_area(courseNum, actNum, levelNum, areaIndex); diff --git a/src/pc/network/packets/packet_change_level.c b/src/pc/network/packets/packet_change_level.c index b4c42c01d..95ca3f2ae 100644 --- a/src/pc/network/packets/packet_change_level.c +++ b/src/pc/network/packets/packet_change_level.c @@ -1,5 +1,6 @@ #include #include "../network.h" +#include "../reservation_area.h" #include "level_table.h" //#define DISABLE_MODULE_LOG 1 #include "pc/debuglog.h" @@ -13,6 +14,7 @@ static void player_changed_level(struct NetworkPlayer* np, s16 courseNum, s16 ac np->currLevelSyncValid = false; np->currAreaSyncValid = false; network_send_level_area_inform(np); + reservation_area_change(np); // find a NetworkPlayer around that location struct NetworkPlayer* npLevelAreaMatch = get_network_player_from_area(courseNum, actNum, levelNum, areaIndex); diff --git a/src/pc/network/packets/packet_chat.c b/src/pc/network/packets/packet_chat.c index 39b086c68..82b93bd42 100644 --- a/src/pc/network/packets/packet_chat.c +++ b/src/pc/network/packets/packet_chat.c @@ -1,5 +1,6 @@ #include #include "../network.h" +#include "../reservation_area.h" #include "game/chat.h" #include "pc/debuglog.h" @@ -39,6 +40,7 @@ void network_send_chat(char* message, u8 rgb[3]) { #ifdef DEVELOPMENT print_network_player_table(); + reservation_area_debug(); #endif } @@ -55,8 +57,9 @@ void network_receive_chat(struct Packet* p) { // add the message chat_add_message_ext(remoteMessage, CMT_REMOTE, rgb); LOG_INFO("rx chat: %s", remoteMessage); - + /* #ifdef DEVELOPMENT print_network_player_table(); #endif + */ } diff --git a/src/pc/network/packets/packet_object.c b/src/pc/network/packets/packet_object.c index 881bfc0e2..153a12172 100644 --- a/src/pc/network/packets/packet_object.c +++ b/src/pc/network/packets/packet_object.c @@ -1,6 +1,7 @@ #include #include #include "../network.h" +#include "../reservation_area.h" #include "object_fields.h" #include "object_constants.h" #include "behavior_data.h" @@ -10,7 +11,6 @@ #include "src/game/obj_behaviors.h" #include "pc/debuglog.h" -static u8 nextSyncID = 1; struct SyncObject gSyncObjects[MAX_SYNC_OBJECTS] = { 0 }; struct Packet sLastSyncEntReliablePacket[MAX_SYNC_OBJECTS] = { 0 }; @@ -77,7 +77,6 @@ struct SyncObject* network_init_object(struct Object *o, float maxSyncDistance) // set default values for sync object struct SyncObject* so = &gSyncObjects[o->oSyncID]; so->o = o; - so->reserved = 0; so->maxSyncDistance = maxSyncDistance; so->owned = false; so->clockSinceUpdate = clock(); @@ -143,22 +142,28 @@ void network_clear_sync_objects(void) { for (u16 i = 0; i < MAX_SYNC_OBJECTS; i++) { network_forget_sync_object(&gSyncObjects[i]); } - nextSyncID = 1; } void network_set_sync_id(struct Object* o) { if (o->oSyncID != 0) { return; } - u8 reserveId = gNetworkLevelLoaded ? gNetworkPlayerLocal->globalIndex : 0; - - for (u16 i = 0; i < MAX_SYNC_OBJECTS; i++) { - if (gSyncObjects[nextSyncID].reserved == reserveId && gSyncObjects[nextSyncID].o == NULL) { break; } - nextSyncID = (nextSyncID + 1) % MAX_SYNC_OBJECTS; + u8 syncId = 0; + if (!gNetworkLevelLoaded) { + // while loading, just fill in sync ids from 1 to MAX_SYNC_OBJECTS + for (int i = 1; i < MAX_SYNC_OBJECTS; i++) { + if (gSyncObjects[i].o != NULL) { continue; } + syncId = i; + break; + } + } else { + // no longer loading, require reserved id + syncId = reservation_area_local_grab_id(); } - assert(gSyncObjects[nextSyncID].o == NULL); - assert(gSyncObjects[nextSyncID].reserved == reserveId); - o->oSyncID = nextSyncID; - nextSyncID = (nextSyncID + 1) % MAX_SYNC_OBJECTS; + + assert(syncId != 0); + assert(gSyncObjects[syncId].o == NULL); + + o->oSyncID = syncId; if (gNetworkLevelLoaded) { LOG_INFO("set sync id for object w/behavior %d", get_id_from_behavior(o->behavior)); @@ -398,14 +403,15 @@ void network_send_object(struct Object* o) { void network_send_object_reliability(struct Object* o, bool reliable) { // sanity check SyncObject if (!network_sync_object_initialized(o)) { return; } - struct SyncObject* so = &gSyncObjects[o->oSyncID]; + u8 syncId = o->oSyncID; + struct SyncObject* so = &gSyncObjects[syncId]; if (so == NULL) { return; } if (o != so->o) { - LOG_ERROR("object mismatch for %d", o->oSyncID); + LOG_ERROR("object mismatch for %d", syncId); return; } if (o->behavior != so->behavior && !allowable_behavior_change(so, so->behavior)) { - LOG_ERROR("behavior mismatch for %d: %04X vs %04X", o->oSyncID, get_id_from_behavior(o->behavior), get_id_from_behavior(so->behavior)); + LOG_ERROR("behavior mismatch for %d: %04X vs %04X", syncId, get_id_from_behavior(o->behavior), get_id_from_behavior(so->behavior)); network_forget_sync_object(so); return; } @@ -426,9 +432,14 @@ void network_send_object_reliability(struct Object* o, bool reliable) { // check for object death if (o->activeFlags == ACTIVE_FLAG_DEACTIVATED) { network_forget_sync_object(so); + if (gNetworkType == NT_SERVER) { + reservation_area_release(gNetworkPlayerLocal, syncId); + } else { + network_send_reservation_release(syncId); + } } else { // remember packet - packet_duplicate(&p, &sLastSyncEntReliablePacket[o->oSyncID]); + packet_duplicate(&p, &sLastSyncEntReliablePacket[syncId]); } // send the packet out @@ -511,12 +522,11 @@ void network_forget_sync_object(struct SyncObject* so) { so->o = NULL; so->behavior = NULL; - so->reserved = 0; so->owned = false; } void network_update_objects(void) { - for (u32 i = 1; i < nextSyncID; i++) { + for (u32 i = 1; i < MAX_SYNC_OBJECTS; i++) { struct SyncObject* so = &gSyncObjects[i]; if (so->o == NULL) { continue; } diff --git a/src/pc/network/packets/packet_reservation.c b/src/pc/network/packets/packet_reservation.c deleted file mode 100644 index 1b50f5bc4..000000000 --- a/src/pc/network/packets/packet_reservation.c +++ /dev/null @@ -1,73 +0,0 @@ -#include -#include "../network.h" -#include "object_fields.h" -#include "object_constants.h" -#include "behavior_table.h" -#include "course_table.h" -#include "src/game/interaction.h" -#include "src/engine/math_util.h" -#define DISABLE_MODULE_LOG 1 -#include "pc/debuglog.h" - -#define RESERVATION_COUNT 5 - -void network_send_reservation_request(void) { - assert(gNetworkType == NT_CLIENT); - - struct Packet p; - packet_init(&p, PACKET_RESERVATION_REQUEST, true, false); - network_send_to(gNetworkPlayerServer->localIndex , &p); -} - -void network_receive_reservation_request(struct Packet* p) { - assert(gNetworkType == NT_SERVER); - network_send_reservation(p->localIndex); -} - -void network_send_reservation(u8 toLocalIndex) { - assert(gNetworkType == NT_SERVER); - assert(toLocalIndex != 0); - - // find all reserved objects - u8 reservedObjs[RESERVATION_COUNT] = { 0 }; - u16 reservedIndex = 0; - for (u16 i = 1; i < MAX_SYNC_OBJECTS; i++) { - if (gSyncObjects[i].reserved == toLocalIndex) { - reservedObjs[reservedIndex++] = i; - if (reservedIndex >= RESERVATION_COUNT) { break; } - } - } - - if (reservedIndex < RESERVATION_COUNT) { - // reserve the rest - for (u16 i = MAX_SYNC_OBJECTS - 1; i > 0; i--) { - if (gSyncObjects[i].o != NULL) { continue; } - if (gSyncObjects[i].reserved != 0) { continue; } - gSyncObjects[i].reserved = toLocalIndex; - reservedObjs[reservedIndex++] = i; - if (reservedIndex >= RESERVATION_COUNT) { break; } - } - } - - struct Packet p; - packet_init(&p, PACKET_RESERVATION, true, false); - packet_write(&p, reservedObjs, sizeof(u8) * RESERVATION_COUNT); - network_send_to(toLocalIndex, &p); - LOG_INFO("sent reservation list to %d", toLocalIndex); -} - -void network_receive_reservation(struct Packet* p) { - assert(gNetworkType == NT_CLIENT); - - // find all reserved objects - u8 reservedObjs[RESERVATION_COUNT] = { 0 }; - packet_read(p, reservedObjs, sizeof(u8) * RESERVATION_COUNT); - - for (u16 i = 0; i < RESERVATION_COUNT; i++) { - u16 index = reservedObjs[i]; - if (index == 0) { continue; } - if (gSyncObjects[index].o != NULL) { continue; } - gSyncObjects[index].reserved = gNetworkPlayerLocal->globalIndex; - } - LOG_INFO("received reservation list"); -} diff --git a/src/pc/network/packets/packet_reservation_list.c b/src/pc/network/packets/packet_reservation_list.c new file mode 100644 index 000000000..8cf6f2c9f --- /dev/null +++ b/src/pc/network/packets/packet_reservation_list.c @@ -0,0 +1,52 @@ +#include +#include "../network.h" +#include "../reservation_area.h" +#include "object_fields.h" +#include "object_constants.h" +#include "behavior_table.h" +#include "course_table.h" +#include "src/game/interaction.h" +#include "src/engine/math_util.h" +#define DISABLE_MODULE_LOG 1 +#include "pc/debuglog.h" + +void network_send_reservation_list(struct NetworkPlayer* np, u8 syncIds[]) { + assert(gNetworkType == NT_SERVER); + + struct Packet p; + packet_init(&p, PACKET_RESERVATION_LIST, true, false); + + packet_write(&p, &np->currCourseNum, sizeof(u8)); + packet_write(&p, &np->currActNum, sizeof(u8)); + packet_write(&p, &np->currLevelNum, sizeof(u8)); + packet_write(&p, &np->currAreaIndex, sizeof(u8)); + + for (int i = 0; i < RESERVED_IDS_PER_PLAYER_COUNT; i++) { + packet_write(&p, &syncIds[i], sizeof(u8)); + } + + network_send_to(np->localIndex, &p); +} + +void network_receive_reservation_list(struct Packet* p) { + assert(gNetworkType == NT_CLIENT); + + u8 courseNum, actNum, levelNum, areaIndex; + packet_read(p, &courseNum, sizeof(u8)); + packet_read(p, &actNum, sizeof(u8)); + packet_read(p, &levelNum, sizeof(u8)); + packet_read(p, &areaIndex, sizeof(u8)); + + extern s16 gCurrCourseNum, gCurrActNum, gCurrLevelNum, gCurrAreaIndex; + if (courseNum != gCurrCourseNum || actNum != gCurrActNum || levelNum != gCurrLevelNum || areaIndex != gCurrAreaIndex) { + LOG_ERROR("received an improper location"); + return; + } + + u8 syncIds[RESERVED_IDS_PER_PLAYER_COUNT]; + for (int i = 0; i < RESERVED_IDS_PER_PLAYER_COUNT; i++) { + packet_read(p, &syncIds[i], sizeof(u8)); + } + + reservation_area_local_update(courseNum, actNum, levelNum, areaIndex, syncIds); +} diff --git a/src/pc/network/packets/packet_reservation_release.c b/src/pc/network/packets/packet_reservation_release.c new file mode 100644 index 000000000..475f29a2c --- /dev/null +++ b/src/pc/network/packets/packet_reservation_release.c @@ -0,0 +1,58 @@ +#include +#include "../network.h" +#include "../reservation_area.h" +#include "object_fields.h" +#include "object_constants.h" +#include "behavior_table.h" +#include "course_table.h" +#include "src/game/interaction.h" +#include "src/engine/math_util.h" +#define DISABLE_MODULE_LOG 1 +#include "pc/debuglog.h" + +void network_send_reservation_release(u8 syncId) { + assert(gNetworkType == NT_CLIENT); + + // make sure this is a reserved id + if (syncId < RESERVED_IDS_SYNC_OBJECT_OFFSET) { return; } + + struct Packet p; + packet_init(&p, PACKET_RESERVATION_RELEASE, true, false); + + extern s16 gCurrCourseNum, gCurrActNum, gCurrLevelNum, gCurrAreaIndex; + packet_write(&p, &gCurrCourseNum, sizeof(u8)); + packet_write(&p, &gCurrActNum, sizeof(u8)); + packet_write(&p, &gCurrLevelNum, sizeof(u8)); + packet_write(&p, &gCurrAreaIndex, sizeof(u8)); + + packet_write(&p, &syncId, sizeof(u8)); + + network_send_to(gNetworkPlayerServer->localIndex, &p); +} + +void network_receive_reservation_release(struct Packet* p) { + assert(gNetworkType == NT_SERVER); + + struct NetworkPlayer* np = &gNetworkPlayers[p->localIndex]; + if (np == NULL || np->localIndex == UNKNOWN_LOCAL_INDEX || !np->connected) { + LOG_ERROR("Receiving from inactive player!"); + return; + } + + u8 courseNum, actNum, levelNum, areaIndex; + packet_read(p, &courseNum, sizeof(u8)); + packet_read(p, &actNum, sizeof(u8)); + packet_read(p, &levelNum, sizeof(u8)); + packet_read(p, &areaIndex, sizeof(u8)); + + extern s16 gCurrCourseNum, gCurrActNum, gCurrLevelNum, gCurrAreaIndex; + if (courseNum != gCurrCourseNum || actNum != gCurrActNum || levelNum != gCurrLevelNum || areaIndex != gCurrAreaIndex) { + LOG_ERROR("received an improper location"); + return; + } + + u8 syncId; + packet_read(p, &syncId, sizeof(u8)); + + reservation_area_release(np, syncId); +} diff --git a/src/pc/network/packets/packet_reservation_use.c b/src/pc/network/packets/packet_reservation_use.c new file mode 100644 index 000000000..521ece2b2 --- /dev/null +++ b/src/pc/network/packets/packet_reservation_use.c @@ -0,0 +1,58 @@ +#include +#include "../network.h" +#include "../reservation_area.h" +#include "object_fields.h" +#include "object_constants.h" +#include "behavior_table.h" +#include "course_table.h" +#include "src/game/interaction.h" +#include "src/engine/math_util.h" +#define DISABLE_MODULE_LOG 1 +#include "pc/debuglog.h" + +void network_send_reservation_use(u8 syncId) { + assert(gNetworkType == NT_CLIENT); + + // make sure this is a reserved id + if (syncId < RESERVED_IDS_SYNC_OBJECT_OFFSET) { return; } + + struct Packet p; + packet_init(&p, PACKET_RESERVATION_USE, true, false); + + extern s16 gCurrCourseNum, gCurrActNum, gCurrLevelNum, gCurrAreaIndex; + packet_write(&p, &gCurrCourseNum, sizeof(u8)); + packet_write(&p, &gCurrActNum, sizeof(u8)); + packet_write(&p, &gCurrLevelNum, sizeof(u8)); + packet_write(&p, &gCurrAreaIndex, sizeof(u8)); + + packet_write(&p, &syncId, sizeof(u8)); + + network_send_to(gNetworkPlayerServer->localIndex, &p); +} + +void network_receive_reservation_use(struct Packet* p) { + assert(gNetworkType == NT_SERVER); + + struct NetworkPlayer* np = &gNetworkPlayers[p->localIndex]; + if (np == NULL || np->localIndex == UNKNOWN_LOCAL_INDEX || !np->connected) { + LOG_ERROR("Receiving from inactive player!"); + return; + } + + u8 courseNum, actNum, levelNum, areaIndex; + packet_read(p, &courseNum, sizeof(u8)); + packet_read(p, &actNum, sizeof(u8)); + packet_read(p, &levelNum, sizeof(u8)); + packet_read(p, &areaIndex, sizeof(u8)); + + extern s16 gCurrCourseNum, gCurrActNum, gCurrLevelNum, gCurrAreaIndex; + if (courseNum != gCurrCourseNum || actNum != gCurrActNum || levelNum != gCurrLevelNum || areaIndex != gCurrAreaIndex) { + LOG_ERROR("received an improper location"); + return; + } + + u8 syncId; + packet_read(p, &syncId, sizeof(u8)); + + reservation_area_use(np, syncId); +} diff --git a/src/pc/network/packets/packet_spawn_objects.c b/src/pc/network/packets/packet_spawn_objects.c index f90af7423..96673f27b 100644 --- a/src/pc/network/packets/packet_spawn_objects.c +++ b/src/pc/network/packets/packet_spawn_objects.c @@ -1,5 +1,6 @@ #include #include "../network.h" +#include "../reservation_area.h" #include "object_fields.h" #include "object_constants.h" #include "src/game/object_helpers.h" @@ -48,7 +49,16 @@ void network_send_spawn_objects_to(u8 sendToLocalIndex, struct Object* objects[] assert(objectCount < MAX_SPAWN_OBJECTS_PER_PACKET); struct Packet p; - packet_init(&p, PACKET_SPAWN_OBJECTS, true, true); + packet_init(&p, PACKET_SPAWN_OBJECTS, true, false); + + // level location + extern s16 gCurrCourseNum, gCurrActNum, gCurrLevelNum, gCurrAreaIndex; + packet_write(&p, &gCurrCourseNum, sizeof(s16)); + packet_write(&p, &gCurrActNum, sizeof(s16)); + packet_write(&p, &gCurrLevelNum, sizeof(s16)); + packet_write(&p, &gCurrAreaIndex, sizeof(s16)); + + // objects packet_write(&p, &objectCount, sizeof(u8)); for (u8 i = 0; i < objectCount; i++) { @@ -74,13 +84,22 @@ void network_send_spawn_objects_to(u8 sendToLocalIndex, struct Object* objects[] } void network_receive_spawn_objects(struct Packet* p) { + // read level location + 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)); + + extern s16 gCurrCourseNum, gCurrActNum, gCurrLevelNum, gCurrAreaIndex; + if (courseNum != gCurrCourseNum || actNum != gCurrActNum || levelNum != gCurrLevelNum || areaIndex != gCurrAreaIndex) { + LOG_ERROR("received an improper location"); + return; + } + u8 objectCount = 0; - packet_read(p, &objectCount, sizeof(u8)); - u8 reserveId = gNetworkLevelLoaded ? gNetworkPlayers[p->localIndex].globalIndex : 0; - bool receivedReservedSyncObject = false; - struct Object* spawned[MAX_SPAWN_OBJECTS_PER_PACKET] = { 0 }; for (u8 i = 0; i < objectCount; i++) { struct SpawnObjectData data = { 0 }; @@ -129,20 +148,11 @@ void network_receive_spawn_objects(struct Packet* p) { // correct the temporary parent with the object itself if (data.parentId == (u8)-1) { o->parentObj = o; } - if (o->oSyncID != 0) { + if (o->oSyncID != 0 && o->oSyncID >= RESERVED_IDS_SYNC_OBJECT_OFFSET) { // check if they've allocated one of their reserved sync objects - receivedReservedSyncObject = (o->oSyncID != 0 && gSyncObjects[o->oSyncID].reserved == reserveId); - if (receivedReservedSyncObject || gNetworkLevelSyncing) { - gSyncObjects[o->oSyncID].o = o; - gSyncObjects[o->oSyncID].reserved = 0; - } + gSyncObjects[o->oSyncID].o = o; } spawned[i] = o; } - - // update their block of reserved ids - if (gNetworkType == NT_SERVER && receivedReservedSyncObject) { - network_send_reservation(p->localIndex); - } } diff --git a/src/pc/network/reservation_area.c b/src/pc/network/reservation_area.c new file mode 100644 index 000000000..df86a8911 --- /dev/null +++ b/src/pc/network/reservation_area.c @@ -0,0 +1,300 @@ +#include +#include "reservation_area.h" +#include "network.h" +#include "object_fields.h" +#include "object_constants.h" +#include "behavior_table.h" +#include "course_table.h" +#include "src/game/interaction.h" +#include "src/engine/math_util.h" +//#define DISABLE_MODULE_LOG 1 +#include "pc/debuglog.h" + +#define RESERVED_IDS_PER_AREA 127 +#define RESERVED_IDS_UNRESERVED ((u8)-1) +#define RESERVED_IDS_USED ((u8)-2) + +struct ReservationArea { + u8 courseNum; + u8 actNum; + u8 levelNum; + u8 areaIndex; + u8 playersActive; + u8 reservedIds[RESERVED_IDS_PER_AREA]; + struct ReservationArea* next; +}; + +struct ReservationArea* sReservationAreas = NULL; +struct ReservationArea* sReservationAreaPerPlayer[MAX_PLAYERS] = { NULL }; + +struct LocalReservationArea { + u8 courseNum; + u8 actNum; + u8 levelNum; + u8 areaIndex; + u8 reservedIds[RESERVED_IDS_PER_PLAYER_COUNT]; +}; +struct LocalReservationArea sLocalReservationArea = { 0 }; + +void reservation_area_debug(void) { + printf("\n============ %02d ============\n", gNetworkPlayerLocal->globalIndex); + + printf("reservation area per player:\n"); + for (int i = 0; i < MAX_PLAYERS; i++) { + struct ReservationArea* ra = sReservationAreaPerPlayer[i]; + if (ra != NULL) { + printf(" %d : (%d, %d, %d, %d)\n", i, ra->courseNum, ra->actNum, ra->levelNum, ra->areaIndex); + } + } + printf("\n"); + + printf("reservation areas:\n"); + struct ReservationArea* ra = sReservationAreas; + while (ra != NULL) { + printf(" (%d, %d, %d, %d) : %d\n", ra->courseNum, ra->actNum, ra->levelNum, ra->areaIndex, ra->playersActive); + + printf(" "); + u8 idsUntilBreak = 10; + for (int i = 0; i < RESERVED_IDS_PER_AREA; i++) { + switch (ra->reservedIds[i]) + { + case RESERVED_IDS_UNRESERVED: printf("UNR "); break; + case RESERVED_IDS_USED: printf("USD "); break; + default: printf("%03d ", ra->reservedIds[i]); break; + } + if (--idsUntilBreak == 0) { + printf("\n "); + idsUntilBreak = 10; + } + } + printf("\n\n"); + ra = ra->next; + } + + printf("local reservation area:\n"); + struct LocalReservationArea* la = &sLocalReservationArea; + printf(" (%d, %d, %d, %d) : ", la->courseNum, la->actNum, la->levelNum, la->areaIndex); + for (int i = 0; i < RESERVED_IDS_PER_PLAYER_COUNT; i++) { + switch (la->reservedIds[i]) + { + case RESERVED_IDS_UNRESERVED: printf("UNR "); break; + case RESERVED_IDS_USED: printf("USD "); break; + default: printf("%03d ", la->reservedIds[i]); break; + } + } + printf("\n\n"); +} + +static void reservation_area_refresh_ids(struct NetworkPlayer* np) { + bool informPlayer = false; + + // make sure player has a RA + struct ReservationArea* ra = sReservationAreaPerPlayer[np->globalIndex]; + if (ra == NULL) { return; } + + // count current reserved ids + u8 reservedIds[RESERVED_IDS_PER_PLAYER_COUNT] = { RESERVED_IDS_UNRESERVED }; + u8 reservedIdCount = 0; + for (int i = 0; i < RESERVED_IDS_PER_AREA; i++) { + if (ra->reservedIds[i] != np->globalIndex) { continue; } + reservedIds[reservedIdCount] = i + RESERVED_IDS_SYNC_OBJECT_OFFSET; + reservedIdCount++; + } + + // fill in missing reserved ids + if (reservedIdCount < RESERVED_IDS_PER_PLAYER_COUNT) { + for (int i = 0; i < RESERVED_IDS_PER_AREA; i++) { + if (ra->reservedIds[i] != RESERVED_IDS_UNRESERVED) { continue; } + ra->reservedIds[i] = np->globalIndex; + informPlayer = true; + reservedIds[reservedIdCount] = i + RESERVED_IDS_SYNC_OBJECT_OFFSET; + reservedIdCount++; + if (reservedIdCount >= RESERVED_IDS_PER_PLAYER_COUNT) { break; } + } + } + + if (gNetworkType == NT_SERVER && np == gNetworkPlayerLocal) { + // refresh server's local + sLocalReservationArea.courseNum = np->currCourseNum; + sLocalReservationArea.actNum = np->currActNum; + sLocalReservationArea.levelNum = np->currLevelNum; + sLocalReservationArea.areaIndex = np->currAreaIndex; + + for (int i = 0; i < RESERVED_IDS_PER_PLAYER_COUNT; i++) { + sLocalReservationArea.reservedIds[i] = reservedIds[i]; + } + } else if (informPlayer) { + // inform remote player of reservation list + network_send_reservation_list(np, reservedIds); + } +} + +static void reservation_area_unload(struct ReservationArea* unloadRa) { + struct ReservationArea* ra = sReservationAreas; + struct ReservationArea* lastRa = NULL; + while (ra != NULL) { + if (ra == unloadRa) { + if (lastRa == NULL) { + sReservationAreas = ra->next; + } else { + lastRa->next = ra->next; + } + free(unloadRa); + return; + } + lastRa = ra; + ra = ra->next; + } + assert(false); +} + +static void reservation_area_player_left(struct NetworkPlayer* np) { + // make sure player has a RA + struct ReservationArea* ra = sReservationAreaPerPlayer[np->globalIndex]; + if (ra == NULL) { return; } + + // remove player's reserved ids + for (int i = 0; i < RESERVED_IDS_PER_AREA; i++) { + if (ra->reservedIds[i] == np->globalIndex) { + ra->reservedIds[i] = RESERVED_IDS_UNRESERVED; + } + } + + // remove player from RA, unload if sensible + ra->playersActive--; + if (ra->playersActive == 0) { + reservation_area_unload(ra); + } + sReservationAreaPerPlayer[np->globalIndex] = NULL; +} + +void reservation_area_change(struct NetworkPlayer* np) { + // check for disconnection + if (!np->connected) { + reservation_area_player_left(np); + return; + } + + // make sure the location actually changed + struct ReservationArea* ra = sReservationAreaPerPlayer[np->globalIndex]; + if (ra != NULL && ra->courseNum == np->currCourseNum && ra->actNum == np->currActNum && ra->levelNum == np->currLevelNum && ra->areaIndex == np->currAreaIndex) { + return; + } + + // remove from the old reservation area + reservation_area_player_left(np); + + // find the reservation area + ra = sReservationAreas; + struct ReservationArea* lastRa = ra; + while (ra != NULL) { + if (ra->courseNum == np->currCourseNum && ra->actNum == np->currActNum && ra->levelNum == np->currLevelNum && ra->areaIndex == np->currAreaIndex) { + // add to new reservation area + ra->playersActive++; + sReservationAreaPerPlayer[np->globalIndex] = ra; + reservation_area_refresh_ids(np); + return; + } + lastRa = ra; + ra = ra->next; + } + + // allocate the reservation area + ra = malloc(sizeof(struct ReservationArea)); + ra->courseNum = np->currCourseNum; + ra->actNum = np->currActNum; + ra->levelNum = np->currLevelNum; + ra->areaIndex = np->currAreaIndex; + for (int i = 0; i < RESERVED_IDS_PER_AREA; i++) { + ra->reservedIds[i] = RESERVED_IDS_UNRESERVED; + } + + ra->playersActive = 1; + ra->next = NULL; + + sReservationAreaPerPlayer[np->globalIndex] = ra; + + // fix up linked list + if (lastRa == NULL) { + sReservationAreas = ra; + } else { + lastRa->next = ra; + } + + // refresh ids + reservation_area_refresh_ids(np); +} + +void reservation_area_use(struct NetworkPlayer* np, u8 syncId) { + // make sure player has a RA + struct ReservationArea* ra = sReservationAreaPerPlayer[np->globalIndex]; + if (ra == NULL) { return; } + + u8 offset = syncId - RESERVED_IDS_SYNC_OBJECT_OFFSET; + + // sanity check + if (offset >= RESERVED_IDS_PER_AREA) { return; } + + ra->reservedIds[offset] = RESERVED_IDS_USED; + + reservation_area_refresh_ids(np); +} + +void reservation_area_release(struct NetworkPlayer* np, u8 syncId) { + // make sure player has a RA + struct ReservationArea* ra = sReservationAreaPerPlayer[np->globalIndex]; + if (ra == NULL) { return; } + + u8 offset = syncId - RESERVED_IDS_SYNC_OBJECT_OFFSET; + + // sanity check + if (offset >= RESERVED_IDS_PER_AREA) { return; } + + ra->reservedIds[offset] = RESERVED_IDS_UNRESERVED; + + reservation_area_refresh_ids(np); +} + +void reservation_area_local_update(u8 courseNum, u8 actNum, u8 levelNum, u8 areaIndex, u8 syncIds[]) { + sLocalReservationArea.courseNum = courseNum; + sLocalReservationArea.actNum = actNum; + sLocalReservationArea.levelNum = levelNum; + sLocalReservationArea.areaIndex = areaIndex; + + for (int i = 0; i < RESERVED_IDS_PER_PLAYER_COUNT; i++) { + sLocalReservationArea.reservedIds[i] = syncIds[i]; + } +} + +u8 reservation_area_local_grab_id(void) { + struct LocalReservationArea* la = &sLocalReservationArea; + + extern s16 gCurrCourseNum, gCurrActNum, gCurrLevelNum, gCurrAreaIndex; + if (la->courseNum != gCurrCourseNum || la->actNum != gCurrActNum || la->levelNum != gCurrLevelNum || la->areaIndex != gCurrAreaIndex) { + // invalid location + return 0; + } + + // grab a sync id from reserved list + u8 syncId = 0; + for (int i = 0; i < RESERVED_IDS_PER_PLAYER_COUNT; i++) { + if (la->reservedIds[i] == 0 || la->reservedIds[i] == RESERVED_IDS_UNRESERVED || la->reservedIds[i] == RESERVED_IDS_USED) { continue; } + + // found one + syncId = la->reservedIds[i]; + la->reservedIds[i] = 0; + break; + } + + // sanity check + if (syncId == 0) { return 0; } + + // inform the server that we used that id + if (gNetworkType == NT_SERVER) { + reservation_area_use(gNetworkPlayerLocal, syncId); + } else { + network_send_reservation_use(syncId); + } + + return syncId; +} diff --git a/src/pc/network/reservation_area.h b/src/pc/network/reservation_area.h new file mode 100644 index 000000000..d5c3c1da8 --- /dev/null +++ b/src/pc/network/reservation_area.h @@ -0,0 +1,22 @@ +#ifndef RESERVATION_AREA_H +#define RESERVATION_AREA_H + +#include "PR/ultratypes.h" +#include +#include +#include +#include + +#define RESERVED_IDS_PER_PLAYER_COUNT 5 +#define RESERVED_IDS_SYNC_OBJECT_OFFSET 127 + +struct NetworkPlayer; + +void reservation_area_debug(void); +void reservation_area_change(struct NetworkPlayer* np); +void reservation_area_use(struct NetworkPlayer* np, u8 syncId); +void reservation_area_release(struct NetworkPlayer* np, u8 syncId); +void reservation_area_local_update(u8 courseNum, u8 actNum, u8 levelNum, u8 areaIndex, u8 syncIds[]); +u8 reservation_area_local_grab_id(void); + +#endif diff --git a/src/pc/network/version.h b/src/pc/network/version.h index b65d13606..9bb8def19 100644 --- a/src/pc/network/version.h +++ b/src/pc/network/version.h @@ -2,7 +2,7 @@ #define VERSION_H #define UNSTABLE_BRANCH -#define VERSION_NUMBER 3 +#define VERSION_NUMBER 4 #define MAX_VERSION_LENGTH 10 char* get_version(void);