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);