From 7ae26b3ea0536b53d8a4e312b403425ec7f86e6d Mon Sep 17 00:00:00 2001 From: MysterD Date: Sun, 13 Jun 2021 16:04:28 -0700 Subject: [PATCH] Complete rewrite of entity ID reservation system Now "syncId"s can only be reserved at ID 127+. This ensures that static level objects and spawned reserved objects never clash. The server keeps track of every player's list of reserved IDs, as well as when an ID is used, or when an entity using that ID is destroyed. This is much more complicated than it used to be, but this complication is required due to players being allowed to be in different areas of the game. --- build-windows-visual-studio/sm64ex.vcxproj | 26 +- .../sm64ex.vcxproj.filters | 87 ++++- src/pc/controller/controller_keyboard_debug.c | 16 + src/pc/network/network.c | 5 - src/pc/network/network.h | 1 - src/pc/network/network_player.c | 2 + src/pc/network/packets/packet.c | 13 +- src/pc/network/packets/packet.h | 26 +- src/pc/network/packets/packet_change_area.c | 2 + src/pc/network/packets/packet_change_level.c | 2 + src/pc/network/packets/packet_chat.c | 5 +- src/pc/network/packets/packet_object.c | 46 +-- src/pc/network/packets/packet_reservation.c | 73 ----- .../network/packets/packet_reservation_list.c | 52 +++ .../packets/packet_reservation_release.c | 58 ++++ .../network/packets/packet_reservation_use.c | 58 ++++ src/pc/network/packets/packet_spawn_objects.c | 42 ++- src/pc/network/reservation_area.c | 300 ++++++++++++++++++ src/pc/network/reservation_area.h | 22 ++ src/pc/network/version.h | 2 +- 20 files changed, 692 insertions(+), 146 deletions(-) delete mode 100644 src/pc/network/packets/packet_reservation.c create mode 100644 src/pc/network/packets/packet_reservation_list.c create mode 100644 src/pc/network/packets/packet_reservation_release.c create mode 100644 src/pc/network/packets/packet_reservation_use.c create mode 100644 src/pc/network/reservation_area.c create mode 100644 src/pc/network/reservation_area.h 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);