Skip to content

Service Menu Affordability Guards

rayswaynl edited this page Jun 21, 2026 · 13 revisions

Service Menu Affordability Guards

This page is a focused implementation guide for the vehicle/person service menu debit checks. It is a local correctness patch, not a replacement for the broader server-authority redesign documented in Server authority migration map.

All source paths are relative to Missions/[55-2hc]warfarev2_073v48co.chernarus/.

Why This Matters

The service menu controls rearm, repair, refuel, crew heal and EASA entry. The menu displays prices and disables buttons when the current client thinks the player cannot afford an action, but the action branches do not consistently re-check price, funds and usable vehicle state before debiting money and starting the support script.

That creates a normal-client correctness bug: a stale MenuAction, stale price, or quickly changed funds/context can debit an unaffordable action. It also explains why this is not real anti-cheat: the whole flow remains client-side, so public-server hardening still needs a server ledger or BattlEye posture decision.

Current Branch Matrix

Root / branch Service menu status EASA status Practical meaning
Current docs/source Chernarus 8b71e2a1 Buttons still use cached prices before recalculation (GUI_Menu_Service.sqf:130-190); rearm/refuel debit without action-time funds/context guards (:198, :219), while repair/heal only check positive price (:208, :229). Purchase still uses strict _funds > price and client-side ChangePlayerFunds (GUI_Menu_EASA.sqf:40-53, action at :47-49). Patch-ready and source-unpatched. Fix Chernarus first if this lane becomes code work.
Maintained Vanilla Takistan 8b71e2a1 Same source shape as Chernarus for service and EASA. Same strict exact-funds rejection and client-side debit as Chernarus. Must be propagated deliberately; do not call a Chernarus-only fix complete.
Stable origin/master cf2a6d6a Newer controller/debit helper exists in both maintained roots (GUI_Menu_Service.sqf:157,197), and legacy action branches partially guard rearm/refuel with _funds >= _price (:466, :489). Repair/heal still only check positive price (:478, :501). Exact-funds rejection remains in both maintained roots (GUI_Menu_EASA.sqf:76-78). Stable narrows stale-button and rearm/refuel behavior, but it is not complete service/EASA authority or local context hardening.
Miksuu b8389e74, perf 0076040f and EASA QoL a66d4691 Service action branches still debit from client state: rearm/refuel debit directly and repair/heal only check positive price (GUI_Menu_Service.sqf:326-358 in checked roots). Exact-funds rejection remains at old EASA lines for Miksuu/perf and QoL Vanilla (GUI_Menu_EASA.sqf:47-49); QoL Chernarus shifts the same strict check/debit to :58-60. No stable-style service guard rescue exists on these refs; QoL only changes Chernarus EASA orientation.
Release origin/release/2026-06-feature-bundle a96fdda2 Matches the stable partial shape in both maintained roots: helper debits at :157, :197; rearm/refuel guard with _funds >= _price at :466, :489; repair/heal still only positive-price guarded at :478, :501. Release Chernarus and Vanilla still use strict _funds > price for EASA (GUI_Menu_EASA.sqf:76-78). Treat release as partial local QoL, not a complete service/EASA affordability or authority fix.

The small local fix is still useful, but it is only a correctness patch. Full public-server hardening belongs to Server authority migration map and Economy authority first cut.

Source Flow

Step Source Behavior
Service dialog loads controller Rsc/Dialogs.hpp:2873 RscMenu_Service runs Client/GUI/GUI_Menu_Service.sqf.
Support functions are compiled Client/Init/Init_Client.sqf:71-74 Compiles SupportHeal, SupportRearm, SupportRefuel and SupportRepair.
Menu reads current funds Client/GUI/GUI_Menu_Service.sqf:126 _funds = Call GetPlayerFunds.
Vehicle action buttons use cached prices GUI_Menu_Service.sqf:140-148 Buttons require _canBeUsed and _funds >= _price, but prices are recalculated later in the same loop.
Prices are recalculated GUI_Menu_Service.sqf:155-190 Repair, rearm and refuel prices are updated after the enable checks.
Rearm action GUI_Menu_Service.sqf:196-201 Debits _rearmPrice and spawns SupportRearm with no action-time price/funds/context check.
Repair action GUI_Menu_Service.sqf:205-212 Checks _repairPrice > 0, then debits/spawns; no action-time affordability check.
Refuel action GUI_Menu_Service.sqf:217-222 Debits _refuelPrice and spawns SupportRefuel with no action-time price/funds/context check.
Heal action GUI_Menu_Service.sqf:226-233 Checks _healPrice > 0, then debits/spawns; no action-time affordability check.
Support scripts re-check world context Client_SupportRearm.sqf:56-72, Client_SupportRefuel.sqf:61-77, Client_SupportRepair.sqf:56-72, Client_SupportHeal.sqf:56-76 Timed scripts re-check support distance, vehicle alive state and airborne state before applying effects, but they do not know whether the player could afford the already-debited action.

The person heal branch has the same stale-price shape: GUI_Menu_Service.sqf:130-133 enables the heal button before recomputing _healPrice.

Current Action Matrix (docs/source checkout 8b71e2a1)

