From aae3e57bb09b833ef1dd6d3a0b2aed313e6ba9e7 Mon Sep 17 00:00:00 2001 From: Isaac0-dev <62234577+Isaac0-dev@users.noreply.github.com> Date: Wed, 11 Jun 2025 22:34:12 +1000 Subject: [PATCH] make multiple controllers work --- src/game/game_init.c | 2 +- src/game/local_multiplayer.c | 42 +++++++++++++++++++++- src/game/local_multiplayer.h | 2 ++ src/game/rendering_graph_node.c | 2 -- src/pc/controller/controller_entry_point.c | 9 +++-- src/pc/controller/controller_sdl2.c | 27 ++++++++++---- src/pc/controller/controller_system.c | 26 +++++++++----- src/pc/controller/controller_system.h | 10 ++++-- src/pc/djui/djui_hud_utils.c | 12 +++++-- src/pc/djui/djui_panel_splitscreen.c | 20 ++++------- src/pc/network/network.c | 11 ++++-- src/pc/network/network_player.c | 7 ++-- 12 files changed, 125 insertions(+), 45 deletions(-) diff --git a/src/game/game_init.c b/src/game/game_init.c index 584e90901..c47d23265 100644 --- a/src/game/game_init.c +++ b/src/game/game_init.c @@ -575,7 +575,7 @@ void init_controllers(void) { } } - for (u8 i = 0; i < gNumPlayersLocal; i++) { + for (u8 i = 0; i < POSSIBLE_NUM_PLAYERS; i++) { gControllers[i].controllerData = &gControllerPads[i]; } diff --git a/src/game/local_multiplayer.c b/src/game/local_multiplayer.c index ec029e401..2386bbd4a 100644 --- a/src/game/local_multiplayer.c +++ b/src/game/local_multiplayer.c @@ -211,6 +211,46 @@ void set_screen_rendering(u16 index) { if (gNumPlayersLocal == 3 && index == 2) { gSw = 2; - aspect_mask = 1.f; + aspect_mask = 2.f; } } + +bool dummy_initialize(UNUSED enum NetworkType type, UNUSED bool reconnecting) { return true; } +s64 dummy_get_id(UNUSED u8 localIndex) { return 0; } +char* dummy_get_id_str(UNUSED u8 localIndex) { return "dummy_id"; } +void dummy_save_id(UNUSED u8 localIndex, UNUSED s64 networkId) {} +void dummy_clear_id(UNUSED u8 localIndex) {} +void* dummy_dup_addr(UNUSED u8 localIndex) { return NULL; } +bool dummy_match_addr(void* addr1, void* addr2) { return addr1 == addr2; } +void dummy_update(void) {} +int dummy_send(UNUSED u8 localIndex, UNUSED void* addr, UNUSED u8* data, UNUSED u16 dataLength) { return 0; } +void dummy_get_lobby_id(char* destination, u32 destLength) { + if (destLength > 0) { + strncpy(destination, "dummy_lobby", destLength - 1); + destination[destLength - 1] = '\0'; + } +} +void dummy_get_lobby_secret(char* destination, u32 destLength) { + if (destLength > 0) { + strncpy(destination, "dummy_secret", destLength - 1); + destination[destLength - 1] = '\0'; + } +} +void dummy_shutdown(UNUSED bool reconnecting) {} + +struct NetworkSystem gNetworkSystemDummy = { + .initialize = dummy_initialize, + .get_id = dummy_get_id, + .get_id_str = dummy_get_id_str, + .save_id = dummy_save_id, + .clear_id = dummy_clear_id, + .dup_addr = dummy_dup_addr, + .match_addr = dummy_match_addr, + .update = dummy_update, + .send = dummy_send, + .get_lobby_id = dummy_get_lobby_id, + .get_lobby_secret = dummy_get_lobby_secret, + .shutdown = dummy_shutdown, + .requireServerBroadcast = false, + .name = "Offline", +}; diff --git a/src/game/local_multiplayer.h b/src/game/local_multiplayer.h index 18189c52a..35352a5b2 100644 --- a/src/game/local_multiplayer.h +++ b/src/game/local_multiplayer.h @@ -30,6 +30,8 @@ extern struct HudDisplay gHudDisplays[]; extern struct Controller gSharedController; extern struct Controller *gSharedCtr; +extern struct NetworkSystem gNetworkSystemDummy; + typedef struct { u8 w, h; f32 a; } ScreenSize; struct GfxLoad { diff --git a/src/game/rendering_graph_node.c b/src/game/rendering_graph_node.c index 930bf9a9e..48f9294d5 100644 --- a/src/game/rendering_graph_node.c +++ b/src/game/rendering_graph_node.c @@ -360,7 +360,6 @@ void patch_mtx_interpolated(f32 delta) { delta_interpolate_vec3f(focusInterp, sCameraNode->prevFocus, sCameraNode->focus, delta); mtxf_lookat(camInterp.m, posInterp, focusInterp, sCameraNode->roll); mtxf_to_mtx(&camInterp, camInterp.m); - printf("%d interp (%d, %d), curr: (%d, %d), interp: (%d, %d)\n", gCurrPlayer, (int) sCameraNode->prevPos[0], (int) sCameraNode->prevPos[2], (int) sCameraNode->pos[0], (int) sCameraNode->pos[2], (int) posInterp[0], (int) posInterp[2]); // printf("%d interp (%d, %d), real: (%d, %d)\n", gCurrPlayer, (int) sCameraNode->focus[0], (int) sCameraNode->focus[2], (int) sCameraNode->prevFocus[0], (int) sCameraNode->prevFocus[2]); } @@ -615,7 +614,6 @@ static void geo_process_camera(struct GraphNodeCamera *node) { vec3f_copy(node->pos, sCameraNodes[gCurrPlayer].pos); vec3f_copy(node->focus, sCameraNodes[gCurrPlayer].focus); - printf("%d cam (%d, %d), real: (%d, %d)\n", gCurrPlayer, (int) node->pos[0], (int) node->pos[2], (int) gLakituState.pos[0], (int) gLakituState.pos[2]); // printf("%d cam (%d, %d), real: (%d, %d)\n", gCurrPlayer, (int) node->focus[0], (int) node->focus[2], (int) gLakituState.focus[0], (int) gLakituState.focus[2]); vec3f_copy(node->prevPos, node->pos); diff --git a/src/pc/controller/controller_entry_point.c b/src/pc/controller/controller_entry_point.c index b9858a56f..4524b83f8 100644 --- a/src/pc/controller/controller_entry_point.c +++ b/src/pc/controller/controller_entry_point.c @@ -69,21 +69,24 @@ void osContGetReadData(OSContPad *pad) { void osContGetReadDataIndex(OSContPad *pad, int i) { osContResetPad(pad); - struct ControllerPlace *cntr = &gPlayerControllerInfos[i]; - if (!cntr->connected) { return; } + struct ControllerInfo *cntr = &gPlayerControllerInfos[i]; if (cntr->type >= ARRAY_COUNT(controller_implementations)) { return; } + gReadingController = cntr; controller_implementations[cntr->type]->read(pad); + gReadingController = NULL; } void osContGetReadDataIndexNoReset(OSContPad *pad, int i) { - struct ControllerPlace *cntr = &gPlayerControllerInfos[i]; + struct ControllerInfo *cntr = &gPlayerControllerInfos[i]; if (!cntr->connected) { return; } if (cntr->type >= ARRAY_COUNT(controller_implementations)) { return; } + gReadingController = cntr; controller_implementations[cntr->type]->read(pad); + gReadingController = NULL; } u32 controller_get_raw_key(void) { diff --git a/src/pc/controller/controller_sdl2.c b/src/pc/controller/controller_sdl2.c index 373065415..d9b64586c 100644 --- a/src/pc/controller/controller_sdl2.c +++ b/src/pc/controller/controller_sdl2.c @@ -16,10 +16,12 @@ #include "controller_api.h" #include "controller_sdl.h" #include "controller_mouse.h" +#include "controller_system.h" #include "pc/pc_main.h" #include "pc/configfile.h" #include "pc/platform.h" #include "pc/fs/fs.h" +#include "game/local_multiplayer.h" #include "game/level_update.h" #include "game/first_person_cam.h" @@ -205,6 +207,14 @@ static void controller_sdl_read(OSContPad *pad) { if (configDisableGamepads) { return; } + u8 readController = configGamepadNumber; + if (gNumPlayersLocal > 1 && gReadingController) { + readController = gReadingController->index; + sdl_cntrl = gReadingController->sdl_cntrl; + sdl_haptic = gReadingController->sdl_haptic; + sdl_joystick = gReadingController->sdl_joystick; + } + SDL_GameControllerUpdate(); if (sdl_cntrl != NULL && !SDL_GameControllerGetAttached(sdl_cntrl)) { @@ -214,21 +224,26 @@ static void controller_sdl_read(OSContPad *pad) { sdl_haptic = NULL; } - if ((!sdl_cntrl && !sdl_joystick) || last_gamepad != configGamepadNumber) { + if ((!sdl_cntrl && !sdl_joystick)) { if (sdl_haptic) { SDL_HapticClose(sdl_haptic); sdl_haptic = NULL; } if (sdl_cntrl) { SDL_GameControllerClose(sdl_cntrl); sdl_cntrl = NULL; } if (sdl_joystick) { SDL_JoystickClose(sdl_joystick); sdl_joystick = NULL; } - last_gamepad = configGamepadNumber; - if (SDL_IsGameController(configGamepadNumber)) { - sdl_cntrl = SDL_GameControllerOpen(configGamepadNumber); + last_gamepad = readController; + if (SDL_IsGameController(readController)) { + sdl_cntrl = SDL_GameControllerOpen(readController); if (sdl_cntrl != NULL) { - sdl_haptic = controller_sdl_init_haptics(configGamepadNumber); + sdl_haptic = controller_sdl_init_haptics(readController); } } else { - sdl_joystick = SDL_JoystickOpen(configGamepadNumber); + sdl_joystick = SDL_JoystickOpen(readController); if (!sdl_joystick) { return; } } } + if (gReadingController) { + gReadingController->sdl_cntrl = sdl_cntrl; + gReadingController->sdl_haptic = sdl_haptic; + gReadingController->sdl_joystick = sdl_joystick; + } int16_t leftx = 0, lefty = 0, rightx = 0, righty = 0; int16_t ltrig = 0, rtrig = 0; diff --git a/src/pc/controller/controller_system.c b/src/pc/controller/controller_system.c index a1eb73195..6c5edc656 100644 --- a/src/pc/controller/controller_system.c +++ b/src/pc/controller/controller_system.c @@ -8,6 +8,7 @@ #include "pc/djui/djui_panel_splitscreen.h" #include "engine/math_util.h" #include "pc/network/network.h" +#include "pc/pc_main.h" // Constants for clarity #define MAX_GAMEPADS 10 @@ -17,8 +18,10 @@ char gGamepadChoicesBuffer[MAX_GAMEPADS][MAX_NAME_LEN] = { 0 }; char *gGamepadChoices[MAX_GAMEPADS] = { 0 }; int gNumJoys = 0; +bool gSuppressConnectedPopup = true; +struct ControllerInfo *gReadingController = NULL; -struct ControllerPlace gPlayerControllerInfos[POSSIBLE_NUM_PLAYERS] = {{ +struct ControllerInfo gPlayerControllerInfos[POSSIBLE_NUM_PLAYERS] = {{ .index = 0, .type = 1, // Make player 1 the keyboard by default .connected = true @@ -42,9 +45,8 @@ void controller_update_gamepad_choices() { if (gNumJoys <= 0) { gNumJoys = 1; } if (gNumJoys > MAX_GAMEPADS) { gNumJoys = MAX_GAMEPADS; } - gGamepadChoices[0] = gGamepadChoicesBuffer[0]; - for (int i = 0; i < gNumJoys; i++) { + gGamepadChoices[i] = gGamepadChoicesBuffer[i]; const char *joystickName = SDL_JoystickNameForIndex(i); snprintf(gGamepadChoices[i], MAX_NAME_LEN, "%s", joystickName ? joystickName : "Unknown"); @@ -69,21 +71,29 @@ void controller_update_gamepad_choices() { void controller_update_controller_count() { static u16 prevNumPlayers = 1; gNumPlayersLocal = clamp(SDL_NumJoysticks() + 1, 1, POSSIBLE_NUM_PLAYERS); - for (u16 i = 1; i < POSSIBLE_NUM_PLAYERS; i++) { - struct ControllerPlace *c = &gPlayerControllerInfos[i]; - if (c->type == 1) { + for (u16 i = 0, index = 0; i < gNumPlayersLocal; i++) { + struct ControllerInfo *c = &gPlayerControllerInfos[i]; + if (gNumPlayersLocal == 1 && c->type == 1 && i != 0) { ++gNumPlayersLocal; - break; } + c->connected = true; + c->index = index; + if (c->type == 1) { continue; } + index++; } + + gSuppressConnectedPopup = gGameInited; if (gNumPlayersLocal > prevNumPlayers) { extern const struct PlayerPalette DEFAULT_MARIO_PALETTE; - network_player_connected(NPT_LOCAL, gNumPlayersLocal - 1, 0, &DEFAULT_MARIO_PALETTE, "Mario", "0"); + char name[10]; + snprintf(name, 10, "Player-%d", gNumPlayersLocal); + network_player_connected(NPT_LOCAL, gNumPlayersLocal - 1, 0, &DEFAULT_MARIO_PALETTE, name, "0"); } else if (gNumPlayersLocal < prevNumPlayers) { u8 index = gNetworkPlayers[prevNumPlayers].globalIndex; if (index == 0) { index = prevNumPlayers; } network_player_disconnected(index); } + gSuppressConnectedPopup = true; prevNumPlayers = gNumPlayersLocal; } diff --git a/src/pc/controller/controller_system.h b/src/pc/controller/controller_system.h index 5e3bd264f..fedc8882e 100644 --- a/src/pc/controller/controller_system.h +++ b/src/pc/controller/controller_system.h @@ -1,14 +1,18 @@ -struct ControllerPlace { +struct ControllerInfo { u8 index; u32 type; bool connected; + SDL_GameController *sdl_cntrl; + SDL_Joystick *sdl_joystick; + SDL_Haptic *sdl_haptic; }; extern char *gGamepadChoices[]; +extern bool gSuppressConnectedPopup; extern int gNumJoys; -extern struct ControllerPlace gPlayerControllerInfos[]; +extern struct ControllerInfo *gReadingController; +extern struct ControllerInfo gPlayerControllerInfos[]; void controller_update_gamepad_choices(); void controller_update_controller_count(); void controller_update_connected_controllers(); - diff --git a/src/pc/djui/djui_hud_utils.c b/src/pc/djui/djui_hud_utils.c index e6def42eb..d8247416c 100644 --- a/src/pc/djui/djui_hud_utils.c +++ b/src/pc/djui/djui_hud_utils.c @@ -72,15 +72,21 @@ static void djui_hud_position_translate(f32* x, f32* y) { if (sResolution == RESOLUTION_DJUI) { djui_gfx_position_translate(x, y); } else { + *x *= ((gSw == 1 && gSw == 1) ? 0.5f : 1.f); + *y *= ((gSh == 1 && gSw == 1) ? 0.5f : 1.f); + *x += (gfx_current_dimensions.aspect_ratio * SCREEN_HEIGHT * 0.5f) * gSx; + *y += (SCREEN_HEIGHT / 2) * gSy; + *x = GFX_DIMENSIONS_FROM_LEFT_EDGE(0) + *x; *y = SCREEN_HEIGHT - *y; } - // transform_y_f32(y, sResolution); } static void djui_hud_size_translate(f32* size) { if (sResolution == RESOLUTION_DJUI) { djui_gfx_size_translate(size); + } else { + *size *= ((gSh == 1 && gSw == 1) ? 0.5f : 1.f); } } @@ -252,7 +258,7 @@ u32 djui_hud_get_screen_width(void) { u32 r = (sResolution == RESOLUTION_N64) ? gfx_current_dimensions.aspect_ratio * SCREEN_HEIGHT : (windowWidth / djui_gfx_get_scale()); - if (gSw == 1) { return r / 2; } + if (gSw == 1 && gSh != 1) { return r / 2; } return r; } @@ -263,7 +269,7 @@ u32 djui_hud_get_screen_height(void) { u32 r = (sResolution == RESOLUTION_N64) ? SCREEN_HEIGHT : (windowHeight / djui_gfx_get_scale()); - if (gSh == 1) { return r / 2; } + if (gSh == 1 && gSw != 1) { return r / 2; } return r; } diff --git a/src/pc/djui/djui_panel_splitscreen.c b/src/pc/djui/djui_panel_splitscreen.c index 678a1d008..71b3dca40 100644 --- a/src/pc/djui/djui_panel_splitscreen.c +++ b/src/pc/djui/djui_panel_splitscreen.c @@ -14,20 +14,11 @@ struct DjuiSplitScreenWindow { static struct DjuiSplitScreenWindow *sDjuiSplitScreenWindows[POSSIBLE_NUM_PLAYERS] = { 0 }; static void djui_panel_splitscreen_window_update_controller_text(struct DjuiSplitScreenWindow *window, u16 index) { - - // Update the index translation - for (u16 i = 0, index = 0; i < POSSIBLE_NUM_PLAYERS; i++) { - struct ControllerPlace *c = &gPlayerControllerInfos[i]; - c->index = index; - if (c->type == 1) { continue; } - index++; - } - - struct ControllerPlace *cntr = &gPlayerControllerInfos[index]; + struct ControllerInfo *cntr = &gPlayerControllerInfos[index]; const char *name = "Main Keyboard"; if (cntr->type != 1) { name = SDL_JoystickNameForIndex(cntr->index); - name = name ? name : "Disconnected"; + name = (name) ? name : "Disconnected"; } djui_text_set_text(window->ctrText, name); @@ -54,13 +45,13 @@ void djui_panel_splitscreen_selection_box_changed(struct DjuiBase *caller) { struct DjuiSplitScreenWindow *window = sDjuiSplitScreenWindows[i]; if (!window || window->selectionBox != selectionBox) { continue; } - struct ControllerPlace *cntr = &gPlayerControllerInfos[i]; + struct ControllerInfo *cntr = &gPlayerControllerInfos[i]; if (cntr->type != 1) { continue; } // This player is a keyboard for (u16 j = 0; j < POSSIBLE_NUM_PLAYERS; j++) { if (i == j) { continue; } - struct ControllerPlace *c = &gPlayerControllerInfos[j]; + struct ControllerInfo *c = &gPlayerControllerInfos[j]; if (c->type == 1) { c->type = 0; struct DjuiSplitScreenWindow *window2 = sDjuiSplitScreenWindows[j]; @@ -75,7 +66,7 @@ void djui_panel_splitscreen_selection_box_changed(struct DjuiBase *caller) { static struct DjuiSplitScreenWindow *djui_panel_splitscreen_controller_window_create(struct DjuiBase *parent, u16 index) { struct DjuiSplitScreenWindow *window = calloc(1, sizeof(struct DjuiSplitScreenWindow)); - struct ControllerPlace *cntr = &gPlayerControllerInfos[index]; + struct ControllerInfo *cntr = &gPlayerControllerInfos[index]; // Border window->rect = djui_rect_create(parent); @@ -129,6 +120,7 @@ void djui_panel_splitscreen_options_create(struct DjuiBase* caller) { struct DjuiThreePanel *panel = djui_panel_menu_create(DLANG(HOST, SPLITSCREEN), false); struct DjuiBase *body = djui_three_panel_get_body(panel); + controller_update_controller_count(); { for (u16 i = 0; i < POSSIBLE_NUM_PLAYERS; i += 2) { struct DjuiFlowLayout* row = djui_flow_layout_create(body); diff --git a/src/pc/network/network.c b/src/pc/network/network.c index 2ce4c4b7e..5c652eefc 100644 --- a/src/pc/network/network.c +++ b/src/pc/network/network.c @@ -102,8 +102,6 @@ bool network_is_online() { void network_set_system(enum NetworkSystemType nsType) { network_forget_all_reliable(); - if (!network_is_online()) { gNetworkSystem = NULL; } - switch (nsType) { case NS_SOCKET: gNetworkSystem = &gNetworkSystemSocket; break; #ifdef COOPNET @@ -111,6 +109,7 @@ void network_set_system(enum NetworkSystemType nsType) { #endif default: gNetworkSystem = &gNetworkSystemSocket; LOG_ERROR("Unknown network system: %d", nsType); break; } + if (!network_is_online()) { gNetworkSystem = &gNetworkSystemDummy; } } bool network_init(enum NetworkType inNetworkType, bool reconnecting) { @@ -174,6 +173,14 @@ bool network_init(enum NetworkType inNetworkType, bool reconnecting) { dynos_behavior_hook_all_custom_behaviors(); network_player_connected(NPT_LOCAL, 0, configPlayerModel, &configPlayerPalette, configPlayerName, get_local_discord_id()); + + extern const struct PlayerPalette DEFAULT_MARIO_PALETTE; + for (u8 i = 1; i < gNumPlayersLocal; i++) { + char name[10]; + snprintf(name, 10, "Player-%d", i); + network_player_connected(NPT_LOCAL, i, 0, &DEFAULT_MARIO_PALETTE, name, "0"); + } + extern u8* gOverrideEeprom; gOverrideEeprom = NULL; diff --git a/src/pc/network/network_player.c b/src/pc/network/network_player.c index cf70c04ea..41ce8e679 100644 --- a/src/pc/network/network_player.c +++ b/src/pc/network/network_player.c @@ -18,6 +18,7 @@ #include "game/mario.h" #include "pc/djui/djui_unicode.h" #include "game/local_multiplayer.h" +#include "pc/controller/controller_system.h" struct NetworkPlayer gNetworkPlayers[MAX_PLAYERS] = { 0 }; struct NetworkPlayer *gNetworkPlayerLocal = NULL; @@ -207,6 +208,7 @@ void network_player_update(void) { for (s32 i = 1; i < MAX_PLAYERS; i++) { struct NetworkPlayer *np = &gNetworkPlayers[i]; if (!np->connected && i > 0) { continue; } + if (i < gNumPlayersLocal) { continue; } float elapsed = (clock_elapsed() - np->lastPingSent); if (elapsed > NETWORK_PLAYER_PING_TIMEOUT) { network_send_ping(np); @@ -218,6 +220,7 @@ void network_player_update(void) { for (s32 i = 1; i < MAX_PLAYERS; i++) { struct NetworkPlayer *np = &gNetworkPlayers[i]; if (!np->connected && i > 0) { continue; } + if (i < gNumPlayersLocal) { continue; } float elapsed = (clock_elapsed() - np->lastReceived); #ifdef DEVELOPMENT @@ -352,7 +355,7 @@ u8 network_player_connected(enum NetworkPlayerType type, u8 globalIndex, u8 mode } // display connected popup - if (!gCurrentlyJoining && type != NPT_SERVER && (gNetworkType != NT_SERVER || type != NPT_LOCAL)) { + if (!gCurrentlyJoining && type != NPT_SERVER && (gNetworkType != NT_SERVER || type != NPT_LOCAL || !gSuppressConnectedPopup)) { construct_player_popup(np, DLANG(NOTIF, CONNECTED), NULL); } LOG_INFO("player connected, local %d, global %d", localIndex, np->globalIndex); @@ -405,7 +408,7 @@ u8 network_player_disconnected(u8 globalIndex) { LOG_INFO("player disconnected, local %d, global %d", i, globalIndex); // display popup - if (np->localIndex >= gNumPlayersLocal) { + if (np->type != NPT_LOCAL || !gSuppressConnectedPopup) { construct_player_popup(np, DLANG(NOTIF, DISCONNECTED), NULL); }