From b5111232aa13952f58ed1b3b3525ea825662b95c Mon Sep 17 00:00:00 2001 From: Sean Hoyt Date: Sun, 1 Mar 2026 10:50:48 -0600 Subject: feat: Windows64 local multiplayer support (#13) - Skip QuadrantSignin (profile selector) on Windows64 in both LoadMenu and CreateWorldMenu, proceeding directly to local play since Xbox Live stubs always return true for IsSignedInLive() - Fix IsLocalMultiplayerAvailable() to not require IsHiDef() on Windows64 - Allow pad-connected players to join without a profile sign-in check - Fix ghost RemotePlayer creation by scanning all local player slots and matching on server-assigned player index rather than controller slot, fixing P3/P4 ghost entities when joining out of controller order - Give each player a unique name (Player 1-4) based on controller index instead of a single shared stub name - Use raw XInput (XInputGetState) for secondary controller join detection, bypassing the 4J toggle system which consumes all button presses before game logic runs; uses a 120-frame latch for a reliable detection window - Add .gitignore for Visual Studio build artifacts and output directories --- Minecraft.Client/Minecraft.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'Minecraft.Client/Minecraft.cpp') diff --git a/Minecraft.Client/Minecraft.cpp b/Minecraft.Client/Minecraft.cpp index ff8346be..3192410d 100644 --- a/Minecraft.Client/Minecraft.cpp +++ b/Minecraft.Client/Minecraft.cpp @@ -1564,7 +1564,31 @@ void Minecraft::run_middle() // 4J Stu - This doesn't make any sense with the way we handle XboxOne users #ifndef _DURANGO // did we just get input from a player who doesn't exist? They'll be wanting to join the game then +#ifdef _WINDOWS64 + // The 4J toggle system is unreliable here: UIController::handleInput() calls + // ButtonPressed for every ACTION_MENU_* mapped button (which covers all physical + // buttons) before run_middle() runs. Bypass it with raw XInput and own edge detection. + // A latch counter keeps startJustPressed active for ~120 frames after the rising edge + // so the detection window is large enough to be caught reliably. + static WORD s_prevXButtons[XUSER_MAX_COUNT] = {}; + static int s_startPressLatch[XUSER_MAX_COUNT] = {}; + XINPUT_STATE xstate_join; + memset(&xstate_join, 0, sizeof(xstate_join)); + WORD xCurButtons = 0; + if (XInputGetState(i, &xstate_join) == ERROR_SUCCESS) + { + xCurButtons = xstate_join.Gamepad.wButtons; + if ((xCurButtons & XINPUT_GAMEPAD_START) != 0 && (s_prevXButtons[i] & XINPUT_GAMEPAD_START) == 0) + s_startPressLatch[i] = 120; // rising edge: latch for ~120 frames (~2s at 60fps) + else if (s_startPressLatch[i] > 0) + s_startPressLatch[i]--; + s_prevXButtons[i] = xCurButtons; + } + bool startJustPressed = s_startPressLatch[i] > 0; + bool tryJoin = !pause && !ui.IsIgnorePlayerJoinMenuDisplayed(ProfileManager.GetPrimaryPad()) && g_NetworkManager.SessionHasSpace() && xCurButtons != 0; +#else bool tryJoin = !pause && !ui.IsIgnorePlayerJoinMenuDisplayed(ProfileManager.GetPrimaryPad()) && g_NetworkManager.SessionHasSpace() && RenderManager.IsHiDef() && InputManager.ButtonPressed(i); +#endif #ifdef __ORBIS__ // Check for remote play tryJoin = tryJoin && InputManager.IsLocalMultiplayerAvailable(); @@ -1592,6 +1616,8 @@ void Minecraft::run_middle() // did we just get input from a player who doesn't exist? They'll be wanting to join the game then #ifdef __ORBIS__ if(InputManager.ButtonPressed(i, ACTION_MENU_A)) +#elif defined _WINDOWS64 + if(startJustPressed) #else if(InputManager.ButtonPressed(i, MINECRAFT_ACTION_PAUSEMENU)) #endif @@ -1599,7 +1625,11 @@ void Minecraft::run_middle() // Let them join // are they signed in? +#ifdef _WINDOWS64 + if(ProfileManager.IsSignedIn(i) || (g_NetworkManager.IsLocalGame() && InputManager.IsPadConnected(i))) +#else if(ProfileManager.IsSignedIn(i)) +#endif { // if this is a local game, then the player just needs to be signed in if( g_NetworkManager.IsLocalGame() || (ProfileManager.IsSignedInLive(i) && ProfileManager.AllowedToPlayMultiplayer(i) ) ) -- cgit v1.2.3