mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2024-11-28 23:13:02 +00:00
Completely rewrote level transition synchronization code
Should be snappier and less prone to crashes. I tested as many scenarios as I could think of and it has been rock solid. But time will tell. Also created a new debug log system, just so I could understand what the hell was going on with this code.
This commit is contained in:
parent
e48a9c25ab
commit
1e6c734ced
15 changed files with 274 additions and 174 deletions
|
@ -4305,6 +4305,7 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\include\behavior_table.h" />
|
||||
<ClInclude Include="..\src\pc\debuglog.h" />
|
||||
<ClInclude Include="..\src\pc\network\network.h" />
|
||||
<ClInclude Include="..\src\pc\network\socket\socket.h" />
|
||||
<ClInclude Include="..\src\pc\network\socket\socket_linux.h" />
|
||||
|
|
|
@ -15928,5 +15928,8 @@
|
|||
<ClInclude Include="..\include\behavior_table.h">
|
||||
<Filter>Header Files\include</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\pc\debuglog.h">
|
||||
<Filter>Source Files\src\pc</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -12,6 +12,8 @@ if [ ! -f "$FILE" ]; then
|
|||
fi
|
||||
|
||||
$FILE --server 27015 --configfile sm64config_server.txt &
|
||||
#$FILE --client 127.0.0.1 27015 --configfile sm64config_client.txt &
|
||||
#exit
|
||||
|
||||
# debug if cgdb exists
|
||||
if ! [ -x "$(command -v cgdb)" ]; then
|
||||
|
|
|
@ -254,6 +254,7 @@ void load_area(s32 index) {
|
|||
}
|
||||
|
||||
void unload_area(void) {
|
||||
network_clear_sync_objects();
|
||||
if (gCurrentArea != NULL) {
|
||||
unload_objects_from_area(0, gCurrentArea->index);
|
||||
geo_call_global_function_nodes(&gCurrentArea->unk04->node, GEO_CONTEXT_AREA_UNLOAD);
|
||||
|
|
|
@ -3123,12 +3123,6 @@ s16 render_course_complete_screen(void) {
|
|||
gInGameLanguage = eu_get_language();
|
||||
#endif
|
||||
|
||||
// if we went into a painting, no more save menu!
|
||||
if (gInsidePainting == TRUE) {
|
||||
gMenuMode = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (gDialogBoxState) {
|
||||
case DIALOG_STATE_OPENING:
|
||||
render_course_complete_lvl_info_and_hud_str();
|
||||
|
|
|
@ -46,6 +46,10 @@
|
|||
|
||||
#define WARP_NODE_CREDITS_MIN 0xF8
|
||||
|
||||
u8 gControlledWarp = 0;
|
||||
u8 gReceiveWarp = 0;
|
||||
struct WarpDest gReceiveWarpDest = { 0 };
|
||||
|
||||
#ifdef VERSION_JP
|
||||
const char *credits01[] = { "1GAME DIRECTOR", "SHIGERU MIYAMOTO" };
|
||||
const char *credits02[] = { "2ASSISTANT DIRECTORS", "YOSHIAKI KOIZUMI", "TAKASHI TEZUKA" };
|
||||
|
@ -169,11 +173,6 @@ s8 D_8032C9E0 = 0;
|
|||
u8 unused3[4];
|
||||
u8 unused4[2];
|
||||
|
||||
u8 gInsidePainting = false;
|
||||
u8 gControlPainting = false;
|
||||
u8 gWaitingForRemotePainting = false;
|
||||
struct WarpNode gPaintingWarpNode = { 0 };
|
||||
|
||||
u16 level_control_timer(s32 timerOp) {
|
||||
switch (timerOp) {
|
||||
case TIMER_CONTROL_SHOW:
|
||||
|
@ -361,7 +360,17 @@ void set_mario_initial_action(struct MarioState *m, u32 spawnType, u32 actionArg
|
|||
set_mario_initial_cap_powerup(m);
|
||||
}
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
void init_mario_after_warp(void) {
|
||||
printf("===== init mario =====\n");
|
||||
printf("areaIdx = %d\n", sWarpDest.areaIdx);
|
||||
printf("arg = %d\n", sWarpDest.arg);
|
||||
printf("levelNum = %d\n", sWarpDest.levelNum);
|
||||
printf("nodeId = %d\n", sWarpDest.nodeId);
|
||||
printf("type = %d\n", sWarpDest.type);
|
||||
fflush(stdout);
|
||||
|
||||
struct ObjectWarpNode *spawnNode = area_get_warp_node(sWarpDest.nodeId);
|
||||
u32 marioSpawnType = get_mario_spawn_type(spawnNode->object);
|
||||
|
||||
|
@ -671,35 +680,7 @@ struct WarpNode *get_painting_warp_node(void) {
|
|||
return warpNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check is Mario has entered a painting, and if so, initiate a warp.
|
||||
*/
|
||||
void initiate_painting_warp(void) {
|
||||
if (gCurrentArea->paintingWarpNodes != NULL && gMarioState->floor != NULL) {
|
||||
struct WarpNode *pWarpNode = get_painting_warp_node();
|
||||
|
||||
if (pWarpNode != NULL) {
|
||||
if (gMarioState->action & ACT_FLAG_INTANGIBLE) {
|
||||
play_painting_eject_sound();
|
||||
} else if (pWarpNode->id != 0) {
|
||||
initiate_painting_warp_node(pWarpNode, false);
|
||||
gControlPainting = true;
|
||||
gWaitingForRemotePainting = (gNetworkType != NT_NONE);
|
||||
set_mario_action(gMarioState, ACT_DISAPPEARED, 0);
|
||||
gMarioState->marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void initiate_painting_warp_node(struct WarpNode *pWarpNode, u8 instant) {
|
||||
if (pWarpNode->id == 0) { return; }
|
||||
|
||||
gControlPainting = false;
|
||||
gWaitingForRemotePainting = false;
|
||||
gInsidePainting = true;
|
||||
|
||||
gPaintingWarpNode = *pWarpNode;
|
||||
static void initiate_painting_warp_node(struct WarpNode *pWarpNode, u8 instant) {
|
||||
struct WarpNode warpNode = *pWarpNode;
|
||||
|
||||
if (!(warpNode.destLevel & 0x80)) {
|
||||
|
@ -718,6 +699,26 @@ void initiate_painting_warp_node(struct WarpNode *pWarpNode, u8 instant) {
|
|||
func_sh_8024C89C(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check is Mario has entered a painting, and if so, initiate a warp.
|
||||
*/
|
||||
void initiate_painting_warp(void) {
|
||||
if (gCurrentArea->paintingWarpNodes != NULL && gMarioState->floor != NULL) {
|
||||
struct WarpNode *pWarpNode = get_painting_warp_node();
|
||||
|
||||
if (pWarpNode != NULL) {
|
||||
if (gMarioState->action & ACT_FLAG_INTANGIBLE) {
|
||||
play_painting_eject_sound();
|
||||
} else if (pWarpNode->id != 0) {
|
||||
initiate_painting_warp_node(pWarpNode, false);
|
||||
set_mario_action(gMarioState, ACT_DISAPPEARED, 0);
|
||||
gMarioState->marioObj->header.gfx.node.flags &= ~GRAPH_RENDER_ACTIVE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If there is not already a delayed warp, schedule one. The source node is
|
||||
* based on the warp operation and sometimes Mario's used object.
|
||||
|
@ -727,7 +728,6 @@ s16 level_trigger_warp(struct MarioState *m, s32 warpOp) {
|
|||
// only allow for local player
|
||||
if (m != &gMarioStates[0]) { return 0; }
|
||||
|
||||
gControlPainting = TRUE;
|
||||
s32 val04 = TRUE;
|
||||
|
||||
if (sDelayedWarpOp == WARP_OP_NONE) {
|
||||
|
@ -996,6 +996,29 @@ void basic_update(UNUSED s16 *arg) {
|
|||
}
|
||||
}
|
||||
|
||||
static void check_for_received_warp(void) {
|
||||
if (!gReceiveWarp) { return; }
|
||||
gReceiveWarp = FALSE;
|
||||
sWarpDest = gReceiveWarpDest;
|
||||
|
||||
if (!gControlledWarp) {
|
||||
// force well behaved state
|
||||
extern s16 gMenuMode;
|
||||
reset_dialog_render_state();
|
||||
level_set_transition(0, 0);
|
||||
sTransitionUpdate = NULL;
|
||||
gMenuMode = -1;
|
||||
gPauseScreenMode = 1;
|
||||
gSaveOptSelectIndex = 0;
|
||||
gMarioStates[0].action = (gMarioStates[0].pos[1] <= gMarioStates[0].waterLevel) ? ACT_WATER_IDLE : ACT_IDLE;
|
||||
gCameraMovementFlags &= ~CAM_MOVE_PAUSE_SCREEN;
|
||||
}
|
||||
|
||||
set_play_mode((sWarpDest.type == WARP_TYPE_CHANGE_LEVEL)
|
||||
? PLAY_MODE_CHANGE_LEVEL
|
||||
: PLAY_MODE_CHANGE_AREA);
|
||||
}
|
||||
|
||||
int gPressedStart = 0;
|
||||
|
||||
s32 play_mode_normal(void) {
|
||||
|
@ -1036,14 +1059,14 @@ s32 play_mode_normal(void) {
|
|||
set_play_mode(PLAY_MODE_CHANGE_LEVEL);
|
||||
} else {
|
||||
set_play_mode((gNetworkType != NT_NONE) ? PLAY_MODE_SYNC_LEVEL : PLAY_MODE_CHANGE_LEVEL);
|
||||
network_send_level_warp();
|
||||
network_send_level_warp(FALSE);
|
||||
}
|
||||
} else if (sTransitionTimer != 0) {
|
||||
if (sWarpDest.type == WARP_TYPE_NOT_WARPING || gCurrentArea->index == sWarpDest.areaIdx) {
|
||||
set_play_mode(PLAY_MODE_CHANGE_AREA);
|
||||
} else {
|
||||
set_play_mode((gNetworkType != NT_NONE) ? PLAY_MODE_SYNC_LEVEL : PLAY_MODE_CHANGE_AREA);
|
||||
network_send_level_warp();
|
||||
network_send_level_warp(FALSE);
|
||||
}
|
||||
} else if (pressed_pause()) {
|
||||
lower_background_noise(1);
|
||||
|
@ -1053,6 +1076,8 @@ s32 play_mode_normal(void) {
|
|||
}
|
||||
}
|
||||
|
||||
check_for_received_warp();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1073,7 +1098,7 @@ s32 play_mode_paused(void) {
|
|||
gSavedCourseNum = COURSE_NONE;
|
||||
}
|
||||
set_play_mode((gNetworkType != NT_NONE) ? PLAY_MODE_SYNC_LEVEL : PLAY_MODE_CHANGE_LEVEL);
|
||||
network_send_level_warp();
|
||||
network_send_level_warp(FALSE);
|
||||
} else if (gPauseScreenMode == 3) {
|
||||
// We should only be getting "int 3" to here
|
||||
initiate_warp(LEVEL_CASTLE, 1, 0x1F, 0);
|
||||
|
@ -1087,6 +1112,7 @@ s32 play_mode_paused(void) {
|
|||
}
|
||||
|
||||
s32 play_mode_sync_level(void) {
|
||||
check_for_received_warp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1123,8 +1149,6 @@ void level_set_transition(s16 length, void (*updateFunction)(s16 *)) {
|
|||
* Play the transition and then return to normal play mode.
|
||||
*/
|
||||
s32 play_mode_change_area(void) {
|
||||
network_on_init_level();
|
||||
|
||||
//! This maybe was supposed to be sTransitionTimer == -1? sTransitionUpdate
|
||||
// is never set to -1.
|
||||
if (sTransitionUpdate == (void (*)(s16 *)) - 1) {
|
||||
|
@ -1222,7 +1246,6 @@ s32 update_level(void) {
|
|||
|
||||
s32 init_level(void) {
|
||||
reset_dialog_render_state();
|
||||
network_on_init_level();
|
||||
|
||||
s32 val4 = 0;
|
||||
|
||||
|
|
|
@ -69,10 +69,6 @@ extern struct CreditsEntry *gCurrCreditsEntry;
|
|||
|
||||
extern struct MarioState gMarioStates[];
|
||||
extern struct MarioState *gMarioState;
|
||||
extern u8 gInsidePainting;
|
||||
extern u8 gControlPainting;
|
||||
extern u8 gWaitingForRemotePainting;
|
||||
extern struct WarpNode gPaintingWarpNode;
|
||||
|
||||
extern s16 sCurrPlayMode;
|
||||
extern u16 D_80339ECA;
|
||||
|
@ -140,7 +136,6 @@ void basic_update(UNUSED s16 *arg);
|
|||
|
||||
s32 init_level(void);
|
||||
|
||||
void initiate_painting_warp_node(struct WarpNode *pWarpNode, u8 instant);
|
||||
void star_select_finish_selection(void);
|
||||
|
||||
#endif // LEVEL_UPDATE_H
|
||||
|
|
|
@ -2083,8 +2083,6 @@ static void init_single_mario(struct MarioState* m) {
|
|||
}
|
||||
|
||||
void init_mario(void) {
|
||||
gInsidePainting = false;
|
||||
|
||||
for (int i = 0; i < MAX_PLAYERS; i++) {
|
||||
gMarioStates[i].playerIndex = i;
|
||||
init_single_mario(&gMarioStates[i]);
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
static struct Object *sStarSelectorModels[8];
|
||||
|
||||
// The act the course is loaded as, affects whether some objects spawn.
|
||||
static s8 sLoadedActNum;
|
||||
s8 sLoadedActNum;
|
||||
|
||||
// Number of obtained stars, excluding the coin star.
|
||||
static u8 sObtainedStars;
|
||||
|
@ -54,6 +54,8 @@ s8 sSelectableStarIndex = 0;
|
|||
// Act Selector menu timer that keeps counting until you choose an act.
|
||||
static s32 sActSelectorMenuTimer = 0;
|
||||
|
||||
extern u8 gControlledWarp;
|
||||
|
||||
/**
|
||||
* Act Selector Star Type Loop Action
|
||||
* Defines a select type for a star in the act selector.
|
||||
|
@ -156,7 +158,10 @@ void bhv_act_selector_init(void) {
|
|||
}
|
||||
|
||||
render_100_coin_star(stars);
|
||||
gInsidePainting = TRUE;
|
||||
|
||||
if (gControlledWarp) {
|
||||
network_send_inside_painting(TRUE, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -175,7 +180,11 @@ void bhv_act_selector_loop(void) {
|
|||
// Sometimes, stars are not selectable even if they appear on the screen.
|
||||
// This code filters selectable and non-selectable stars.
|
||||
sSelectedActIndex = 0;
|
||||
if (gControlPainting) { handle_menu_scrolling(MENU_SCROLL_HORIZONTAL, &sSelectableStarIndex, 0, sObtainedStars); }
|
||||
if (gControlledWarp) {
|
||||
s8 oldIndex = sSelectableStarIndex;
|
||||
handle_menu_scrolling(MENU_SCROLL_HORIZONTAL, &sSelectableStarIndex, 0, sObtainedStars);
|
||||
if (oldIndex != sSelectableStarIndex) { network_send_inside_painting(FALSE, FALSE); }
|
||||
}
|
||||
starIndexCounter = sSelectableStarIndex;
|
||||
for (i = 0; i < sVisibleStars; i++) {
|
||||
// Can the star be selected (is it either already completed or the first non-completed mission)
|
||||
|
@ -189,7 +198,11 @@ void bhv_act_selector_loop(void) {
|
|||
}
|
||||
} else {
|
||||
// If all stars are collected then they are all selectable.
|
||||
if (gControlPainting) { handle_menu_scrolling(MENU_SCROLL_HORIZONTAL, &sSelectableStarIndex, 0, sVisibleStars - 1); }
|
||||
if (gControlledWarp) {
|
||||
s8 oldIndex = sSelectableStarIndex;
|
||||
handle_menu_scrolling(MENU_SCROLL_HORIZONTAL, &sSelectableStarIndex, 0, sVisibleStars - 1);
|
||||
if (oldIndex != sSelectableStarIndex) { network_send_inside_painting(FALSE, FALSE); }
|
||||
}
|
||||
sSelectedActIndex = sSelectableStarIndex;
|
||||
}
|
||||
|
||||
|
@ -289,7 +302,7 @@ void print_act_selector_strings(void) {
|
|||
create_dl_ortho_matrix();
|
||||
|
||||
// display disclaimer that the other player has to select
|
||||
if (!gControlPainting || gWaitingForRemotePainting) {
|
||||
if (!gControlledWarp) {
|
||||
gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
|
||||
u8 a = ((gGlobalTimer % 24) >= 12) ? 160 : 130;
|
||||
gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, a);
|
||||
|
@ -426,8 +439,7 @@ s32 lvl_init_act_selector_values_and_stars(UNUSED s32 arg, UNUSED s32 unused) {
|
|||
* Also updates objects and returns act number selected after is chosen.
|
||||
*/
|
||||
s32 lvl_update_obj_and_load_act_button_actions(UNUSED s32 arg, UNUSED s32 unused) {
|
||||
u8 allowSelection = (gControlPainting && !gWaitingForRemotePainting);
|
||||
if (sActSelectorMenuTimer >= 11 && allowSelection) {
|
||||
if (gControlledWarp && sActSelectorMenuTimer >= 11) {
|
||||
// If any of these buttons are pressed, play sound and go to course act
|
||||
#ifndef VERSION_EU
|
||||
if ((gPlayer3Controller->buttonPressed & A_BUTTON)
|
||||
|
@ -458,8 +470,5 @@ void star_select_finish_selection(void) {
|
|||
}
|
||||
gDialogCourseActNum = sSelectedActIndex + 1;
|
||||
|
||||
gInsidePainting = FALSE;
|
||||
if (gControlPainting) {
|
||||
network_send_inside_painting(TRUE);
|
||||
}
|
||||
if (gControlledWarp) { network_send_inside_painting(FALSE, TRUE); }
|
||||
}
|
||||
|
|
34
src/pc/debuglog.h
Normal file
34
src/pc/debuglog.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include "pc/network/network.h"
|
||||
|
||||
#if defined(DEBUG) && !defined(DISABLE_MODULE_LOG)
|
||||
static void debuglog_print_timestamp(void) {
|
||||
time_t ltime = time(NULL);
|
||||
char* str = asctime(localtime(<ime));
|
||||
printf("%.*s", (int)strlen(str) - 1, str);
|
||||
}
|
||||
|
||||
static void debuglog_print_network_type(void) {
|
||||
printf(" [%s] ", NETWORKTYPESTR);
|
||||
}
|
||||
|
||||
static void debuglog_print_short_filename(char* filename) {
|
||||
char* last = strrchr(filename, '/');
|
||||
if (last != NULL) {
|
||||
printf("%s: ", last + 1);
|
||||
} else {
|
||||
printf("???: ");
|
||||
}
|
||||
}
|
||||
|
||||
static void debuglog_print_log(char* filename) {
|
||||
debuglog_print_timestamp();
|
||||
debuglog_print_network_type();
|
||||
debuglog_print_short_filename(filename);
|
||||
}
|
||||
|
||||
#define LOG_INFO(...) ( debuglog_print_log(__FILE__), printf(__VA_ARGS__), printf("\n") )
|
||||
#else
|
||||
#define LOG_INFO(...)
|
||||
#endif
|
|
@ -6,7 +6,6 @@
|
|||
#include "pc/configfile.h"
|
||||
|
||||
// Mario 64 specific externs
|
||||
extern u8 gInsidePainting;
|
||||
extern s16 sCurrPlayMode;
|
||||
|
||||
enum NetworkType gNetworkType = NT_NONE;
|
||||
|
@ -105,9 +104,7 @@ void network_update(void) {
|
|||
}
|
||||
|
||||
// figure out which update loop to run
|
||||
if (gInsidePainting && sCurrPlayMode == PLAY_MODE_CHANGE_LEVEL) {
|
||||
network_update_inside_painting();
|
||||
} else if (sCurrPlayMode == PLAY_MODE_NORMAL || sCurrPlayMode == PLAY_MODE_PAUSED) {
|
||||
if (sCurrPlayMode == PLAY_MODE_NORMAL || sCurrPlayMode == PLAY_MODE_PAUSED) {
|
||||
network_update_player();
|
||||
network_update_objects();
|
||||
}
|
||||
|
|
|
@ -131,12 +131,11 @@ void network_send_spawn_star(struct Object* o, u8 starType, f32 x, f32 y, f32 z,
|
|||
void network_receive_spawn_star(struct Packet* p);
|
||||
|
||||
// packet_level_warp.c
|
||||
void network_send_level_warp(void);
|
||||
void network_send_level_warp(u8 done);
|
||||
void network_receive_level_warp(struct Packet* p);
|
||||
|
||||
// packet_inside_painting.c
|
||||
void network_update_inside_painting(void);
|
||||
void network_send_inside_painting(bool reliable);
|
||||
void network_send_inside_painting(u8 startOfEvent, u8 endOfEvent);
|
||||
void network_receive_inside_painting(struct Packet* p);
|
||||
|
||||
// packet_collect_star.c
|
||||
|
|
|
@ -2,81 +2,83 @@
|
|||
#include "../network.h"
|
||||
#include "src/game/level_update.h"
|
||||
#include "src/game/area.h"
|
||||
#define DISABLE_MODULE_LOG
|
||||
#include "pc/debuglog.h"
|
||||
|
||||
extern u8 gControlledWarp;
|
||||
|
||||
extern struct WarpNode gPaintingWarpNode;
|
||||
extern u8 sSelectableStarIndex;
|
||||
extern u8 sSelectedActIndex;
|
||||
extern s8 sLoadedActNum;
|
||||
|
||||
struct PacketDataInsidePainting {
|
||||
u8 insidePainting;
|
||||
u8 controlPainting;
|
||||
#pragma pack(1)
|
||||
struct PacketInsidePaintingData {
|
||||
u8 seqId;
|
||||
u8 eventId;
|
||||
u8 starIndex;
|
||||
u8 actIndex;
|
||||
u8 loadedActNum;
|
||||
};
|
||||
|
||||
static clock_t lastSentTime = 0;
|
||||
static float minUpdateRate = 5.0f;
|
||||
static struct PacketDataInsidePainting lastSentData = { 0 };
|
||||
static u8 eventId = 0;
|
||||
static u8 remoteFinishedEventId = (u8)-1;
|
||||
|
||||
static void populate_packet_data(struct PacketDataInsidePainting* data) {
|
||||
data->insidePainting = gInsidePainting;
|
||||
data->controlPainting = gControlPainting;
|
||||
static u8 seqId = 0;
|
||||
static u8 remoteLastSeqId = (u8)-1;
|
||||
|
||||
static void populate_packet_data(struct PacketInsidePaintingData* data) {
|
||||
data->seqId = seqId;
|
||||
data->eventId = eventId;
|
||||
data->starIndex = sSelectableStarIndex;
|
||||
data->actIndex = sSelectedActIndex;
|
||||
data->loadedActNum = sLoadedActNum;
|
||||
}
|
||||
|
||||
void network_send_inside_painting(bool reliable) {
|
||||
struct PacketDataInsidePainting data = { 0 };
|
||||
void network_send_inside_painting(u8 startOfEvent, u8 endOfEvent) {
|
||||
if (startOfEvent) { eventId++; }
|
||||
struct PacketInsidePaintingData data = { 0 };
|
||||
populate_packet_data(&data);
|
||||
|
||||
struct Packet p;
|
||||
packet_init(&p, PACKET_INSIDE_PAINTING, reliable);
|
||||
packet_write(&p, &data, sizeof(struct PacketDataInsidePainting));
|
||||
packet_init(&p, PACKET_INSIDE_PAINTING, true);
|
||||
packet_write(&p, &data, sizeof(struct PacketInsidePaintingData));
|
||||
network_send(&p);
|
||||
|
||||
lastSentData = data;
|
||||
lastSentTime = clock();
|
||||
seqId++;
|
||||
}
|
||||
|
||||
void network_receive_inside_painting(struct Packet* p) {
|
||||
struct PacketDataInsidePainting remote = { 0 };
|
||||
packet_read(p, &remote, sizeof(struct PacketDataInsidePainting));
|
||||
struct PacketInsidePaintingData local = { 0 };
|
||||
populate_packet_data(&local);
|
||||
|
||||
if (gNetworkType == NT_CLIENT && gControlPainting && remote.controlPainting) {
|
||||
// we both think we should control the painting, host wins the tie
|
||||
gControlPainting = false;
|
||||
struct PacketInsidePaintingData remote = { 0 };
|
||||
packet_read(p, &remote, sizeof(struct PacketInsidePaintingData));
|
||||
|
||||
// de-dup
|
||||
if (remote.seqId == remoteLastSeqId) {
|
||||
LOG_INFO("we've seen this packet, escape!");
|
||||
return;
|
||||
}
|
||||
remoteLastSeqId = remote.seqId;
|
||||
if (remote.eventId == remoteFinishedEventId || (remote.eventId == remoteFinishedEventId - 1)) {
|
||||
LOG_INFO("we've finished this event, escape!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gControlPainting && remote.controlPainting) {
|
||||
// update star/act index to show the one in control's selection
|
||||
// two-player hack: gControlledWarp is a bool instead of an index
|
||||
if (gControlledWarp) {
|
||||
LOG_INFO("this should never happen, received inside_painting when gControlledWarp");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO("received update");
|
||||
eventId = remote.eventId;
|
||||
sSelectableStarIndex = remote.starIndex;
|
||||
sSelectedActIndex = remote.actIndex;
|
||||
}
|
||||
sLoadedActNum = remote.loadedActNum;
|
||||
|
||||
if (gControlPainting && !remote.controlPainting) {
|
||||
// remote is well behaved now, we can control the painting
|
||||
gWaitingForRemotePainting = false;
|
||||
}
|
||||
|
||||
if (gControlPainting && !remote.controlPainting && !gInsidePainting && remote.insidePainting) {
|
||||
// we're in control and no longer in the painting, let remote know
|
||||
network_send_inside_painting(false);
|
||||
}
|
||||
|
||||
if (!gControlPainting && remote.controlPainting && !remote.insidePainting) {
|
||||
// remote is in control and in game, we should be too
|
||||
star_select_finish_selection();
|
||||
if (sLoadedActNum != 0) {
|
||||
LOG_INFO("finished with painting");
|
||||
remoteFinishedEventId = remote.eventId;
|
||||
}
|
||||
}
|
||||
|
||||
void network_update_inside_painting(void) {
|
||||
struct PacketDataInsidePainting data = { 0 };
|
||||
populate_packet_data(&data);
|
||||
int compareData = memcmp(&data, &lastSentData, sizeof(struct PacketDataInsidePainting));
|
||||
|
||||
float timeSinceSend = (clock() - lastSentTime) / CLOCKS_PER_SEC;
|
||||
|
||||
if (compareData != 0 || timeSinceSend > minUpdateRate) {
|
||||
network_send_inside_painting(timeSinceSend > 5);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,73 +1,114 @@
|
|||
#include <stdio.h>
|
||||
#include "../network.h"
|
||||
#include "src/game/level_update.h"
|
||||
#include "src/game/area.h"
|
||||
#include "src/game/ingame_menu.h"
|
||||
#include "sm64.h"
|
||||
#include "../network.h"
|
||||
#include "game/level_update.h"
|
||||
#include "game/area.h"
|
||||
#include "game/ingame_menu.h"
|
||||
#define DISABLE_MODULE_LOG
|
||||
#include "pc/debuglog.h"
|
||||
|
||||
int matchCount = 0;
|
||||
static u8 eventId = 0;
|
||||
static u8 remoteFinishedEventId = (u8)-1;
|
||||
|
||||
extern s16 gMenuMode;
|
||||
static u8 seqId = 0;
|
||||
static u8 remoteLastSeqId = (u8)-1;
|
||||
|
||||
void network_send_level_warp(void) {
|
||||
struct Packet p;
|
||||
packet_init(&p, PACKET_LEVEL_WARP, true);
|
||||
packet_write(&p, &sCurrPlayMode, sizeof(s16));
|
||||
packet_write(&p, &sWarpDest, sizeof(struct WarpDest));
|
||||
extern u8 gControlledWarp; // two-player hack
|
||||
extern u8 gReceiveWarp;
|
||||
extern struct WarpDest gReceiveWarpDest;
|
||||
|
||||
network_send(&p);
|
||||
struct WarpDest savedWarpNode = { 0 };
|
||||
|
||||
#pragma pack(1)
|
||||
struct PacketLevelWarpData {
|
||||
u8 seqId;
|
||||
u8 eventId;
|
||||
u8 done;
|
||||
u8 controlledWarp;
|
||||
struct WarpDest warpDest;
|
||||
};
|
||||
|
||||
static void populate_packet_data(struct PacketLevelWarpData* data, bool done) {
|
||||
data->seqId = seqId;
|
||||
data->eventId = eventId;
|
||||
data->done = done;
|
||||
data->controlledWarp = gControlledWarp;
|
||||
data->warpDest = savedWarpNode;
|
||||
}
|
||||
|
||||
static void force_well_behaved_state(void) {
|
||||
reset_dialog_render_state();
|
||||
level_set_transition(0, 0);
|
||||
gMenuMode = -1;
|
||||
gPauseScreenMode = 1;
|
||||
gSaveOptSelectIndex = 0;
|
||||
gMarioStates[0].action = (gMarioStates[0].pos[1] <= (gMarioStates[0].waterLevel - 100)) ? ACT_WATER_IDLE : ACT_IDLE;
|
||||
gCameraMovementFlags &= ~CAM_MOVE_PAUSE_SCREEN;
|
||||
void network_send_level_warp(u8 done) {
|
||||
if (!done) {
|
||||
savedWarpNode = sWarpDest;
|
||||
gControlledWarp = true;
|
||||
eventId++;
|
||||
LOG_INFO("new event [%d]!", eventId);
|
||||
}
|
||||
|
||||
struct PacketLevelWarpData data = { 0 };
|
||||
populate_packet_data(&data, done);
|
||||
|
||||
struct Packet p;
|
||||
packet_init(&p, PACKET_LEVEL_WARP, true);
|
||||
packet_write(&p, &data, sizeof(struct PacketLevelWarpData));
|
||||
network_send(&p);
|
||||
|
||||
seqId++;
|
||||
}
|
||||
|
||||
static void do_warp(void) {
|
||||
gReceiveWarpDest = savedWarpNode;
|
||||
gReceiveWarp = TRUE;
|
||||
}
|
||||
|
||||
void network_receive_level_warp(struct Packet* p) {
|
||||
s16 remotePlayMode;
|
||||
struct WarpDest remoteWarpDest;
|
||||
struct PacketLevelWarpData remote = { 0 };
|
||||
packet_read(p, &remote, sizeof(struct PacketLevelWarpData));
|
||||
|
||||
packet_read(p, &remotePlayMode, sizeof(s16));
|
||||
packet_read(p, &remoteWarpDest, sizeof(struct WarpDest));
|
||||
|
||||
bool matchingDest = memcmp(&remoteWarpDest, &sWarpDest, sizeof(struct WarpDest)) == 0;
|
||||
|
||||
if (remotePlayMode == PLAY_MODE_SYNC_LEVEL && (sCurrPlayMode == PLAY_MODE_NORMAL || sCurrPlayMode == PLAY_MODE_PAUSED)) {
|
||||
if (remoteWarpDest.type == WARP_TYPE_NOT_WARPING) { return; }
|
||||
sCurrPlayMode = PLAY_MODE_SYNC_LEVEL;
|
||||
sWarpDest = remoteWarpDest;
|
||||
force_well_behaved_state();
|
||||
network_send_level_warp();
|
||||
// de-dup
|
||||
if (remote.seqId == remoteLastSeqId) {
|
||||
LOG_INFO("we've seen this packet, escape!");
|
||||
return;
|
||||
}
|
||||
remoteLastSeqId = remote.seqId;
|
||||
LOG_INFO("rx event [%d] last [%d]!", remote.eventId, remoteFinishedEventId);
|
||||
if (remote.eventId == remoteFinishedEventId || (remote.eventId == remoteFinishedEventId - 1)) {
|
||||
LOG_INFO("we've finished this event, escape!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (remotePlayMode == PLAY_MODE_SYNC_LEVEL && sCurrPlayMode == PLAY_MODE_SYNC_LEVEL) {
|
||||
if (matchingDest) {
|
||||
switch (sWarpDest.type) {
|
||||
case WARP_TYPE_CHANGE_AREA: sCurrPlayMode = PLAY_MODE_CHANGE_AREA; break;
|
||||
case WARP_TYPE_CHANGE_LEVEL: sCurrPlayMode = PLAY_MODE_CHANGE_LEVEL; break;
|
||||
}
|
||||
if (gNetworkType == NT_SERVER) {
|
||||
if (sCurrPlayMode != PLAY_MODE_SYNC_LEVEL) {
|
||||
// client initiated warp
|
||||
LOG_INFO("client initiated warp!");
|
||||
gControlledWarp = FALSE;
|
||||
savedWarpNode = remote.warpDest;
|
||||
eventId = remote.eventId;
|
||||
remoteFinishedEventId = remote.eventId;
|
||||
LOG_INFO("finished event [%d]!", remote.eventId);
|
||||
do_warp();
|
||||
network_send_level_warp(TRUE);
|
||||
return;
|
||||
} else if (remote.done) {
|
||||
// client done with warp
|
||||
LOG_INFO("client is done with warp, lets-a-go!");
|
||||
remoteFinishedEventId = remote.eventId;
|
||||
do_warp();
|
||||
return;
|
||||
} else {
|
||||
if (gNetworkType == NT_CLIENT) {
|
||||
if (remoteWarpDest.type == WARP_TYPE_NOT_WARPING) { return; }
|
||||
// two-player hack: would need to use player index as priority
|
||||
sWarpDest = remoteWarpDest;
|
||||
}
|
||||
}
|
||||
network_send_level_warp();
|
||||
LOG_INFO("client initiated warp, but server is already warping!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ((remotePlayMode == PLAY_MODE_CHANGE_LEVEL || remotePlayMode == PLAY_MODE_CHANGE_AREA) && sCurrPlayMode == PLAY_MODE_SYNC_LEVEL) {
|
||||
if (remoteWarpDest.type == WARP_TYPE_NOT_WARPING) { return; }
|
||||
switch (sWarpDest.type) {
|
||||
case WARP_TYPE_CHANGE_AREA: sCurrPlayMode = PLAY_MODE_CHANGE_AREA; break;
|
||||
case WARP_TYPE_CHANGE_LEVEL: sCurrPlayMode = PLAY_MODE_CHANGE_LEVEL; break;
|
||||
}
|
||||
}
|
||||
assert(gNetworkType == NT_CLIENT);
|
||||
|
||||
// server initiated warp
|
||||
LOG_INFO("server initiated warp!");
|
||||
gControlledWarp = !remote.controlledWarp; // two-player hack
|
||||
savedWarpNode = remote.warpDest;
|
||||
eventId = remote.eventId;
|
||||
remoteFinishedEventId = remote.eventId;
|
||||
LOG_INFO("finished event [%d]!", remote.eventId);
|
||||
do_warp();
|
||||
network_send_level_warp(TRUE);
|
||||
}
|
||||
|
|
|
@ -83,6 +83,7 @@ bool network_sync_object_initialized(struct Object* o) {
|
|||
}
|
||||
|
||||
void network_clear_sync_objects(void) {
|
||||
network_on_init_level();
|
||||
for (u16 i = 0; i < MAX_SYNC_OBJECTS; i++) {
|
||||
network_forget_sync_object(&gSyncObjects[i]);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue