mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
mods: read local host sticks for presentation-only plugins
psx_mod_read_local_pad_sticks(player, out[4]) returns the mapped local stick axes (lx, ly, rx, ry; 0..255, centre 128) without touching the SIO sample, so a presentation plugin (a look-around camera) can read the right stick even when the game selected a digital pad. It returns 0 and centred axes for an invalid or disconnected player, in netplay, and during rollback resimulation; peer input is never presentation input. The debug-server axis override feeds it for deterministic tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -207,6 +207,14 @@ if(BUILD_TESTING)
|
||||
add_test(NAME launcher_pad_mode_resolve_test
|
||||
COMMAND launcher_pad_mode_resolve_test)
|
||||
|
||||
# Presentation-only mod input must not be sourced from disconnected,
|
||||
# peer-synchronized, or rollback-replayed pad state.
|
||||
add_executable(mod_local_input_scope_test
|
||||
tests/test_mod_local_input_scope.c)
|
||||
target_include_directories(mod_local_input_scope_test PRIVATE src)
|
||||
add_test(NAME mod_local_input_scope_test
|
||||
COMMAND mod_local_input_scope_test)
|
||||
|
||||
# COP0.CAUSE IP2 must mirror the INTC line combinationally, never latch.
|
||||
add_executable(cause_ip2_combinational_test
|
||||
tests/test_cause_ip2_combinational.c)
|
||||
|
||||
@@ -627,6 +627,14 @@ typedef struct PSXModControllerInput {
|
||||
} PSXModControllerInput;
|
||||
typedef uint32_t (*PSXModControllerPresentationCallback)(
|
||||
const PSXModControllerInput* input);
|
||||
/* Read-only snapshot of the local host stick state for an emulation-thread
|
||||
* presentation callback. Values are ordered lx, ly, rx, ry and use the
|
||||
* DualShock byte range (0..255, centered at 128). This reads mapped local
|
||||
* controller axes independently of the guest's current digital/analog SIO
|
||||
* mode and never changes the simulation's controller sample. Returns 0 and
|
||||
* centers the output for invalid/disconnected input, netplay, or rollback
|
||||
* resimulation; presentation state must never follow synchronized peer input. */
|
||||
int psx_mod_read_local_pad_sticks(uint32_t player, uint8_t out[4]);
|
||||
/*
|
||||
* Override one player's resolved controller presentation mode for this launch.
|
||||
* This is intentionally a trusted-plugin API, not a generic launcher setting.
|
||||
|
||||
@@ -94,6 +94,7 @@ extern "C" void psx_event_step_conservative_env_init(void);
|
||||
#include "launcher_device.h"
|
||||
#include "game_options.h"
|
||||
#include "mod_plugins.h"
|
||||
#include "mod_local_input_policy.h"
|
||||
#include "mod_session_baseline.h"
|
||||
#include "mod_runtime.h"
|
||||
#include "mod_packages.h"
|
||||
@@ -5646,6 +5647,49 @@ static void apply_input_override_to_sio(int override_word) {
|
||||
(uint8_t)(eff_analog ? 1 : 0));
|
||||
}
|
||||
|
||||
/* Local host sticks for presentation-only mods (mod_plugins.h). */
|
||||
extern "C" int psx_mod_read_local_pad_sticks(uint32_t player,
|
||||
uint8_t out[4]) {
|
||||
if (!out) return 0;
|
||||
out[0] = out[1] = out[2] = out[3] = 0x80;
|
||||
|
||||
const bool player_valid = player < PSX_MAX_PLAYERS;
|
||||
const bool netplay = psx_netplay_active() != 0;
|
||||
const bool resimulating = psx_netplay_is_resimulating() != 0;
|
||||
if (!player_valid || netplay || resimulating) return 0;
|
||||
|
||||
PlayerInput& p = g_players[player];
|
||||
const bool dev_here = player == 0 && dev_any_input_enabled();
|
||||
const bool attached = p.kind == 1 || (p.kind == 2 && p.handle);
|
||||
if (!psx_mod_local_input_available(player_valid, attached || dev_here,
|
||||
netplay, resimulating))
|
||||
return 0;
|
||||
|
||||
#ifndef PSX_NO_DEBUG_TOOLS
|
||||
/* The local debug injector supplies a deterministic host-side sample for
|
||||
* camera/input tests. It is never sourced from rollback or a peer. */
|
||||
if (player == 0) {
|
||||
uint8_t injected[4];
|
||||
if (debug_server_get_axis_override(injected)) {
|
||||
std::memcpy(out, injected, sizeof(injected));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (attached) {
|
||||
/* Read the host pad mapping directly. The SIO-facing mode can be
|
||||
* digital because the game selected a digital pad; presentation mods
|
||||
* still need local axes without changing that simulation state. */
|
||||
pad_sticks_for(p, (int)player + 1, out);
|
||||
return 1;
|
||||
}
|
||||
const Uint8* keys = SDL_GetKeyboardState(NULL);
|
||||
psx_keybinds_sticks(keys, (int)player + 1, out);
|
||||
dev_any_controller_sticks(out);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Capture one SIO slot's host pad into a netplay/local blob. Returns 1 if a
|
||||
* device (or dev-any P1) is present; 0 leaves *out as released/disconnected. */
|
||||
static int capture_pad_slot(int s, PsxNetPad* out) {
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef PSXRECOMP_MOD_LOCAL_INPUT_POLICY_H
|
||||
#define PSXRECOMP_MOD_LOCAL_INPUT_POLICY_H
|
||||
|
||||
/* Local presentation plugins may inspect live host input only while its
|
||||
* controller is available during ordinary offline execution. Guest pad mode
|
||||
* is deliberately absent: a game selecting digital SIO does not make its
|
||||
* locally attached analog stick unavailable to presentation code. Peer input
|
||||
* and rollback replay remain simulation state, never presentation input. */
|
||||
static inline int psx_mod_local_input_available(int player_valid,
|
||||
int connected,
|
||||
int netplay_active,
|
||||
int resimulating) {
|
||||
return player_valid && connected &&
|
||||
!netplay_active && !resimulating;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,16 @@
|
||||
#include "mod_local_input_policy.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static int failures;
|
||||
#define CHECK(cond) do { if (!(cond)) { fprintf(stderr, "FAIL: %s\n", #cond); failures++; } } while (0)
|
||||
|
||||
int main(void) {
|
||||
/* Digital guest pad mode does not gate local presentation input. */
|
||||
CHECK(psx_mod_local_input_available(1, 1, 0, 0));
|
||||
CHECK(!psx_mod_local_input_available(0, 1, 0, 0));
|
||||
CHECK(!psx_mod_local_input_available(1, 0, 0, 0));
|
||||
CHECK(!psx_mod_local_input_available(1, 1, 1, 0));
|
||||
CHECK(!psx_mod_local_input_available(1, 1, 0, 1));
|
||||
return failures ? 1 : 0;
|
||||
}
|
||||
Reference in New Issue
Block a user