Observe native prison lock geometry and requalify Core loops

This commit is contained in:
2026-09-19 18:58:12 +02:00
parent e10b8799e9
commit b7fba094d7
39 changed files with 9884 additions and 12 deletions
@@ -0,0 +1,180 @@
#ifndef WORKBENCH
// Read the installed interaction handler's accumulated state. No virtual
// callbacks are overridden, and this accessor never starts/selects an action.
modded class SCR_InteractionHandlerComponent
{
bool ERR_GateReadProgress(BaseUserAction expected, UserActionContext context, out float progress, out bool performing)
{
progress = 0; performing = false;
if (!expected || !context || GetSelectedAction() != expected || GetCurrentContext() != context || m_pLastUserAction != expected) return false;
if (m_bIsPerforming && m_pLastContext != context) return false;
progress = m_fCurrentProgress;
performing = m_bIsPerforming;
return true;
}
}
// Inert without matching explicit CLI opt-ins and a validated ordinary caller.
// No schedule, input, action query that changes denial text, or world mutation.
class ERR_OrdinaryGateObserver
{
protected static ref ERR_OrdinaryGateObserver s_Request;
protected static bool s_Attempted;
protected ERR_GameMode m_Mode;
protected BaseWorld m_World;
protected PlayerController m_Controller;
protected IEntity m_Actor;
protected ERR_PlayerLife m_Life;
protected UUID m_Account;
protected IEntity m_Gate;
protected BaseActionsManagerComponent m_Manager;
protected ERR_UnlockPrisonAction m_Lock;
protected UserActionContext m_Context;
protected BaseDoorComponent m_Door;
protected SCR_InteractionHandlerComponent m_Handler;
protected string m_Nonce;
protected int m_Start;
protected int m_Previous;
protected int m_Index;
protected bool m_Stopped;
protected static bool Enabled(string nonce)
{
string gate, input, scenario;
return !System.IsConsoleApp() && Replication.IsServer() && RplSession.Mode() == RplMode.None
&& ERR_OrdinaryInputObserverRules.Nonce(nonce)
&& System.GetCLIParam("errOrdinaryGateObserve", gate) && gate == nonce
&& System.GetCLIParam("errOrdinaryInputObserve", input) && input == nonce
&& System.GetCLIParam("errOrdinaryScenario", scenario) && (scenario == "core" || scenario == "chat");
}
static void Sample(ERR_GameMode mode, BaseWorld world, PlayerController controller, IEntity actor, string nonce, int started, int index, int now)
{
if (!Enabled(nonce))
{
if (s_Request) s_Request.Cancel(index, now, "explicit-scope-lost");
return;
}
if (!s_Attempted)
{
s_Attempted = true;
s_Request = new ERR_OrdinaryGateObserver();
s_Request.m_Mode = mode; s_Request.m_World = world;
s_Request.m_Controller = controller; s_Request.m_Actor = actor;
s_Request.m_Nonce = nonce; s_Request.m_Start = started; s_Request.m_Previous = started;
if (mode && mode.Players()) s_Request.m_Life = mode.Players().CurrentActor(actor);
if (s_Request.m_Life) s_Request.m_Account = s_Request.m_Life.AccountId;
if (!s_Request.BindGate()) { s_Request.Cancel(index, now, "original-prison-lock-unavailable"); return; }
}
s_Request.Observe(mode, world, controller, actor, nonce, started, index, now);
}
protected bool BindGate()
{
if (!m_Mode || m_Mode.Phase() != ERR_Phase.PRISON || !m_Mode.PrisonEscape() || !m_Mode.PrisonEscape().Registered() || m_Mode.PrisonEscape().GateLost() || !m_Controller) return false;
m_Gate = m_Mode.PrisonEscape().Gate();
if (!m_Gate) return false;
m_Manager = BaseActionsManagerComponent.Cast(m_Gate.FindComponent(BaseActionsManagerComponent));
m_Door = BaseDoorComponent.Cast(m_Gate.FindComponent(BaseDoorComponent));
m_Handler = SCR_InteractionHandlerComponent.Cast(m_Controller.FindComponent(SCR_InteractionHandlerComponent));
if (!m_Manager || !m_Door || !m_Handler) return false;
m_Context = m_Manager.GetContext("SCR_DoorUserAction");
if (!m_Context) return false;
array<BaseUserAction> actions = {};
m_Context.GetActionsList(actions);
int matches;
foreach (BaseUserAction action : actions)
{
ERR_UnlockPrisonAction candidate = ERR_UnlockPrisonAction.Cast(action);
if (!candidate || candidate.GetOwner() != m_Gate) continue;
m_Lock = candidate; matches++;
}
return matches == 1 && m_Manager.FindAction(m_Lock.GetActionID()) == m_Lock && m_Lock.GetActionDuration() == 4;
}
protected bool Context()
{
if (!GetGame() || !m_Mode || !m_World || !m_Controller || !m_Actor || !m_Life || !m_Gate || !m_Manager || !m_Lock || !m_Context || !m_Door || !m_Handler) return false;
if (m_Mode != ERR_GameMode.Get() || !m_Mode.Active() || (m_Mode.Phase() != ERR_Phase.PRISON && m_Mode.Phase() != ERR_Phase.RADIO_SEARCH)) return false;
if (GetGame().GetWorld() != m_World || m_Mode.GetWorld() != m_World || m_Actor.GetWorld() != m_World || m_Gate.GetWorld() != m_World) return false;
if (GetGame().GetPlayerController() != m_Controller || m_Controller.GetControlledEntity() != m_Actor || m_Handler.GetControlledEntity() != m_Actor) return false;
PlayerManager players = GetGame().GetPlayerManager();
int connection = m_Controller.GetPlayerId();
if (!players || connection <= 0 || players.GetPlayerController(connection) != m_Controller || players.GetPlayerControlledEntity(connection) != m_Actor) return false;
if (!m_Mode.Players() || m_Mode.Players().CurrentActor(m_Actor) != m_Life || m_Life.AccountId != m_Account || !ERR_Actors.CanFight(m_Actor) || CompartmentAccessComponent.GetVehicleIn(m_Actor)) return false;
if (!m_Mode.PrisonEscape() || !m_Mode.PrisonEscape().Registered() || m_Mode.PrisonEscape().Gate() != m_Gate || m_Mode.PrisonEscape().GateLost()) return false;
if (m_Gate.FindComponent(BaseActionsManagerComponent) != m_Manager || m_Gate.FindComponent(BaseDoorComponent) != m_Door || m_Controller.FindComponent(SCR_InteractionHandlerComponent) != m_Handler) return false;
if (m_Manager.GetContext("SCR_DoorUserAction") != m_Context || m_Manager.FindAction(m_Lock.GetActionID()) != m_Lock || m_Lock.GetOwner() != m_Gate || m_Lock.GetActionDuration() != 4) return false;
if (!GetGame().GetInputManager() || !GetGame().GetMenuManager() || GetGame().GetMenuManager().GetTopMenu()) return false;
return true;
}
protected void Cancel(int index, int now, string reason)
{
if (m_Stopped) return;
m_Stopped = true;
PrintFormat("ERR GATE OBSERVE: run=%1 sample=%2 real=%3 CANCELLED %4", m_Nonce, index, now, reason);
m_Mode = null; m_World = null; m_Controller = null; m_Actor = null; m_Life = null;
m_Gate = null; m_Manager = null; m_Lock = null; m_Context = null; m_Door = null; m_Handler = null;
}
protected bool CachedPermission()
{
array<BaseUserAction> actions = {};
array<bool> permitted = {};
m_Handler.GetFilteredActions(actions, permitted);
if (actions.Count() != permitted.Count()) return false;
for (int i = 0; i < actions.Count(); i++)
if (actions[i] == m_Lock) return permitted[i];
return false;
}
protected static bool FiniteVector(vector value)
{
for (int i = 0; i < 3; i++)
if (!(value[i] >= -10000000 && value[i] <= 10000000)) return false;
return true;
}
protected void Observe(ERR_GameMode mode, BaseWorld world, PlayerController controller, IEntity actor, string nonce, int started, int index, int now)
{
if (m_Stopped) return;
if (mode != m_Mode || world != m_World || controller != m_Controller || actor != m_Actor || nonce != m_Nonce || started != m_Start)
{ Cancel(index, now, "original-binding-changed"); return; }
if (index != m_Index + 1 || index > 450 || now < m_Previous || now < m_Start || now - m_Start >= 45000)
{ Cancel(index, now, "sample-or-clock-bound"); return; }
if (!Context()) { Cancel(index, now, "native-player-or-prison-context-lost"); return; }
bool selected = m_Handler.GetSelectedAction() == m_Lock;
bool contextSelected = m_Handler.GetCurrentContext() == m_Context;
bool permitted = contextSelected && CachedPermission();
float progress;
bool performing;
bool progressBound = m_Handler.ERR_GateReadProgress(m_Lock, m_Context, progress, performing);
if (!progressBound) { progress = 0; performing = false; }
float duration = m_Lock.GetActionDuration();
float pressed = GetGame().GetInputManager().GetActionValue("PerformAction");
float state = m_Door.GetNormalizedDoorState();
float control = m_Door.GetControlValue();
vector origin = m_Actor.GetOrigin();
vector contextOrigin = m_Context.GetOrigin();
float radius = m_Context.GetRadius();
vector prison = m_Mode.PrisonPosition();
vector gate = m_Gate.GetOrigin();
vector gateTransform[4];
m_Gate.GetTransform(gateTransform);
vector gateForward = gateTransform[2];
float gateForwardLength = gateForward.Length();
if (!(progress >= 0 && progress <= 60) || !(pressed >= 0 && pressed <= 1) || !(state >= -1 && state <= 1) || !(control >= -1 && control <= 1) || !(radius > 0 && radius <= 10) || !FiniteVector(origin) || !FiniteVector(contextOrigin) || !FiniteVector(prison) || !FiniteVector(gate) || !FiniteVector(gateForward) || !(gateForwardLength >= 0.5 && gateForwardLength <= 2))
{ Cancel(index, now, "invalid-native-action-sample"); return; }
gateForward = gateForward / gateForwardLength;
if (!Context()) { Cancel(index, now, "native-player-or-prison-context-lost"); return; }
if (m_Index == 0)
PrintFormat("ERR GATE OBSERVE: run=%1 sample=%2 real=%3 BASIS registered-prison-lock-native-interaction", m_Nonce, index, now);
string actionDetail = string.Format("SAMPLE phase=%1 selected=%2 context=%3 cachedPerformable=%4 progressBound=%5 performing=%6 progress=%7 duration=%8 pressed=%9", m_Mode.Phase(), selected, contextSelected, permitted, progressBound, performing, progress, duration, pressed);
PrintFormat("ERR GATE OBSERVE: run=%1 sample=%2 real=%3 %4", m_Nonce, index, now, actionDetail);
string geometry = string.Format("GEOMETRY body=%1 context=%2 radius=%3 doorState=%4 doorControl=%5 prison=%6 gate=%7 gateForward=%8", origin, contextOrigin, radius, state, control, prison, gate, gateForward);
PrintFormat("ERR GATE OBSERVE: run=%1 sample=%2 real=%3 %4", m_Nonce, index, now, geometry);
m_Index = index; m_Previous = now;
}
}
#endif
@@ -263,6 +263,7 @@ class ERR_OrdinaryInputObserver : Managed
m_Samples++;
PrintFormat("ERR INPUT OBSERVE: run=%1 sample=%2 real=%3 world=%4 SAMPLE origin=%5 forward=%6 input=%7 velocity=%8 inventory=%9", m_Nonce, m_Samples, now, m_LastWorldTime, origin, forward, input, velocity, open);
ERR_OrdinaryLookObserver.Sample(m_Mode, m_World, m_Controller, m_Actor, m_Nonce, m_Start, m_Samples, now);
ERR_OrdinaryGateObserver.Sample(m_Mode, m_World, m_Controller, m_Actor, m_Nonce, m_Start, m_Samples, now);
Observe(now, origin, forward, input, velocity, open);
if (m_Opened && !m_Closed && menu) m_Menu = menu;
m_Previous = now;