Files
how_progression/docs/ARCHITECTURE.md
T

16 KiB
Raw Blame History

How Progression: current inventory and migration plan

This describes the live client.lua and server.lua as audited in September 2026 and the planned migration. The .bak* and .pre-* files are historical copies; fxmanifest.lua still runs only client.lua and server.lua as gameplay entry points. Do not infer live behavior from the copies.

Live inventory

Area Implemented behavior
Introduction Appearance save triggers UNKNOWN's one-time NPWD SMS and sets unknown_introduced and Crowbar stage 1. Login sync restores Crowbar and Mack/Vinny state. /testunknown and /prog unknown exercise it.
Crowbar world Local Clay ped, smoking scenario, talk target, map blip, locked frozen Daemon. Talk dialog chooses missions by crowbar_stage; active mission suppresses interaction.
Crowbar 1, The Introduction Server-spawned Emperor, crew keys, retrieve/return objectives and blips/marker; initiator gets $500, eligible crew get 5 Crowbar REP and stage 2. Startup removes leftover mission Emperors.
Crowbar 2, Get Some Wheels Server-spawned locked Premier, three lockpicks per participant; delivery persists a Premier through qbx_vehicles:CreatePlayerVehicle for the owner, with submitted properties; eligible crew get 5 REP/stage 3 and Crowbar SMS. Physical mission Premier is deliberately retained.
Crowbar 3, Quick Score Harmony store index 13, one unloaded pistol to initiator; ff_storerobbery event advances from robbery to losing police. Client escape monitoring completes; eligible crew get 5 REP/stage 4 and marked-cash SMS.
Crowbar 4, Wash the Cash Mack dialog opens Gabe route, Gabe/money-wash event advances objective, randol_moneywash completion event ends mission; eligible crew get 5 REP/stage 5 and Crowbar SMS. Mack stage becomes 1 when spoken to. Gabe is currently a blip/location, not a spawned ped here.
Crowbar 5, Buy In Temporary ox_inventory stash with crowbar_package, permanent local crate/target, inventory swap hook, package-carrier ambush with two trucks and Lost peds, escape/return objective, death removes package. Eligible crew get 5 REP/stage 6; carrier gets $750.
Mack world Mack ped and Deke ped physically spawn at resource start, plus festival bus, damaged Emperor, forklift, six wandering workers. Mack interaction is gated by server state; blip is unlocked separately. Deke opens ox_inventory DekeTools.
Mack 1, Easy Rider Accepted crew each get WEAPON_BAT; initiator spawns 1–5 mission Sanchezes. Leave yard, trash detour car, deliver bikes to Vinny, then speak to Vinny. Server tracks delivered bike indices and crew state; each eligible participant gets $300, 5 Mack REP and stage 2. Several Mack SMS messages drive the detour.
Vinny world/service Vinny ped/blip and five local locked frozen display Sanchezes; target calls existing qbx_vehicleshop purchase. Delayed SMS 60–90 seconds after Easy Rider completion saves vinny_fence_unlocked. Persistent fence marker allows up to three unowned non-mission Sanchezes per player per game day for one markedbills item worth $200 each. Server checks ownership, location, quota, item capacity and vehicle deletion.
Cosmic Dave Local Omega ped with partying scenario; no blip, interaction, phone behavior or mission in this resource yet.
Other useBodyArmor ox_inventory item export; NPWD contact bridge; /testcontact, /givemarked, /skipcrowbar1, /prog.

Responsibilities in current files

fxmanifest.lua declares ox_lib, oxmysql and runtime dependencies and loads one gameplay script per side. client.lua mixes contact presentation, dialogs, local scenery, mission vehicles/peds/props, blips/GPS, per-frame markers, mission polling, targeting, inventory use and NPWD UI bridge. server.lua mixes schema alteration, SQL reads/writes, crew snapshots/rewards/tracker, phone SMS, mission registration/state, entity validation, fence economy, login restoration, developer commands and cleanup. Passive contact/location definitions and an unregistered mission framework now exist; neither drives current gameplay.

Foundation implemented, migration pending