Action Current debit guard Missing local guard
Rearm None before -_rearmPrice Call ChangePlayerFunds. _rearmPrice > 0, fresh _funds >= _rearmPrice, current _canBeUsed, valid selection/support context.
Repair _repairPrice > 0. Fresh _funds >= _repairPrice, current _canBeUsed, valid selection/support context.
Refuel None before -_refuelPrice Call ChangePlayerFunds. _refuelPrice > 0, fresh _funds >= _refuelPrice, current _canBeUsed, valid selection/support context.
Heal _healPrice > 0. Fresh _funds >= _healPrice, current usable context for vehicles, valid selection/support context.
EASA entry Button is enabled by service/EASA context and driver/upgrade checks, then MenuAction == 7 opens EASA from the same loop snapshot. Re-check current vehicle/support context before opening EASA; action-time EASA purchase/equip still belongs with the broader gear/EASA authority work.

Patch Shape

Keep the first patch deliberately small:

  1. Recompute prices before ctrlEnable and before the action branches, or add a fresh action-time _funds = Call GetPlayerFunds and use the currently displayed price after recalculation.
  2. Guard each debit/spawn branch with price, affordability and usable-context checks.
  3. Apply the guard before ChangePlayerFunds.
  4. If the action is rejected, reset MenuAction = -1 and show a short hint; do not start the support thread.
  5. Patch source Chernarus first, then propagate Vanilla with LoadoutManager from a correctly named a2waspwarfare checkout.

Minimal action branch pattern:

if (MenuAction == 1) then {
	MenuAction = -1;
	_funds = Call GetPlayerFunds;
	if (_canBeUsed && _rearmPrice > 0 && _funds >= _rearmPrice) then {
		-_rearmPrice Call ChangePlayerFunds;
		[_veh,_nearSupport select _curSel,_typeRepair,_spType] Spawn SupportRearm;
	} else {
		hint "Unable to rearm: insufficient funds or invalid service state.";
	};
};

Use the same shape for refuel, repair and heal. For Man healing, _canBeUsed is defined in scope (assigned at line 414, before the isKindOf "Man" split at line 416), but it is not referenced in the Man button-enable check or the heal action branch. Include it in the Man action guard the same way as for vehicle actions.

What Not To Claim

  • Do not mark gear/EASA/service as server-authoritative after this patch.
  • Do not claim a public-server exploit class is closed; clients still own service effects and money mutation.
  • Do not combine this with a full server ledger migration unless that larger design is explicitly claimed.
  • Do not hand-edit generated Takistan or modded folders as a substitute for LoadoutManager propagation.

Validation

Scenario Expected result
Player has enough funds and valid service context Rearm, repair, refuel and heal still debit once and start their support scripts.
Player cannot afford rearm/refuel No debit, no support thread, short rejection feedback.
Player cannot afford repair/heal No debit, no support thread, short rejection feedback.
Vehicle is airborne or moving too fast at action time No debit and no support thread for vehicle actions.
Price is zero because the action is unnecessary No debit and no support thread.
Support object moves/dies during timed work Existing support scripts still fail the action without applying the final effect.
Arma smoke/RPT No undefined-variable or out-of-range errors from the new guards.

Relationship To Authority Work

This page is the small local guard for DR-28's service-menu inconsistency. The larger economy authority class still lives in:

Agent Index Facts

[
  {"fact":"service_menu_stale_enable_prices","source":"GUI_Menu_Service.sqf:140-190","summary":"Service action buttons are enabled from funds and cached price variables before the loop recalculates repair/rearm/refuel prices."},
  {"fact":"service_rearm_refuel_partial_guard","source":"GUI_Menu_Service.sqf:196-222 (docs checkout 8b71e2a1); GUI_Menu_Service.sqf:482-513 (stable/master cf2a6d6a)","summary":"On docs checkout 8b71e2a1, rearm and refuel action branches debit money and spawn support threads without price, affordability or action-time usable-state guards. On stable/master (and release), both branches guard the debit with `_funds >= _price` before calling ChangePlayerFunds; no `_price > 0` guard exists on these two branches (unlike repair/heal which check positive price). Neither branch adds a `_canBeUsed` or action-time context recheck at the debit point."},
  {"fact":"service_support_scripts_no_funds_check","source":"Client_SupportRearm.sqf:56-72; Client_SupportRefuel.sqf:61-77; Client_SupportRepair.sqf:56-72; Client_SupportHeal.sqf:56-76","summary":"Support scripts re-check distance/alive/airborne state during timed work, but funds were already debited by the menu."},
  {"fact":"service_easa_branch_matrix_2026_06_14","source":"docs 8b71e2a1 GUI_Menu_Service.sqf:198,208,219,229; stable/release GUI_Menu_Service.sqf:157,197,466,478,489,501; Miksuu/perf/QoL GUI_Menu_Service.sqf:326-358; GUI_Menu_EASA.sqf:47-49,58-60,76-78","summary":"Docs checkout keeps the old unguarded service action shape; stable/release partially guard rearm/refuel in both maintained roots; Miksuu/perf/QoL do not; strict exact-funds EASA rejection and client debit remain everywhere checked."}
]

Continue Reading

Previous: Vehicle cargo equip loop bounds | Next: Tools/build workflow

Main map: Home | Gear map: Gear/loadout/EASA atlas | Authority map: Server authority migration map

Sidebar

Clone this wiki locally