diff --git a/build-windows-visual-studio/sm64ex.vcxproj b/build-windows-visual-studio/sm64ex.vcxproj
index dc68a31d2..d536ee689 100644
--- a/build-windows-visual-studio/sm64ex.vcxproj
+++ b/build-windows-visual-studio/sm64ex.vcxproj
@@ -3974,6 +3974,7 @@
+
diff --git a/build-windows-visual-studio/sm64ex.vcxproj.filters b/build-windows-visual-studio/sm64ex.vcxproj.filters
index 49a460886..a41039554 100644
--- a/build-windows-visual-studio/sm64ex.vcxproj.filters
+++ b/build-windows-visual-studio/sm64ex.vcxproj.filters
@@ -15087,6 +15087,9 @@
Source Files\src\game
+
+ Source Files\src\pc\network\packets
+
diff --git a/developer/network.sh b/developer/network.sh
index 795ded532..8d0b2e08f 100644
--- a/developer/network.sh
+++ b/developer/network.sh
@@ -18,9 +18,9 @@ fi
#exit
# no debug, direct
-#$FILE --server 27015 --configfile sm64config_server.txt &
-#$FILE --client 127.0.0.1 27015 --configfile sm64config_client.txt &
-#exit
+$FILE --server 27015 --configfile sm64config_server.txt &
+$FILE --client 127.0.0.1 27015 --configfile sm64config_client.txt &
+exit
# debug on server
#$FILE --client 127.0.0.1 27015 --configfile sm64config_client.txt &
diff --git a/developer/proto-4.sh b/developer/proto-4.sh
index 623295c62..128afe9c8 100644
--- a/developer/proto-4.sh
+++ b/developer/proto-4.sh
@@ -12,26 +12,6 @@ if [ ! -f "$FILE" ]; then
FILE=./build/us_pc/sm64.us.f3dex2e
fi
-# no debug, discord
-#$FILE --discord 2 --configfile sm64config_server.txt &
-#$FILE --discord 1 --configfile sm64config_client.txt &
-#exit
-
-# no debug, direct
-#$FILE --server 27015 --configfile sm64config_server.txt &
-#$FILE --client 127.0.0.1 27015 --configfile sm64config_client.txt &
-#exit
-
-# debug on server
-#$FILE --client 127.0.0.1 27015 --configfile sm64config_client.txt &
-#winpty cgdb $FILE -ex 'break debug_breakpoint_here' -ex 'run --server 27015 --configfile sm64config_server.txt' -ex 'quit'
-#exit
-
-###################
-# debug on client #
-###################
-
-#winpty cgdb $FILE -ex 'break debug_breakpoint_here' -ex 'run --server 27015 --configfile sm64config_p1.txt' -ex 'quit'
$FILE --server 27015 --configfile sm64config_p1.txt &
sleep 2
$FILE --client 127.0.0.1 27015 --configfile sm64config_p2.txt &
@@ -39,4 +19,7 @@ sleep 2
$FILE --client 127.0.0.1 27015 --configfile sm64config_p3.txt &
sleep 2
$FILE --client 127.0.0.1 27015 --configfile sm64config_p4.txt &
+
+#sleep 2
+#winpty cgdb $FILE -ex 'break debug_breakpoint_here' -ex 'run --server 27015 --configfile sm64config_p1.txt' -ex 'quit'
#winpty cgdb $FILE -ex 'break debug_breakpoint_here' -ex 'run --client 127.0.0.1 27015 --configfile sm64config_p4.txt' -ex 'quit'
diff --git a/src/game/area.c b/src/game/area.c
index aff256358..cafd4207d 100644
--- a/src/game/area.c
+++ b/src/game/area.c
@@ -257,6 +257,15 @@ void load_area(s32 index) {
load_obj_warp_nodes();
geo_call_global_function_nodes(&gCurrentArea->unk04->node, GEO_CONTEXT_AREA_LOAD);
}
+
+ if (!network_is_warp_2_duplicate()) {
+ if (gNetworkType != NT_NONE) {
+ network_send_level_warp_2(TRUE, gNetworkPlayerLocal->globalIndex);
+ }
+ if (gNetworkType == NT_CLIENT) {
+ sCurrPlayMode = PLAY_MODE_SYNC_LEVEL;
+ }
+ }
}
void unload_area(void) {
@@ -442,15 +451,8 @@ void render_game(void) {
}
// only render 'synchronizing' text if we've been waiting for a while
- static u8 syncLevelTime = 0;
if (sCurrPlayMode == PLAY_MODE_SYNC_LEVEL) {
- if (syncLevelTime < 30) {
- syncLevelTime++;
- } else {
- render_sync_level_screen();
- }
- } else {
- syncLevelTime = 0;
+ render_sync_level_screen();
}
D_8032CE74 = NULL;
diff --git a/src/game/level_update.c b/src/game/level_update.c
index 52d8def9f..a334233a4 100644
--- a/src/game/level_update.c
+++ b/src/game/level_update.c
@@ -53,6 +53,10 @@ u8 gControlledWarpGlobalIndex = 0;
extern s8 sReceivedLoadedActNum;
u8 gRejectInstantWarp = 0;
+s16 gChangeLevel = -1;
+s16 gChangeAreaIndex = -1;
+s16 gChangeActNum = -1;
+
#ifdef VERSION_JP
const char *credits01[] = { "1GAME DIRECTOR", "SHIGERU MIYAMOTO" };
const char *credits02[] = { "2ASSISTANT DIRECTORS", "YOSHIAKI KOIZUMI", "TAKASHI TEZUKA" };
@@ -1090,15 +1094,9 @@ s32 play_mode_normal(void) {
}
} else if (!gReceiveWarp.received) {
if (sWarpDest.type == WARP_TYPE_CHANGE_LEVEL) {
- set_play_mode(PLAY_MODE_SYNC_LEVEL);
- network_send_level_warp_begin();
+ set_play_mode(PLAY_MODE_CHANGE_LEVEL);
} else if (sTransitionTimer != 0) {
- if (sWarpDest.type == WARP_TYPE_CHANGE_AREA) {
- set_play_mode(PLAY_MODE_SYNC_LEVEL);
- network_send_level_warp_begin();
- } else {
- set_play_mode(PLAY_MODE_CHANGE_AREA);
- }
+ set_play_mode(PLAY_MODE_CHANGE_AREA);
} else if (sCurrPlayMode == PLAY_MODE_NORMAL && pressed_pause()) {
lower_background_noise(1);
cancel_rumble();
@@ -1129,8 +1127,7 @@ s32 play_mode_paused(void) {
fade_into_special_warp(0, 0);
gSavedCourseNum = COURSE_NONE;
}
- set_play_mode(PLAY_MODE_SYNC_LEVEL);
- network_send_level_warp_begin();
+ set_play_mode(PLAY_MODE_CHANGE_LEVEL);
} else if (gPauseScreenMode == 3) {
// We should only be getting "int 3" to here
initiate_warp(LEVEL_CASTLE, 1, 0x1F, 0);
@@ -1150,7 +1147,7 @@ s32 play_mode_sync_level(void) {
set_menu_mode(-1);
gCameraMovementFlags &= ~CAM_MOVE_PAUSE_SCREEN;
- check_received_warp();
+ //check_received_warp();
return 0;
}
@@ -1262,6 +1259,15 @@ static s32 play_mode_unused(void) {
s32 update_level(void) {
s32 changeLevel = 0;
+ if (gChangeLevel != -1) {
+ gHudDisplay.flags = HUD_DISPLAY_NONE;
+ sTransitionTimer = 0;
+ sTransitionUpdate = NULL;
+ changeLevel = gChangeLevel;
+ gChangeLevel = -1;
+ return changeLevel;
+ }
+
switch (sCurrPlayMode) {
case PLAY_MODE_NORMAL:
changeLevel = play_mode_normal();
diff --git a/src/game/level_update.h b/src/game/level_update.h
index eeea9baac..52f5d9a2e 100644
--- a/src/game/level_update.h
+++ b/src/game/level_update.h
@@ -76,6 +76,10 @@ extern s16 sTransitionTimer;
extern void (*sTransitionUpdate)(s16 *);
extern u8 unused3[4];
+extern s16 gChangeLevel;
+extern s16 gChangeAreaIndex;
+extern s16 gChangeActNum;
+
struct WarpDest {
u8 type;
u8 levelNum;
diff --git a/src/pc/network/packets/packet.c b/src/pc/network/packets/packet.c
index 800cc8db6..b7d44f683 100644
--- a/src/pc/network/packets/packet.c
+++ b/src/pc/network/packets/packet.c
@@ -56,6 +56,7 @@ void packet_receive(struct Packet* p) {
case PACKET_INSTANT_WARP: network_receive_instant_warp(p); break;
case PACKET_NETWORK_PLAYERS: network_receive_network_players(p); break;
case PACKET_DEATH: network_receive_death(p); break;
+ case PACKET_LEVEL_WARP_2: network_receive_level_warp_2(p); break;
///
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 e14b0db3c..2f2835f90 100644
--- a/src/pc/network/packets/packet.h
+++ b/src/pc/network/packets/packet.h
@@ -33,6 +33,7 @@ enum PacketType {
PACKET_INSTANT_WARP,
PACKET_NETWORK_PLAYERS,
PACKET_DEATH,
+ PACKET_LEVEL_WARP_2,
///
PACKET_CUSTOM = 255,
};
@@ -171,4 +172,9 @@ void network_receive_network_players(struct Packet* p);
void network_send_death(void);
void network_receive_death(struct Packet* p);
+// packet_level_warp_2.c
+void network_send_level_warp_2(u8 eventBegins, u8 controlledGlobalIndex);
+void network_receive_level_warp_2(struct Packet* p);
+u8 network_is_warp_2_duplicate(void);
+
#endif
diff --git a/src/pc/network/packets/packet_level_warp_2.c b/src/pc/network/packets/packet_level_warp_2.c
new file mode 100644
index 000000000..9cb574d43
--- /dev/null
+++ b/src/pc/network/packets/packet_level_warp_2.c
@@ -0,0 +1,143 @@
+#include "../network.h"
+#include "game/level_update.h"
+#include "game/object_list_processor.h"
+//#define DISABLE_MODULE_LOG
+#include "pc/debuglog.h"
+
+#define SERVER_RETAIN_WARP_SECONDS 1
+
+extern u8 gControlledWarpGlobalIndex;
+extern float gPaintingMarioYEntry;
+
+#pragma pack(1)
+struct PacketLevelWarp2Data {
+ s16 levelNum;
+ s16 areaIndex;
+ s16 actNum;
+
+ u8 warpType;
+ u8 warpLevelNum;
+ u8 warpAreaIdx;
+ u8 warpNodeId;
+ u32 warpArg;
+
+ s8 inWarpCheckpoint;
+ s16 ttcSpeedSetting;
+ s16 D_80339EE0;
+ f32 paintingMarioYEntry;
+ u8 controlledWarpGlobalIndex;
+};
+
+struct PacketLevelWarp2Data sSavedLevelWarp2Data = { 0 };
+static clock_t sSavedClockTime = 0;
+
+static void populate_packet_data(struct PacketLevelWarp2Data* data) {
+ data->levelNum = gCurrLevelNum;
+ data->areaIndex = gCurrAreaIndex;
+ data->actNum = gCurrActNum;
+
+ data->warpType = sWarpDest.type;
+ data->warpLevelNum = sWarpDest.levelNum;
+ data->warpAreaIdx = sWarpDest.areaIdx;
+ data->warpNodeId = sWarpDest.nodeId;
+ data->warpArg = sWarpDest.arg;
+
+ data->inWarpCheckpoint = gInWarpCheckpoint;
+ data->ttcSpeedSetting = gTTCSpeedSetting;
+ data->D_80339EE0 = D_80339EE0;
+ data->paintingMarioYEntry = gPaintingMarioYEntry;
+ data->controlledWarpGlobalIndex = gControlledWarpGlobalIndex;
+}
+
+void network_send_level_warp_2(u8 eventBegins, u8 controlledGlobalIndex) {
+ struct PacketLevelWarp2Data data = { 0 };
+ if (eventBegins) {
+ gControlledWarpGlobalIndex = controlledGlobalIndex;
+ populate_packet_data(&data);
+ if (gNetworkType == NT_SERVER) {
+ sSavedLevelWarp2Data = data;
+ sSavedClockTime = clock();
+ }
+ } else {
+ data = sSavedLevelWarp2Data;
+ }
+
+ struct Packet p;
+ packet_init(&p, PACKET_LEVEL_WARP_2, true, false);
+ packet_write(&p, &data, sizeof(struct PacketLevelWarp2Data));
+
+ if (gNetworkType == NT_SERVER) {
+ network_send(&p);
+ } else {
+ network_send_to(gNetworkPlayerServer->localIndex, &p);
+ }
+
+ LOG_INFO("send warp: %d, %d, %d", gCurrLevelNum, gCurrAreaIndex, gCurrActNum);
+}
+
+static void do_warp(struct PacketLevelWarp2Data* data) {
+ if (gCurrLevelNum != data->levelNum ) { gChangeLevel = data->levelNum; }
+
+ sWarpDest.type = data->warpType;
+ sWarpDest.levelNum = data->warpLevelNum;
+ sWarpDest.areaIdx = data->warpAreaIdx;
+ sWarpDest.nodeId = data->warpNodeId;
+ sWarpDest.arg = data->warpArg;
+
+ gInWarpCheckpoint = data->inWarpCheckpoint;
+ gTTCSpeedSetting = data->ttcSpeedSetting;
+ D_80339EE0 = data->D_80339EE0;
+ gPaintingMarioYEntry = data->paintingMarioYEntry;
+ gControlledWarpGlobalIndex = data->controlledWarpGlobalIndex;
+
+ gCurrLevelNum = data->levelNum;
+ gCurrAreaIndex = data->areaIndex;
+ gCurrActNum = data->actNum;
+
+ LOG_INFO("do warp: %d, %d, %d", gCurrLevelNum, gCurrAreaIndex, gCurrActNum);
+}
+
+void network_receive_level_warp_2(struct Packet* p) {
+ struct PacketLevelWarp2Data remote = { 0 };
+ packet_read(p, &remote, sizeof(struct PacketLevelWarp2Data));
+ LOG_INFO("rx warp: %d, %d, %d", remote.levelNum, remote.areaIndex, remote.actNum);
+
+ u8 levelOrAreaDifference = (gCurrLevelNum != remote.levelNum) || (gCurrAreaIndex != remote.areaIndex);
+
+ if (gNetworkType == NT_SERVER) {
+ f32 elapsed = (clock() - sSavedClockTime) / (f32)CLOCKS_PER_SEC;
+ if (elapsed < SERVER_RETAIN_WARP_SECONDS || !levelOrAreaDifference) {
+ network_send_level_warp_2(FALSE, gNetworkPlayerLocal->globalIndex);
+ return;
+ }
+ }
+
+ if (levelOrAreaDifference) {
+ do_warp(&remote);
+ }
+
+ if (gNetworkType == NT_CLIENT) {
+ sSavedLevelWarp2Data = remote;
+ sSavedClockTime = clock();
+ }
+
+ if (gNetworkType == NT_SERVER) {
+ network_send_level_warp_2(TRUE, remote.controlledWarpGlobalIndex);
+ } else {
+ sCurrPlayMode = PLAY_MODE_NORMAL;
+ network_on_init_level();
+ }
+}
+
+u8 network_is_warp_2_duplicate(void) {
+ struct PacketLevelWarp2Data data = { 0 };
+ populate_packet_data(&data);
+
+ if (data.levelNum == 1 && data.areaIndex == 1) { return TRUE; }
+
+ if (gNetworkType == NT_SERVER) {
+ f32 elapsed = (clock() - sSavedClockTime) / (f32)CLOCKS_PER_SEC;
+ if (elapsed >= SERVER_RETAIN_WARP_SECONDS) { return FALSE; }
+ }
+ return (memcmp(&sSavedLevelWarp2Data, &data, sizeof(struct PacketLevelWarp2Data)) == 0);
+}
\ No newline at end of file