shared/contacts/*.lua holds seven passive story contact definitions; shared/contacts/init.lua checks IDs and permanent phone-number uniqueness when required. shared/locations.lua mirrors mission locations, shared/mission_ids.lua defines string mission/phase IDs and parses human /prog notation, and server/services/mission_registry.lua supplies an instance registry without registering missions or events. shared/debug_contact.lua records the temporary Michael fixture's avatar separately. The manifest packages shared files but does not run them. Legacy client.lua and server.lua remain authoritative for every active spawn, interaction, mission, SMS, SQL write, event, callback, export and command. Definitions are verified copies and become authoritative only as individual systems migrate.

Proposed layout

how_progression/
  fxmanifest.lua
  shared/
    contacts/{unknown,crowbar,mack,gabe,deke,vinny,cosmic_dave}.lua
    locations.lua
    mission_ids.lua
  client/
    bootstrap.lua
    services/{contacts,world,markers,routes,phone,mission_view}.lua
    missions/crowbar/{introduction,get_some_wheels,quick_score,wash_the_cash,buy_in}.lua
    missions/mack/easy_rider.lua
    services/body_armor.lua
  server/
    bootstrap.lua
    services/{progression,phone,participants,crew_tracker,rewards,mission_registry,entities,restoration}.lua
    missions/crowbar/{introduction,get_some_wheels,quick_score,wash_the_cash,buy_in}.lua
    missions/mack/easy_rider.lua
    services/vinny_fence.lua
    debug/{commands,phases}.lua
  docs/{ARCHITECTURE,MISSION_DESIGN,PROGRESSION,CONTACTS,PHONE,CREWS}.md

Files under shared/contacts are data descriptors, with optional explicit callback IDs for interactions. Functions, live handles and side effects belong in services. Contact descriptors should allow world.alwaysPresent independent of services.requires, blip.requires, and phone.requires. Use ordered manifest loading or explicit require with one bootstrap; do not duplicate event registration during migration. No new SQL table or phone-number allocator is required for this layout.

Move map (preserve event/export names)

Current functions/events or blocks Destination
getProgression, getPlayerProgression, setCrowbarProgression, advanceCrowbar, schema ALTER and SQL projection server/services/progression.lua; preserve existing columns and initialization timing
syncProgressionState, player-loaded/appearance hooks; client syncProgression, initial world thread, unload/reset server/services/restoration.lua, client/bootstrap.lua, client/services/world.lua
sendSmsToPlayer, UNKNOWN introduction, addPhoneContact bridge server/services/phone.lua, client/services/phone.lua; introduction trigger in contact/mission orchestration
getMissionParticipants, getCitizenIdFromSource, participantIsValid server/services/participants.lua
rewardCrowbarParticipants, rewardMackParticipants, mission cash/items server/services/rewards.lua, with mission-specific reward declarations
Repeated *MissionN, *MissionNByCitizenId, clear/start/complete, active lookup server/services/mission_registry.lua plus individual server mission files
All start/phase/cleanup/reward client events, mission polling and spawned mission entities Corresponding client/missions/<contact>/<mission>.lua
createCrowbar, createMack, createDeke, createVinny, createCosmicDave, display bikes and scrapyard ambience Contact definitions plus client/services/contacts.lua and client/services/world.lua
create*Blip, setMackMission1Route, all SetNewWaypoint, mission/fence DrawMarker threads client/services/routes.lua and markers.lua, driven by objective views
All updatePartyTasks/sendPartyNotification calls server/services/crew_tracker.lua; preserve exact task labels and status types initially
fenceSanchez, fence count/day, leave-bike event server/services/vinny_fence.lua, client/services/world.lua; keep separate from Easy Rider mission
/prog, /testunknown, /skipcrowbar1, /givemarked, /testcontact server/debug/commands.lua and phases.lua
useBodyArmor export client/services/body_armor.lua; preserve export name used by ox_inventory
onResourceStop, mission entity deletion and local target cleanup Each module's cleanup registered through one lifecycle coordinator

The move map covers the public event families listed in each mission section, not a rename plan. Keep current event names during extraction so external resources and running clients continue to communicate.

Public handler inventory by destination

Keep these names while moving handlers. client: and server: below expand to how_progression:client: and how_progression:server:.

Destination Existing handlers/callbacks
Restoration/contact services client:unlockCrowbar, client:syncProgression, client:unlockMack, client:unlockGabe, client:resetProgression, client:unlockVinnyFence; QBCore:Client:OnPlayerUnload, QBCore:Server:OnPlayerLoaded, illenium-appearance:server:saveAppearance; server:getCrowbarMission, server:isCrowbarMissionActive, server:canTalkToMack
Crowbar 1 server:startCrowbarMission1, server:crowbarMission1VehicleAcquired, server:completeCrowbarMission1; client:startCrowbarMission1, client:crowbarMission1Return, client:cleanupCrowbarMission1, client:crowbarMission1Reward
Crowbar 2 server:startCrowbarMission2, server:completeCrowbarMission2; client:startCrowbarMission2, client:cleanupCrowbarMission2
Crowbar 3 server:startCrowbarMission3, local server:storeRegisterRobbed from ff_storerobbery, server:completeCrowbarMission3; client:startCrowbarMission3, client:crowbarMission3Escape
Crowbar 4 server:startCrowbarMission4, server:getCrowbarMission4State, server:crowbarMission4TalkedToMack, local server:moneywashStarted, local server:moneyWashed; client:startCrowbarMission4, client:crowbarMission4Mack, client:crowbarMission4Gabe, client:cleanupCrowbarMission4
Crowbar 5 server:startCrowbarMission5, server:crowbarMission5Escaped, server:completeCrowbarMission5, server:crowbarPackageCarrierDied; client:startCrowbarMission5, client:crowbarMission5PackageTaken, client:crowbarMission5Escaped, client:cleanupCrowbarMission5, client:crowbarMission5Complete; baseevents:onPlayerDied, baseevents:onPlayerKilled, ox_inventory swapItems hook
Easy Rider server:startMackMission1, server:mackMission1BikeSpawned, server:mackMission1Detour, server:mackMission1AtDetour, server:mackMission1CarTrashed, server:mackMission1BikeDelivered, server:completeMackMission1, server:getMackMission1State; client:startMackMission1, client:mackMission1TrashCar, client:mackMission1DetourExplained, client:mackMission1DeliverBikes, client:mackMission1DeliveryCount, client:mackMission1TalkVinny, client:mackMission1Complete
Rewards client:crowbarReward, client:mackReward
Vinny fence server:fenceSanchez, client:leaveFenceBike (unlock event listed under restoration)
Phone client:addPhoneContact; NPWD exports are calls, not how_progression event registrations
Lifecycle onResourceStop server handler; client cleanup currently spread across unload and mission handlers

The body-armor useBodyArmor client export and commands /testunknown, /skipcrowbar1, /prog, /givemarked, /testcontact are also public interfaces. No handler for Gabe/Deke/Cosmic Dave missions exists today.

Migration order and gates

  1. Freeze a behavior inventory: event names, SQL columns, item names, text, coordinates, rewards, phase behavior and external call sites. Record a small solo/crew smoke test for every live mission and /prog branch.
  2. Add shared contact/location data without switching runtime use. Verify values against live Lua. Move only pure constants and phone numbers into descriptors; reload and test contact visibility.
  3. Extract server progression, phone and participant helpers behind existing call signatures. Keep SQL and NPWD calls identical. Test login, SMS, stage sync and one crew snapshot.
  4. Extract client world, contact ped/asset, blip and marker services while preserving physical spawn timing and gated access separately. Test reconnect, repeated sync, resource restart and reset.
  5. Add mission registry and crew tracker adapters, initially routing to old mission handlers. Preserve exact task payloads and citizenid maps. Test solo/crew eligibility.
  6. Move one complete mission at a time, with its client and server parts together: Crowbar 1–5, then Easy Rider. At each step, run start, phase, completion, abort/reset and reconnect checks. Do not move a shared helper simultaneously with a mission unless required.
  7. Extract Vinny fence, body-armor export and debug commands. Implement deterministic /prog reconstruction one mission/phase at a time, preserving existing shorthand until replaced and tested.
  8. Once all callers use modules, remove the old live blocks only after event/export and in-game parity checks. Historical copies remain untouched unless separately approved.

Every intermediate manifest must load each event, callback and export exactly once. Keep client.lua/server.lua as temporary bootstraps if that makes extraction safer.

Risks and audit findings

  • The SQL table creation DDL is not in this resource; only the three Vinny fence columns are altered at startup. Inspect the live schema before changing SQL. getProgression inserts a default row and then reads it.
  • Mission state is memory-only, generally one global instance per mission plus a citizenid map. Resource restart loses active mission state. Existing stop cleanup explicitly deletes only Crowbar 1/2 server vehicles. Client world assets/targets and other mission entities have uneven cleanup. Treat as migration risks, not permission to change gameplay in the extraction pass.
  • Several mission starts call clear* on a global mission, which can replace another crew's mission. Easy Rider instead rejects a concurrent start. Preserve current behavior until a separate multiplayer design decision.
  • participantIsValid compares the snapshotted source's current citizenid; a reconnect with a new source does not regain active mission participation. This is distinct from durable identity for progression. Improve through citizenid resolution only with deliberate mission-lifecycle tests.
  • clearMackMission1, setCrowbarProgression, advanceCrowbar, and client unlockMack/unlockGabe have no in-resource caller in the live files. They may be intended APIs/legacy; search external call sites before removal. testcontact intentionally randomizes a temporary Michael number; it must not influence permanent contact numbers.
  • /prog parses integer text components rather than Lua floats, but current shortcuts depend on an already active mission for most later phases; phase 5.3 has a distinct client event path. These are not deterministic reconstruction yet.
  • getCrowbarActiveMission and getMackMission1State are separate lookup paths. Crew task status values mix true/false and current/pending/done; standardize only after confirming bs_groupsystem behavior.
  • The resource uses ff_storerobbery, randol_moneywash, qbx_vehicles, qbx_vehicleshop, ox_inventory, ox_target, qbx_vehiclekeys, NPWD and bs_groupsystem APIs beyond the manifest dependency list. Preserve server.cfg ordering; inspect installed APIs before adding dependencies.
  • The Vinny daily quota uses a client-provided GTA calendar date and SQL count. The server validates shape and monotonicity but does not independently derive the game day. This is a trust boundary to review separately.
  • client.lua.bak* and server.lua.bak* are not loaded, but are numerous and easy to mistake for live code. Keep a clear live-file index; do not delete them as incidental cleanup.