refactor(bank): split monolithic store into focused modules; fix locker grant for attachments
- Split BankBaseStore into SessionManager, Messenger, Model, Store, Validator - Extract validation logic into BankValidator with try/catch and per-action methods - Remove duplicate notifications from transaction actions - Update event handlers to call validator first, forward context to store/session - Fix locker grantItems: add missing 'attachment' category mapping to 'item' - Fix locker grantItems: replace exitWith with if/else to prevent skipping remaining items Co-Authored-By: Oz <oz-agent@warp.dev>
This commit is contained in:
parent
68218304ab
commit
6c0ce9e867
@ -1,5 +1,4 @@
|
||||
PREP(handleUIEvents);
|
||||
PREP(initClass);
|
||||
PREP(initSessionService);
|
||||
PREP(initUIBridge);
|
||||
PREP(openUI);
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#include "script_component.hpp"
|
||||
|
||||
if (isNil QGVAR(BankClass)) then { call FUNC(initClass); };
|
||||
if (isNil QGVAR(BankSessionService)) then { call FUNC(initSessionService); };
|
||||
if (isNil QGVAR(BankUIBridge)) then { call FUNC(initUIBridge); };
|
||||
|
||||
[QGVAR(initBank), {
|
||||
@ -26,8 +25,27 @@ if (isNil QGVAR(BankUIBridge)) then { call FUNC(initUIBridge); };
|
||||
};
|
||||
}] call CFUNC(addEventHandler);
|
||||
|
||||
[QGVAR(responseHydrateBank), {
|
||||
params [["_data", createHashMap, [createHashMap]]];
|
||||
|
||||
if !(isNil QGVAR(BankUIBridge)) then {
|
||||
GVAR(BankUIBridge) call ["handleHydrateResponse", [_data, "bank::hydrate"]];
|
||||
};
|
||||
}] call CFUNC(addEventHandler);
|
||||
|
||||
[QGVAR(responseBankNotice), {
|
||||
params [
|
||||
["_type", "error", [""]],
|
||||
["_message", "", [""]]
|
||||
];
|
||||
|
||||
if !(isNil QGVAR(BankUIBridge)) then {
|
||||
GVAR(BankUIBridge) call ["handleNoticeResponse", [_type, _message]];
|
||||
};
|
||||
}] call CFUNC(addEventHandler);
|
||||
|
||||
[{
|
||||
EGVAR(org,OrgClass) get "isLoaded";
|
||||
getPlayerUID player isNotEqualTo "";
|
||||
}, {
|
||||
[QGVAR(initBank), []] call CFUNC(localEvent);
|
||||
}] call CFUNC(waitUntilAndExecute);
|
||||
|
||||
@ -68,6 +68,11 @@ switch (_event) do {
|
||||
GVAR(BankUIBridge) call ["handleDepositEarningsRequest", [_data]];
|
||||
};
|
||||
};
|
||||
case "bank::pin::request": {
|
||||
if !(isNil QGVAR(BankUIBridge)) then {
|
||||
GVAR(BankUIBridge) call ["handleSubmitPinRequest", [_data]];
|
||||
};
|
||||
};
|
||||
default {
|
||||
hint format ["Unhandled bank UI event: %1", _event];
|
||||
};
|
||||
|
||||
@ -18,7 +18,6 @@ GVAR(BankBaseClass) = compileFinal createHashMapFromArray [
|
||||
["bank", 0],
|
||||
["cash", 0],
|
||||
["earnings", 0],
|
||||
["pin", 1234],
|
||||
["transactions", []]
|
||||
]];
|
||||
_self set ["isLoaded", false];
|
||||
|
||||
@ -1,80 +0,0 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* File: fnc_initSessionService.sqf
|
||||
* Author: IDSolutions
|
||||
* Public: No
|
||||
*
|
||||
* Description:
|
||||
* Initializes the bank session service that shapes the browser payload.
|
||||
*/
|
||||
|
||||
#pragma hemtt ignore_variables ["_self"]
|
||||
GVAR(BankSessionServiceBaseClass) = compileFinal createHashMapFromArray [
|
||||
["#type", "BankSessionServiceBaseClass"],
|
||||
["buildTransferTargets", compileFinal {
|
||||
private _targets = [];
|
||||
|
||||
{
|
||||
if (isNull _x || { _x isEqualTo player }) then {
|
||||
continue;
|
||||
};
|
||||
|
||||
private _uid = getPlayerUID _x;
|
||||
private _name = name _x;
|
||||
if (_uid isEqualTo "" || { _name isEqualTo "" }) then {
|
||||
continue;
|
||||
};
|
||||
|
||||
_targets pushBack (createHashMapFromArray [
|
||||
["name", _name],
|
||||
["uid", _uid]
|
||||
]);
|
||||
} forEach allPlayers;
|
||||
|
||||
private _targetPairs = _targets apply {
|
||||
[toLowerANSI (_x getOrDefault ["name", ""]), _x]
|
||||
};
|
||||
_targetPairs sort true;
|
||||
_targetPairs apply {
|
||||
_x param [1, createHashMap]
|
||||
}
|
||||
}],
|
||||
["buildPayload", compileFinal {
|
||||
params [["_mode", "bank", [""]]];
|
||||
|
||||
private _account = if (isNil QGVAR(BankClass)) then {
|
||||
createHashMap
|
||||
} else {
|
||||
GVAR(BankClass) call ["getAccountState", []]
|
||||
};
|
||||
|
||||
private _orgFunds = 0;
|
||||
private _orgName = "";
|
||||
if !(isNil QEGVAR(org,OrgClass)) then {
|
||||
_orgFunds = EGVAR(org,OrgClass) call ["get", ["funds", 0]];
|
||||
_orgName = EGVAR(org,OrgClass) call ["get", ["name", ""]];
|
||||
};
|
||||
|
||||
createHashMapFromArray [
|
||||
["session", createHashMapFromArray [
|
||||
["mode", ["bank", "atm"] select (toLowerANSI _mode isEqualTo "atm")],
|
||||
["orgFunds", _orgFunds],
|
||||
["orgName", _orgName],
|
||||
["playerName", name player],
|
||||
["transferTargets", _self call ["buildTransferTargets", []]],
|
||||
["uid", getPlayerUID player]
|
||||
]],
|
||||
["account", createHashMapFromArray [
|
||||
["bank", _account getOrDefault ["bank", 0]],
|
||||
["cash", _account getOrDefault ["cash", 0]],
|
||||
["earnings", _account getOrDefault ["earnings", 0]],
|
||||
["pin", str (_account getOrDefault ["pin", 1234])],
|
||||
["transactions", _account getOrDefault ["transactions", []]]
|
||||
]]
|
||||
]
|
||||
}]
|
||||
];
|
||||
|
||||
GVAR(BankSessionService) = createHashMapObject [GVAR(BankSessionServiceBaseClass)];
|
||||
GVAR(BankSessionService)
|
||||
@ -19,9 +19,6 @@ GVAR(BankUIBridgeBaseClass) = compileFinal createHashMapFromArray [
|
||||
["#create", compileFinal {
|
||||
_self set ["mode", "bank"];
|
||||
}],
|
||||
["buildPayload", compileFinal {
|
||||
GVAR(BankSessionService) call ["buildPayload", [_self call ["getMode", []]]]
|
||||
}],
|
||||
["getActiveBrowserControl", compileFinal {
|
||||
private _display = uiNamespace getVariable ["RscBank", displayNull];
|
||||
if (isNull _display) exitWith {
|
||||
@ -36,14 +33,16 @@ GVAR(BankUIBridgeBaseClass) = compileFinal createHashMapFromArray [
|
||||
["getMode", compileFinal {
|
||||
_self getOrDefault ["mode", "bank"]
|
||||
}],
|
||||
["hasOpenScreen", compileFinal {
|
||||
private _screen = _self call ["getScreen", []];
|
||||
private _control = _self call ["getActiveBrowserControl", []];
|
||||
|
||||
!(isNull _control) && { _screen call ["isReady", []] }
|
||||
}],
|
||||
["handleDepositEarningsRequest", compileFinal {
|
||||
params [["_data", createHashMap, [createHashMap]]];
|
||||
|
||||
private _amount = floor (_data getOrDefault ["amount", 0]);
|
||||
if (_amount <= 0) exitWith {
|
||||
_self call ["sendNotice", ["error", "No earnings are available to deposit."]];
|
||||
};
|
||||
|
||||
[SRPC(bank,requestDepositEarnings), [getPlayerUID player, _amount]] call CFUNC(serverEvent);
|
||||
true
|
||||
}],
|
||||
@ -51,22 +50,41 @@ GVAR(BankUIBridgeBaseClass) = compileFinal createHashMapFromArray [
|
||||
params [["_data", createHashMap, [createHashMap]]];
|
||||
|
||||
private _amount = floor (_data getOrDefault ["amount", 0]);
|
||||
if (_amount <= 0) exitWith {
|
||||
_self call ["sendNotice", ["error", "Enter a valid deposit amount."]];
|
||||
};
|
||||
|
||||
[SRPC(bank,requestDeposit), [getPlayerUID player, _amount]] call CFUNC(serverEvent);
|
||||
true
|
||||
}],
|
||||
["handleHydrateResponse", compileFinal {
|
||||
params [["_data", createHashMap, [createHashMap]], ["_event", "bank::hydrate", [""]]];
|
||||
|
||||
if !(_self call ["hasOpenScreen", []]) exitWith { false };
|
||||
|
||||
_self call ["sendEvent", [_event, _data, _self call ["getActiveBrowserControl", []]]]
|
||||
}],
|
||||
["handleNoticeResponse", compileFinal {
|
||||
params [["_type", "error", [""]], ["_message", "", [""]]];
|
||||
|
||||
_self call ["sendNotice", [_type, _message]]
|
||||
}],
|
||||
["handleReady", compileFinal {
|
||||
params [["_control", controlNull, [controlNull]], ["_data", createHashMap, [createHashMap]]];
|
||||
|
||||
private _screen = _self call ["getScreen", []];
|
||||
_screen call ["setControl", [_control]];
|
||||
_screen call ["markReady", [true]];
|
||||
|
||||
_self call ["flushPendingEvents", []];
|
||||
_self call ["sendEvent", ["bank::hydrate", _self call ["buildPayload", []], _control]];
|
||||
|
||||
_self call ["requestHydrate", [true]]
|
||||
}],
|
||||
["handleSubmitPinRequest", compileFinal {
|
||||
params [["_data", createHashMap, [createHashMap]]];
|
||||
|
||||
private _pin = _data getOrDefault ["pin", ""];
|
||||
if !(_pin isEqualType "") then {
|
||||
_pin = str _pin;
|
||||
};
|
||||
|
||||
[SRPC(bank,requestSubmitPin), [getPlayerUID player, _pin]] call CFUNC(serverEvent);
|
||||
true
|
||||
}],
|
||||
["handleTransferRequest", compileFinal {
|
||||
params [["_data", createHashMap, [createHashMap]]];
|
||||
@ -75,18 +93,6 @@ GVAR(BankUIBridgeBaseClass) = compileFinal createHashMapFromArray [
|
||||
private _target = _data getOrDefault ["target", ""];
|
||||
private _from = toLowerANSI (_data getOrDefault ["from", "bank"]);
|
||||
|
||||
if (_target isEqualTo "") exitWith {
|
||||
_self call ["sendNotice", ["error", "Select a transfer recipient."]];
|
||||
};
|
||||
|
||||
if (_target isEqualTo getPlayerUID player) exitWith {
|
||||
_self call ["sendNotice", ["error", "You cannot transfer funds to yourself."]];
|
||||
};
|
||||
|
||||
if (_amount <= 0) exitWith {
|
||||
_self call ["sendNotice", ["error", "Enter a valid transfer amount."]];
|
||||
};
|
||||
|
||||
[SRPC(bank,requestTransfer), [getPlayerUID player, _target, _from, _amount]] call CFUNC(serverEvent);
|
||||
true
|
||||
}],
|
||||
@ -94,23 +100,24 @@ GVAR(BankUIBridgeBaseClass) = compileFinal createHashMapFromArray [
|
||||
params [["_data", createHashMap, [createHashMap]]];
|
||||
|
||||
private _amount = floor (_data getOrDefault ["amount", 0]);
|
||||
if (_amount <= 0) exitWith {
|
||||
_self call ["sendNotice", ["error", "Enter a valid withdrawal amount."]];
|
||||
};
|
||||
|
||||
[SRPC(bank,requestWithdraw), [getPlayerUID player, _amount]] call CFUNC(serverEvent);
|
||||
true
|
||||
}],
|
||||
["refreshSession", compileFinal {
|
||||
private _control = _self call ["getActiveBrowserControl", []];
|
||||
if (isNull _control) exitWith { false };
|
||||
_self call ["requestHydrate", [false]]
|
||||
}],
|
||||
["requestHydrate", compileFinal {
|
||||
params [["_resetAuthorization", false, [false]]];
|
||||
|
||||
_self call ["sendEvent", ["bank::sync", _self call ["buildPayload", []], _control]]
|
||||
if !(_self call ["hasOpenScreen", []]) exitWith { false };
|
||||
|
||||
[SRPC(bank,requestHydrateBank), [getPlayerUID player, _self call ["getMode", []], _resetAuthorization]] call CFUNC(serverEvent);
|
||||
true
|
||||
}],
|
||||
["sendNotice", compileFinal {
|
||||
params [["_type", "error", [""]], ["_message", "", [""]], ["_control", controlNull, [controlNull]]];
|
||||
|
||||
if (_message isEqualTo "") exitWith { false };
|
||||
if (_message isEqualTo "" || { !(_self call ["hasOpenScreen", []]) }) exitWith { false };
|
||||
|
||||
_self call ["sendEvent", ["bank::notice", createHashMapFromArray [
|
||||
["message", _message],
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -40,6 +40,9 @@
|
||||
requestRefresh() {
|
||||
return bridge.send("bank::refresh", {});
|
||||
},
|
||||
requestSubmitPin(payload) {
|
||||
return bridge.send("bank::pin::request", payload);
|
||||
},
|
||||
requestTransfer(payload) {
|
||||
return bridge.send("bank::transfer::request", payload);
|
||||
},
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
const BankApp = (window.BankApp = window.BankApp || {});
|
||||
|
||||
const defaultSession = {
|
||||
atmAuthorized: false,
|
||||
mode: "bank",
|
||||
orgFunds: 0,
|
||||
orgName: "",
|
||||
@ -14,7 +15,6 @@
|
||||
bank: 0,
|
||||
cash: 0,
|
||||
earnings: 0,
|
||||
pin: "1234",
|
||||
transactions: [],
|
||||
};
|
||||
|
||||
|
||||
@ -4,14 +4,6 @@
|
||||
|
||||
let noticeTimer = null;
|
||||
|
||||
function getAccount() {
|
||||
return BankApp.data?.account || {};
|
||||
}
|
||||
|
||||
function getSession() {
|
||||
return BankApp.data?.session || {};
|
||||
}
|
||||
|
||||
function normalizeAmount(value) {
|
||||
const amount = Math.floor(Number(value || 0));
|
||||
return Number.isFinite(amount) ? amount : 0;
|
||||
@ -58,18 +50,6 @@
|
||||
|
||||
function requestDeposit(amountValue) {
|
||||
const amount = normalizeAmount(amountValue);
|
||||
const account = getAccount();
|
||||
|
||||
if (amount <= 0) {
|
||||
showNotice("error", "Enter a valid deposit amount.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (amount > Number(account.cash || 0)) {
|
||||
showNotice("error", "Cash on hand cannot cover that deposit.");
|
||||
return false;
|
||||
}
|
||||
|
||||
const bridge = BankApp.bridge;
|
||||
if (!bridge || typeof bridge.requestDeposit !== "function") {
|
||||
showNotice("error", "Deposit bridge is unavailable.");
|
||||
@ -89,18 +69,6 @@
|
||||
|
||||
function requestWithdraw(amountValue) {
|
||||
const amount = normalizeAmount(amountValue);
|
||||
const account = getAccount();
|
||||
|
||||
if (amount <= 0) {
|
||||
showNotice("error", "Enter a valid withdrawal amount.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (amount > Number(account.bank || 0)) {
|
||||
showNotice("error", "Bank balance cannot cover that withdrawal.");
|
||||
return false;
|
||||
}
|
||||
|
||||
const bridge = BankApp.bridge;
|
||||
if (!bridge || typeof bridge.requestWithdraw !== "function") {
|
||||
showNotice("error", "Withdraw bridge is unavailable.");
|
||||
@ -120,30 +88,8 @@
|
||||
|
||||
function requestTransfer(targetUid, amountValue) {
|
||||
const amount = normalizeAmount(amountValue);
|
||||
const session = getSession();
|
||||
const account = getAccount();
|
||||
const targetId = String(targetUid || "").trim();
|
||||
|
||||
if (!targetId) {
|
||||
showNotice("error", "Select a transfer recipient.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (targetId === String(session.uid || "")) {
|
||||
showNotice("error", "You cannot transfer funds to yourself.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (amount <= 0) {
|
||||
showNotice("error", "Enter a valid transfer amount.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (amount > Number(account.bank || 0)) {
|
||||
showNotice("error", "Bank balance cannot cover that transfer.");
|
||||
return false;
|
||||
}
|
||||
|
||||
const bridge = BankApp.bridge;
|
||||
if (!bridge || typeof bridge.requestTransfer !== "function") {
|
||||
showNotice("error", "Transfer bridge is unavailable.");
|
||||
@ -167,21 +113,6 @@
|
||||
|
||||
function requestDepositEarnings(amountValue) {
|
||||
const amount = normalizeAmount(amountValue);
|
||||
const account = getAccount();
|
||||
|
||||
if (amount <= 0) {
|
||||
showNotice("error", "No earnings are available to deposit.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (amount > Number(account.earnings || 0)) {
|
||||
showNotice(
|
||||
"error",
|
||||
"Pending earnings cannot cover that deposit request.",
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
const bridge = BankApp.bridge;
|
||||
if (!bridge || typeof bridge.requestDepositEarnings !== "function") {
|
||||
showNotice("error", "Earnings bridge is unavailable.");
|
||||
@ -224,21 +155,21 @@
|
||||
|
||||
function submitPin() {
|
||||
const enteredPin = String(store.getEnteredPin() || "");
|
||||
const actualPin = String(getAccount().pin || "1234");
|
||||
|
||||
if (enteredPin.length !== 4) {
|
||||
showNotice("error", "Enter your four-digit access PIN.");
|
||||
const bridge = BankApp.bridge;
|
||||
if (!bridge || typeof bridge.requestSubmitPin !== "function") {
|
||||
showNotice("error", "PIN bridge is unavailable.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (enteredPin !== actualPin) {
|
||||
clearPin();
|
||||
showNotice("error", "Incorrect PIN.");
|
||||
store.startAction("pin");
|
||||
const sent = bridge.requestSubmitPin({ pin: enteredPin });
|
||||
if (!sent) {
|
||||
store.finishAction();
|
||||
showNotice("error", "PIN bridge is unavailable.");
|
||||
return false;
|
||||
}
|
||||
|
||||
clearPin();
|
||||
store.setAtmView("menu");
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -299,7 +230,6 @@
|
||||
|
||||
if (success) {
|
||||
store.setCustomAmount("");
|
||||
store.setAtmView("menu");
|
||||
}
|
||||
|
||||
return success;
|
||||
@ -314,10 +244,6 @@
|
||||
? requestDeposit(amount)
|
||||
: requestWithdraw(amount);
|
||||
|
||||
if (success) {
|
||||
store.setAtmView("menu");
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
@ -25,19 +25,35 @@
|
||||
const mode = String(payload?.session?.mode || "bank")
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
const atmAuthorized = Boolean(payload?.session?.atmAuthorized);
|
||||
const currentMode = this.getMode();
|
||||
const currentAtmView = this.getAtmView();
|
||||
const currentPendingAction = this.getPendingAction();
|
||||
|
||||
this.setMode(mode === "atm" ? "atm" : "bank");
|
||||
this.setPendingAction("");
|
||||
this.setNotice({ text: "", type: "" });
|
||||
this.setEnteredPin("");
|
||||
this.setCustomAmount("");
|
||||
this.setAccountVersion(this.getAccountVersion() + 1);
|
||||
this.setSessionVersion(this.getSessionVersion() + 1);
|
||||
|
||||
if (mode === "atm") {
|
||||
this.setAtmView(currentMode === "atm" ? currentAtmView : "pin");
|
||||
if (!atmAuthorized) {
|
||||
this.setAtmView("pin");
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
currentPendingAction === "deposit" ||
|
||||
currentPendingAction === "withdraw" ||
|
||||
currentAtmView === "pin" ||
|
||||
currentMode !== "atm"
|
||||
) {
|
||||
this.setAtmView("menu");
|
||||
return;
|
||||
}
|
||||
|
||||
this.setAtmView(currentAtmView);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -1,2 +1,6 @@
|
||||
PREP(initBank);
|
||||
PREP(initBankStore);
|
||||
PREP(initMessenger);
|
||||
PREP(initModel);
|
||||
PREP(initSessionManager);
|
||||
PREP(initStore);
|
||||
PREP(initValidator);
|
||||
|
||||
@ -13,15 +13,24 @@ PREP_RECOMPILE_END;
|
||||
GVAR(BankStore) call ["init", [_uid]];
|
||||
}] call CFUNC(addEventHandler);
|
||||
|
||||
[QGVAR(requestHydrateBank), {
|
||||
params [["_uid", "", [""]], ["_mode", "bank", [""]], ["_resetAuthorization", false, [false]]];
|
||||
|
||||
if (_uid isEqualTo "") exitWith { diag_log "[FORGE:Server:Bank] Empty/Invalid UID!" };
|
||||
GVAR(BankStore) call ["hydrateSession", [_uid, _mode, _resetAuthorization]];
|
||||
}] call CFUNC(addEventHandler);
|
||||
|
||||
[QGVAR(requestGetBank), {
|
||||
params [["_uid", "", [""]], ["_field", "", [""]]];
|
||||
|
||||
if (_uid isEqualTo "") exitWith { diag_log "[FORGE:Server:Bank] Empty/Invalid UID!" };
|
||||
|
||||
private _finalData = GVAR(BankStore) call ["get", [GVAR(Registry), _uid, _field]];
|
||||
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||
if (_field isNotEqualTo "") then {
|
||||
_finalData = createHashMapFromArray [[_field, _finalData]];
|
||||
};
|
||||
|
||||
[CRPC(bank,responseSyncBank), [_finalData], _player] call CFUNC(targetEvent);
|
||||
GVAR(BankMessenger) call ["sendAccountSync", [_uid, _finalData]];
|
||||
}] call CFUNC(addEventHandler);
|
||||
|
||||
[QGVAR(requestSetBank), {
|
||||
@ -30,9 +39,7 @@ PREP_RECOMPILE_END;
|
||||
if (_uid isEqualTo "" || _field isEqualTo "") exitWith { diag_log "[FORGE:Server:Bank] Empty/Invalid UID or Key!" };
|
||||
|
||||
private _hashMap = GVAR(BankStore) call ["set", [GVAR(Registry), "bank:update", _uid, _field, _value, _sync]];
|
||||
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||
|
||||
[CRPC(bank,responseSyncBank), [_hashMap], _player] call CFUNC(targetEvent);
|
||||
GVAR(BankMessenger) call ["sendAccountSync", [_uid, _hashMap]];
|
||||
}] call CFUNC(addEventHandler);
|
||||
|
||||
[QGVAR(requestMSetBank), {
|
||||
@ -42,9 +49,7 @@ PREP_RECOMPILE_END;
|
||||
if ((_fieldValuePairs isEqualTo createHashMap) || !(_fieldValuePairs isEqualType createHashMap)) exitWith { diag_log "[FORGE:Server:Bank] Empty/Invalid field pairs!" };
|
||||
|
||||
private _hashMap = GVAR(BankStore) call ["mset", [GVAR(Registry), "bank:update", _uid, _fieldValuePairs, _sync]];
|
||||
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||
|
||||
[CRPC(bank,responseSyncBank), [_hashMap], _player] call CFUNC(targetEvent);
|
||||
GVAR(BankMessenger) call ["sendAccountSync", [_uid, _hashMap]];
|
||||
}] call CFUNC(addEventHandler);
|
||||
|
||||
[QGVAR(requestSaveBank), {
|
||||
@ -53,9 +58,7 @@ PREP_RECOMPILE_END;
|
||||
if (_uid isEqualTo "") exitWith { diag_log "[FORGE:Server:Bank] Empty/Invalid UID!" };
|
||||
|
||||
private _finalData = GVAR(BankStore) call ["save", [GVAR(Registry), "bank:update", _uid]];
|
||||
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||
|
||||
[CRPC(bank,responseSyncBank), [_finalData], _player] call CFUNC(targetEvent);
|
||||
GVAR(BankMessenger) call ["sendAccountSync", [_uid, _finalData]];
|
||||
}] call CFUNC(addEventHandler);
|
||||
|
||||
[QGVAR(requestRemoveBank), {
|
||||
@ -68,44 +71,47 @@ PREP_RECOMPILE_END;
|
||||
[QGVAR(requestDeposit), {
|
||||
params [["_uid", "", [""]], ["_amount", 0, [0]]];
|
||||
|
||||
if (_uid isEqualTo "" || _amount isEqualTo 0) exitWith { diag_log "[FORGE:Server:Bank] Empty/Invalid UID or Amount!" };
|
||||
GVAR(BankStore) call ["deposit", [_uid, _amount]];
|
||||
private _context = GVAR(BankValidator) call ["validateDeposit", [_uid, _amount]];
|
||||
if (_context isEqualTo false) exitWith {};
|
||||
GVAR(BankStore) call ["deposit", [_uid, _amount, _context]];
|
||||
}] call CFUNC(addEventHandler);
|
||||
|
||||
[QGVAR(requestPayment), {
|
||||
params [["_uid", "", [""]], ["_amount", 0, [0]]];
|
||||
|
||||
if (_uid isEqualTo "" || _amount isEqualTo 0) exitWith { diag_log "[FORGE:Server:Bank] Empty/Invalid UID or Amount!" };
|
||||
GVAR(BankStore) call ["payment", [_uid, _amount]];
|
||||
private _context = GVAR(BankValidator) call ["validatePayment", [_uid, _amount]];
|
||||
if (_context isEqualTo false) exitWith {};
|
||||
GVAR(BankStore) call ["payment", [_uid, _amount, _context]];
|
||||
}] call CFUNC(addEventHandler);
|
||||
|
||||
[QGVAR(requestSubmitPin), {
|
||||
params [["_uid", "", [""]], ["_pin", "", [""]]];
|
||||
|
||||
private _context = GVAR(BankValidator) call ["validateSubmitPin", [_uid, _pin]];
|
||||
if (_context isEqualTo false) exitWith {};
|
||||
GVAR(BankSessionManager) call ["submitPin", [_uid, _context]];
|
||||
}] call CFUNC(addEventHandler);
|
||||
|
||||
[QGVAR(requestTransfer), {
|
||||
params [["_uid", "", [""]], ["_target", "", [""]], ["_from", "", [""]], ["_amount", 0, [0]]];
|
||||
|
||||
if (_uid isEqualTo "" || _target isEqualTo "" || _from isEqualTo "" || _amount isEqualTo 0) exitWith {
|
||||
diag_log "[FORGE:Server:Bank] Empty/Invalid UID, Target, From Account, or Amount!"
|
||||
};
|
||||
|
||||
if (_uid isEqualTo _target) exitWith {
|
||||
diag_log format ["[FORGE:Server:Bank] SECURITY: Player %1 attempted self-transfer!", _uid];
|
||||
|
||||
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||
[CRPC(notifications,recieveNotification), ["error", "Bank", "Cannot transfer to yourself!"], _player] call CFUNC(targetEvent);
|
||||
};
|
||||
|
||||
GVAR(BankStore) call ["transfer", [_uid, _target, _from, _amount]];
|
||||
private _context = GVAR(BankValidator) call ["validateTransfer", [_uid, _target, _from, _amount]];
|
||||
if (_context isEqualTo false) exitWith {};
|
||||
GVAR(BankStore) call ["transfer", [_uid, _target, _amount, _context]];
|
||||
}] call CFUNC(addEventHandler);
|
||||
|
||||
[QGVAR(requestWithdraw), {
|
||||
params [["_uid", "", [""]], ["_amount", 0, [0]]];
|
||||
|
||||
if (_uid isEqualTo "" || _amount isEqualTo 0) exitWith { diag_log "[FORGE:Server:Bank] Empty/Invalid UID or Amount!" };
|
||||
GVAR(BankStore) call ["withdraw", [_uid, _amount]];
|
||||
private _context = GVAR(BankValidator) call ["validateWithdraw", [_uid, _amount]];
|
||||
if (_context isEqualTo false) exitWith {};
|
||||
GVAR(BankStore) call ["withdraw", [_uid, _amount, _context]];
|
||||
}] call CFUNC(addEventHandler);
|
||||
|
||||
[QGVAR(requestDepositEarnings), {
|
||||
params [["_uid", "", [""]], ["_amount", 0, [0]]];
|
||||
|
||||
if (_uid isEqualTo "" || _amount isEqualTo 0) exitWith { diag_log "[FORGE:Server:Bank] Empty/Invalid UID or Amount!" };
|
||||
GVAR(BankStore) call ["depositEarnings", [_uid, _amount]];
|
||||
private _context = GVAR(BankValidator) call ["validateDepositEarnings", [_uid, _amount]];
|
||||
if (_context isEqualTo false) exitWith {};
|
||||
GVAR(BankStore) call ["depositEarnings", [_uid, _amount, _context]];
|
||||
}] call CFUNC(addEventHandler);
|
||||
|
||||
@ -1,326 +0,0 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* File: fnc_initBankStore.sqf
|
||||
* Author: IDSolutions
|
||||
* Date: 2025-12-17
|
||||
* Last Update: 2026-02-17
|
||||
* Public: Yes
|
||||
*
|
||||
* Description:
|
||||
* Initializes the bank store for managing player bank accounts.
|
||||
* Provides methods for syncing, saving, and applying bank accounts to the player.
|
||||
*
|
||||
* Arguments:
|
||||
* None
|
||||
*
|
||||
* Return Value:
|
||||
* Bank store object [HASHMAP OBJECT]
|
||||
*
|
||||
* Example:
|
||||
* call forge_server_bank_fnc_initBankStore
|
||||
*/
|
||||
|
||||
#pragma hemtt ignore_variables ["_self"]
|
||||
GVAR(BankModel) = compileFinal createHashMapObject [[
|
||||
["#type", "BankModel"],
|
||||
["defaults", compileFinal {
|
||||
private _account = createHashMap;
|
||||
|
||||
_account set ["uid", ""];
|
||||
_account set ["name", ""];
|
||||
_account set ["bank", 0];
|
||||
_account set ["cash", 0];
|
||||
_account set ["earnings", 0];
|
||||
_account set ["pin", 1234];
|
||||
_account set ["transactions", []];
|
||||
|
||||
_account
|
||||
}],
|
||||
["fromPlayer", compileFinal {
|
||||
params [["_player", objNull, [objNull]]];
|
||||
|
||||
if (_player isEqualTo objNull) exitWith { _self call ["defaults", []] };
|
||||
|
||||
private _account = _self call ["defaults", []];
|
||||
|
||||
_account set ["uid", getPlayerUID _player];
|
||||
_account set ["name", name _player];
|
||||
_account set ["bank", 0];
|
||||
_account set ["cash", 0];
|
||||
_account set ["earnings", 0];
|
||||
_account set ["pin", 1234];
|
||||
_account set ["transactions", []];
|
||||
|
||||
_account
|
||||
}],
|
||||
["migrate", compileFinal {
|
||||
params [["_account", createHashMap, [createHashMap]]];
|
||||
|
||||
private _defaults = _self call ["defaults", []];
|
||||
|
||||
{
|
||||
if !(_x in _account) then { _account set [_x, _y]; };
|
||||
} forEach _defaults;
|
||||
|
||||
_account
|
||||
}],
|
||||
["validate", compileFinal {
|
||||
params [["_account", createHashMap, [createHashMap]]];
|
||||
|
||||
private _uid = _account getOrDefault ["uid", ""];
|
||||
private _name = _account getOrDefault ["name", ""];
|
||||
private _bank = _account getOrDefault ["bank", 0];
|
||||
private _cash = _account getOrDefault ["cash", 0];
|
||||
private _earnings = _account getOrDefault ["earnings", 0];
|
||||
private _pin = _account getOrDefault ["pin", 1234];
|
||||
|
||||
[_uid, _name, _bank, _cash, _earnings, _pin] try {
|
||||
if (_uid isEqualTo "" || !(_uid isEqualType "")) then { throw "Invalid UID!"; };
|
||||
if (_name isEqualTo "" || !(_name isEqualType "")) then { throw "Invalid Name!"; };
|
||||
if (_bank < 0 || !(_bank isEqualType 0)) then { throw "Invalid Bank!"; };
|
||||
if (_cash < 0 || !(_cash isEqualType 0)) then { throw "Invalid Cash!"; };
|
||||
if (_earnings < 0 || !(_earnings isEqualType 0)) then { throw "Invalid Earnings!"; };
|
||||
if (_pin < 1000 || _pin > 9999 || !(_pin isEqualType 0)) then { throw "Invalid Pin!"; };
|
||||
} catch {
|
||||
["ERROR", format ["Failed to validate account %1!", _exception]] call EFUNC(common,log);
|
||||
false
|
||||
};
|
||||
|
||||
true
|
||||
}]
|
||||
]];
|
||||
|
||||
GVAR(BankBaseStore) = compileFinal createHashMapFromArray [
|
||||
["#base", EGVAR(common,BaseStore)],
|
||||
["#type", "BankBaseStore"],
|
||||
["#create", compileFinal {
|
||||
GVAR(IndexRegistry) = createHashMap;
|
||||
GVAR(Registry) = createHashMap;
|
||||
["INFO", "Bank Store Initialized!"] call EFUNC(common,log);
|
||||
}],
|
||||
["init", compileFinal {
|
||||
params [["_uid", "", [""]]];
|
||||
|
||||
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||
private _cached = GVAR(Registry) getOrDefault [_uid, nil];
|
||||
if !(isNil { _cached }) exitWith { [CRPC(bank,responseInitBank), [_cached], _player] call CFUNC(targetEvent); _cached };
|
||||
|
||||
["bank:exists", [_uid]] call EFUNC(extension,extCall) params ["_result", "_isSuccess"];
|
||||
if !(_isSuccess) exitWith {
|
||||
["ERROR", format ["Failed to check if bank account %1 exists! Using fallback account.", _uid]] call EFUNC(common,log);
|
||||
|
||||
private _fallbackAccount = GVAR(BankModel) call ["fromPlayer", [_player]];
|
||||
_fallbackAccount set ["uid", _uid];
|
||||
|
||||
private _regEntry = createHashMapFromArray [["uid", _uid], ["name", (name _player)]];
|
||||
GVAR(IndexRegistry) set [_uid, _regEntry];
|
||||
|
||||
GVAR(Registry) set [_uid, _fallbackAccount];
|
||||
[CRPC(bank,responseInitBank), [_fallbackAccount], _player] call CFUNC(targetEvent);
|
||||
|
||||
_fallbackAccount
|
||||
};
|
||||
|
||||
private _finalAccount = createHashMap;
|
||||
|
||||
if (_result == "true") then {
|
||||
_finalAccount = _self call ["fetch", ["bank:get", _uid]];
|
||||
["INFO", format ["Found bank account for %1", _uid]] call EFUNC(common,log);
|
||||
} else {
|
||||
_finalAccount = GVAR(BankModel) call ["fromPlayer", [_player]];
|
||||
_finalAccount set ["uid", _uid];
|
||||
|
||||
private _json = _self call ["toJSON", [_finalAccount]];
|
||||
["bank:create", [_uid, _json]] call EFUNC(extension,extCall) params ["_result", "_isSuccess"];
|
||||
if !(_isSuccess) exitWith {
|
||||
["ERROR", format ["Failed to create bank account %1! Using fallback account.", _uid]] call EFUNC(common,log);
|
||||
|
||||
private _regEntry = createHashMapFromArray [["uid", _uid], ["name", (name _player)]];
|
||||
GVAR(IndexRegistry) set [_uid, _regEntry];
|
||||
|
||||
GVAR(Registry) set [_uid, _finalAccount];
|
||||
[CRPC(bank,responseInitBank), [_finalAccount], _player] call CFUNC(targetEvent);
|
||||
|
||||
_finalAccount
|
||||
};
|
||||
|
||||
["INFO", format ["Created new bank account for %1", _uid]] call EFUNC(common,log);
|
||||
};
|
||||
|
||||
|
||||
private _regEntry = createHashMapFromArray [["uid", _uid], ["name", (name _player)]];
|
||||
GVAR(IndexRegistry) set [_uid, _regEntry];
|
||||
|
||||
// _finalAccount = GVAR(BankModel) call ["migrate", [_finalAccount]];
|
||||
GVAR(Registry) set [_uid, _finalAccount];
|
||||
[CRPC(bank,responseInitBank), [_finalAccount], _player] call CFUNC(targetEvent);
|
||||
|
||||
_finalAccount
|
||||
}],
|
||||
["deposit", compileFinal {
|
||||
params [["_uid", "", [""]], ["_amount", 0, [0]]];
|
||||
|
||||
["INFO", format ["Deposit %1, for %2", _amount, _uid]] call EFUNC(common,log);
|
||||
|
||||
private _account = GVAR(Registry) getOrDefault [_uid, nil];
|
||||
if (isNil "_account") exitWith { ["ERROR", "Empty/Invalid Account!"] call EFUNC(common,log); };
|
||||
|
||||
private _bank = _account getOrDefault ["bank", 0];
|
||||
private _cash = _account getOrDefault ["cash", 0];
|
||||
if (_cash < _amount) exitWith { ["WARNING", "Insufficient Funds!"] call EFUNC(common,log); };
|
||||
|
||||
private _finalAccount = createHashMapFromArray [["bank", (_bank + _amount)], ["cash", (_cash - _amount)]];
|
||||
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||
|
||||
GVAR(Registry) set [_uid, _finalAccount];
|
||||
|
||||
[CRPC(bank,responseSyncBank), [_finalAccount], _player] call CFUNC(targetEvent);
|
||||
[CRPC(notifications,recieveNotification), ["info", "Bank", format ["Deposited $%1", _amount]], _player] call CFUNC(targetEvent);
|
||||
}],
|
||||
["payment", compileFinal {
|
||||
params [["_uid", "", [""]], ["_amount", 0, [0]]];
|
||||
|
||||
["INFO", format ["Payment %1, for %2", _amount, _uid]] call EFUNC(common,log);
|
||||
|
||||
private _account = GVAR(Registry) getOrDefault [_uid, nil];
|
||||
if (isNil "_account") exitWith { ["ERROR", "Empty/Invalid Account!"] call EFUNC(common,log); };
|
||||
|
||||
private _bank = _account getOrDefault ["bank", 0];
|
||||
private _finalAccount = createHashMapFromArray [["bank", (_bank + _amount)]];
|
||||
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||
|
||||
GVAR(Registry) set [_uid, _finalAccount];
|
||||
|
||||
[CRPC(bank,responseSyncBank), [_finalAccount], _player] call CFUNC(targetEvent);
|
||||
[CRPC(notifications,recieveNotification), ["info", "Bank", format ["Paid $%1", _amount]], _player] call CFUNC(targetEvent);
|
||||
}],
|
||||
["buildChargeResult", compileFinal {
|
||||
params [["_message", "Unable to process bank payment.", [""]]];
|
||||
|
||||
createHashMapFromArray [
|
||||
["success", false],
|
||||
["message", _message],
|
||||
["patch", createHashMap]
|
||||
]
|
||||
}],
|
||||
["chargeCheckout", compileFinal {
|
||||
params [
|
||||
["_uid", "", [""]],
|
||||
["_source", "cash", [""]],
|
||||
["_amount", 0, [0]],
|
||||
["_commit", false, [false]]
|
||||
];
|
||||
|
||||
private _result = _self call ["buildChargeResult", []];
|
||||
private _field = switch (toLowerANSI _source) do {
|
||||
case "cash": { "cash" };
|
||||
case "bank": { "bank" };
|
||||
default { "" };
|
||||
};
|
||||
|
||||
if (_field isEqualTo "") exitWith {
|
||||
_result set ["message", "Selected bank payment source is unsupported."];
|
||||
_result
|
||||
};
|
||||
|
||||
private _account = GVAR(Registry) getOrDefault [_uid, createHashMap];
|
||||
if (_account isEqualTo createHashMap) exitWith {
|
||||
_result set ["message", "Bank account data is unavailable for checkout."];
|
||||
_result
|
||||
};
|
||||
|
||||
private _balance = _account getOrDefault [_field, 0];
|
||||
if (_balance < _amount) exitWith {
|
||||
private _message = [
|
||||
"Bank balance cannot cover this checkout.",
|
||||
"Cash on hand cannot cover this checkout."
|
||||
] select (_field isEqualTo "cash");
|
||||
|
||||
_result set ["message", _message];
|
||||
_result
|
||||
};
|
||||
|
||||
private _patch = createHashMapFromArray [[_field, (_balance - _amount)]];
|
||||
if (_commit) then {
|
||||
_patch = _self call ["mset", [GVAR(Registry), "bank:update", _uid, _patch, false]];
|
||||
};
|
||||
|
||||
_result set ["success", true];
|
||||
_result set ["message", ""];
|
||||
_result set ["patch", _patch];
|
||||
_result
|
||||
}],
|
||||
["transfer", compileFinal {
|
||||
params [["_uid", "", [""]], ["_target", "", [""]], ["_from", "", [""]], ["_amount", 0, [0]]];
|
||||
|
||||
if (_uid isEqualTo _target) exitWith { ["WARNING", format ["Self-transfer attempt blocked for %1", _uid]] call EFUNC(common,log); };
|
||||
|
||||
private _account = GVAR(Registry) getOrDefault [_uid, nil];
|
||||
if (isNil "_account") exitWith { ["ERROR", "Empty/Invalid Account!"] call EFUNC(common,log); };
|
||||
|
||||
private _targetAccount = GVAR(Registry) getOrDefault [_target, nil];
|
||||
if (isNil "_targetAccount") exitWith { ["ERROR", "Empty/Invalid Target Account!"] call EFUNC(common,log); };
|
||||
|
||||
private _selected = _account getOrDefault [_from, 0];
|
||||
if (_selected < _amount) exitWith { ["WARNING", "Insufficient Funds!"] call EFUNC(common,log); };
|
||||
|
||||
private _targetBank = _targetAccount getOrDefault ["bank", 0];
|
||||
private _finalAccount = createHashMapFromArray [[_from, (_selected - _amount)]];
|
||||
private _finalTargetBank = createHashMapFromArray [["bank", (_targetBank + _amount)]];
|
||||
|
||||
GVAR(Registry) set [_uid, _finalAccount];
|
||||
GVAR(Registry) set [_target, _finalTargetBank];
|
||||
|
||||
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||
private _targetPlayer = [_target] call EFUNC(common,getPlayer);
|
||||
|
||||
[CRPC(bank,responseSyncBank), [_finalAccount], _player] call CFUNC(targetEvent);
|
||||
[CRPC(bank,responseSyncBank), [_finalTargetBank], _targetPlayer] call CFUNC(targetEvent);
|
||||
[CRPC(notifications,recieveNotification), ["info", "Bank", format ["Transferred $%1 to %2", _amount, (name _targetPlayer)]], _player] call CFUNC(targetEvent);
|
||||
[CRPC(notifications,recieveNotification), ["info", "Bank", format ["Received $%1 from %2", _amount, (name _player)]], _targetPlayer] call CFUNC(targetEvent);
|
||||
}],
|
||||
["withdraw", compileFinal {
|
||||
params [["_uid", "", [""]], ["_amount", 0, [0]]];
|
||||
|
||||
["INFO", format ["Withdraw %1, for %2", _amount, _uid]] call EFUNC(common,log);
|
||||
|
||||
private _account = GVAR(Registry) getOrDefault [_uid, nil];
|
||||
if (isNil "_account") exitWith { ["ERROR", "Empty/Invalid Account!"] call EFUNC(common,log); };
|
||||
|
||||
private _bank = _account getOrDefault ["bank", 0];
|
||||
private _cash = _account getOrDefault ["cash", 0];
|
||||
if (_bank < _amount) exitWith { ["WARNING", "Insufficient Funds!"] call EFUNC(common,log); };
|
||||
|
||||
private _finalAccount = createHashMapFromArray [["bank", (_bank - _amount)], ["cash", (_cash + _amount)]];
|
||||
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||
|
||||
GVAR(Registry) set [_uid, _finalAccount];
|
||||
|
||||
[CRPC(bank,responseSyncBank), [_finalAccount], _player] call CFUNC(targetEvent);
|
||||
[CRPC(notifications,recieveNotification), ["info", "Bank", format ["Withdrew $%1", _amount]], _player] call CFUNC(targetEvent);
|
||||
}],
|
||||
["depositEarnings", compileFinal {
|
||||
params [["_uid", "", [""]], ["_amount", 0, [0]]];
|
||||
|
||||
["INFO", format ["Deposit Earnings %1, for %2", _amount, _uid]] call EFUNC(common,log);
|
||||
|
||||
private _account = GVAR(Registry) getOrDefault [_uid, nil];
|
||||
if (isNil "_account") exitWith { ["ERROR", "Empty/Invalid Account!"] call EFUNC(common,log); };
|
||||
|
||||
private _bank = _account getOrDefault ["bank", 0];
|
||||
private _earnings = _account getOrDefault ["earnings", 0];
|
||||
if (_earnings < _amount) exitWith { ["WARNING", "Insufficient Earnings!"] call EFUNC(common,log); };
|
||||
|
||||
private _finalAccount = createHashMapFromArray [["bank", (_bank + _amount)], ["earnings", (_earnings - _amount)]];
|
||||
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||
|
||||
GVAR(Registry) set [_uid, _finalAccount];
|
||||
|
||||
[CRPC(bank,responseSyncBank), [_finalAccount], _player] call CFUNC(targetEvent);
|
||||
[CRPC(notifications,recieveNotification), ["info", "Bank", format ["Deposited $%1 from earnings", _amount]], _player] call CFUNC(targetEvent);
|
||||
}]
|
||||
];
|
||||
|
||||
GVAR(BankStore) = createHashMapObject [GVAR(BankBaseStore)];
|
||||
GVAR(BankStore)
|
||||
75
arma/server/addons/bank/functions/fnc_initMessenger.sqf
Normal file
75
arma/server/addons/bank/functions/fnc_initMessenger.sqf
Normal file
@ -0,0 +1,75 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* File: fnc_initMessenger.sqf
|
||||
* Author: IDSolutions
|
||||
* Date: 2026-03-16
|
||||
* Last Update: 2026-03-16
|
||||
* Public: No
|
||||
*
|
||||
* Description:
|
||||
* Initializes the bank messenger for all server-to-client
|
||||
* communication including account syncs, toast notifications,
|
||||
* and inline bank UI notices.
|
||||
*
|
||||
* Parameter(s):
|
||||
* None
|
||||
*
|
||||
* Returns:
|
||||
* Messenger object [HASHMAP OBJECT]
|
||||
*
|
||||
* Example(s):
|
||||
* call forge_server_bank_fnc_initMessenger
|
||||
*/
|
||||
|
||||
#pragma hemtt ignore_variables ["_self"]
|
||||
GVAR(BankMessenger) = createHashMapObject [[
|
||||
["#type", "BankMessenger"],
|
||||
["buildClientAccountPatch", compileFinal {
|
||||
params [["_account", createHashMap, [createHashMap]]];
|
||||
|
||||
private _patch = createHashMap;
|
||||
{
|
||||
if (_x in _account) then {
|
||||
_patch set [_x, _account get _x];
|
||||
};
|
||||
} forEach ["uid", "name", "bank", "cash", "earnings", "transactions"];
|
||||
|
||||
_patch
|
||||
}],
|
||||
["sendAccountSync", compileFinal {
|
||||
params [["_uid", "", [""]], ["_account", createHashMap, [createHashMap]], ["_event", CRPC(bank,responseSyncBank), [""]]];
|
||||
|
||||
if (_uid isEqualTo "" || { _account isEqualTo createHashMap }) exitWith { false };
|
||||
|
||||
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||
if (isNull _player) exitWith { false };
|
||||
|
||||
[_event, [_self call ["buildClientAccountPatch", [_account]]], _player] call CFUNC(targetEvent);
|
||||
true
|
||||
}],
|
||||
["sendClientNotification", compileFinal {
|
||||
params [["_uid", "", [""]], ["_type", "info", [""]], ["_title", "Bank", [""]], ["_message", "", [""]]];
|
||||
|
||||
if (_uid isEqualTo "" || { _message isEqualTo "" }) exitWith { false };
|
||||
|
||||
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||
if (isNull _player) exitWith { false };
|
||||
|
||||
[CRPC(notifications,recieveNotification), [_type, _title, _message], _player] call CFUNC(targetEvent);
|
||||
true
|
||||
}],
|
||||
["sendNotice", compileFinal {
|
||||
params [["_uid", "", [""]], ["_type", "error", [""]], ["_message", "", [""]]];
|
||||
|
||||
if (_uid isEqualTo "" || { _message isEqualTo "" }) exitWith { false };
|
||||
|
||||
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||
if (isNull _player) exitWith { false };
|
||||
|
||||
[CRPC(bank,responseBankNotice), [_type, _message], _player] call CFUNC(targetEvent);
|
||||
true
|
||||
}]
|
||||
]];
|
||||
|
||||
GVAR(BankMessenger)
|
||||
91
arma/server/addons/bank/functions/fnc_initModel.sqf
Normal file
91
arma/server/addons/bank/functions/fnc_initModel.sqf
Normal file
@ -0,0 +1,91 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* File: fnc_initModel.sqf
|
||||
* Author: IDSolutions
|
||||
* Date: 2026-03-16
|
||||
* Last Update: 2026-03-16
|
||||
* Public: No
|
||||
*
|
||||
* Description:
|
||||
* Initializes the bank account data model. Provides default account
|
||||
* schema, player-based account creation, schema migration for
|
||||
* existing accounts, and field-level validation.
|
||||
*
|
||||
* Parameter(s):
|
||||
* None
|
||||
*
|
||||
* Returns:
|
||||
* Bank model object [HASHMAP OBJECT]
|
||||
*
|
||||
* Example(s):
|
||||
* call forge_server_bank_fnc_initModel
|
||||
*/
|
||||
|
||||
#pragma hemtt ignore_variables ["_self"]
|
||||
GVAR(BankModel) = compileFinal createHashMapObject [[
|
||||
["#type", "BankModel"],
|
||||
["defaults", compileFinal {
|
||||
private _account = createHashMap;
|
||||
|
||||
_account set ["uid", ""];
|
||||
_account set ["name", ""];
|
||||
_account set ["bank", 0];
|
||||
_account set ["cash", 0];
|
||||
_account set ["earnings", 0];
|
||||
_account set ["pin", 1234];
|
||||
_account set ["transactions", []];
|
||||
|
||||
_account
|
||||
}],
|
||||
["fromPlayer", compileFinal {
|
||||
params [["_player", objNull, [objNull]]];
|
||||
|
||||
if (_player isEqualTo objNull) exitWith { _self call ["defaults", []] };
|
||||
|
||||
private _account = _self call ["defaults", []];
|
||||
|
||||
_account set ["uid", getPlayerUID _player];
|
||||
_account set ["name", name _player];
|
||||
|
||||
_account
|
||||
}],
|
||||
["migrate", compileFinal {
|
||||
params [["_account", createHashMap, [createHashMap]]];
|
||||
|
||||
private _defaults = _self call ["defaults", []];
|
||||
{
|
||||
if !(_x in _account) then {
|
||||
_account set [_x, _y];
|
||||
};
|
||||
} forEach _defaults;
|
||||
|
||||
_account
|
||||
}],
|
||||
["validate", compileFinal {
|
||||
params [["_account", createHashMap, [createHashMap]]];
|
||||
|
||||
private _uid = _account getOrDefault ["uid", ""];
|
||||
private _name = _account getOrDefault ["name", ""];
|
||||
private _bank = _account getOrDefault ["bank", 0];
|
||||
private _cash = _account getOrDefault ["cash", 0];
|
||||
private _earnings = _account getOrDefault ["earnings", 0];
|
||||
private _pin = _account getOrDefault ["pin", 1234];
|
||||
|
||||
[_uid, _name, _bank, _cash, _earnings, _pin] try {
|
||||
if (_uid isEqualTo "" || !(_uid isEqualType "")) then { throw "Invalid UID!"; };
|
||||
if (_name isEqualTo "" || !(_name isEqualType "")) then { throw "Invalid Name!"; };
|
||||
if (_bank < 0 || !(_bank isEqualType 0)) then { throw "Invalid Bank!"; };
|
||||
if (_cash < 0 || !(_cash isEqualType 0)) then { throw "Invalid Cash!"; };
|
||||
if (_earnings < 0 || !(_earnings isEqualType 0)) then { throw "Invalid Earnings!"; };
|
||||
if (_pin < 1000 || _pin > 9999 || !(_pin isEqualType 0)) then { throw "Invalid Pin!"; };
|
||||
} catch {
|
||||
["ERROR", format ["Failed to validate account %1!", _exception]] call EFUNC(common,log);
|
||||
false
|
||||
};
|
||||
|
||||
true
|
||||
}]
|
||||
]];
|
||||
|
||||
GVAR(BankModel)
|
||||
94
arma/server/addons/bank/functions/fnc_initSessionManager.sqf
Normal file
94
arma/server/addons/bank/functions/fnc_initSessionManager.sqf
Normal file
@ -0,0 +1,94 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* File: fnc_initSessionManager.sqf
|
||||
* Author: IDSolutions
|
||||
* Date: 2026-03-16
|
||||
* Last Update: 2026-03-16
|
||||
* Public: No
|
||||
*
|
||||
* Description:
|
||||
* Initializes the bank session manager for managing ATM/bank
|
||||
* session state, mode resolution, and PIN authorization.
|
||||
*
|
||||
* Parameter(s):
|
||||
* None
|
||||
*
|
||||
* Returns:
|
||||
* Session manager object [HASHMAP OBJECT]
|
||||
*
|
||||
* Example(s):
|
||||
* call forge_server_bank_fnc_initSessionManager
|
||||
*/
|
||||
|
||||
#pragma hemtt ignore_variables ["_self"]
|
||||
GVAR(BankSessionManager) = createHashMapObject [[
|
||||
["#type", "BankSessionManager"],
|
||||
["getSessionState", compileFinal {
|
||||
params [["_uid", "", [""]]];
|
||||
|
||||
private _session = GVAR(SessionRegistry) getOrDefault [_uid, createHashMap];
|
||||
if (_session isEqualTo createHashMap) then {
|
||||
_session = createHashMapFromArray [
|
||||
["atmAuthorized", false],
|
||||
["mode", "bank"]
|
||||
];
|
||||
GVAR(SessionRegistry) set [_uid, _session];
|
||||
};
|
||||
|
||||
_session
|
||||
}],
|
||||
["setSessionState", compileFinal {
|
||||
params [["_uid", "", [""]], ["_fieldValuePairs", createHashMap, [createHashMap]]];
|
||||
|
||||
if (_uid isEqualTo "") exitWith { createHashMap };
|
||||
|
||||
private _session = +(_self call ["getSessionState", [_uid]]);
|
||||
{ _session set [_x, _y]; } forEach _fieldValuePairs;
|
||||
|
||||
GVAR(SessionRegistry) set [_uid, _session];
|
||||
_session
|
||||
}],
|
||||
["resolveMode", compileFinal {
|
||||
params [["_mode", "bank", [""]]];
|
||||
|
||||
private _finalMode = toLowerANSI _mode;
|
||||
if !(_finalMode in ["atm", "bank"]) then { _finalMode = "bank"; };
|
||||
|
||||
_finalMode
|
||||
}],
|
||||
["syncSessionMode", compileFinal {
|
||||
params [["_uid", "", [""]], ["_mode", "", [""]], ["_resetAuthorization", false, [false]]];
|
||||
|
||||
private _current = _self call ["getSessionState", [_uid]];
|
||||
private _finalMode = if (_mode isEqualTo "") then {
|
||||
_current getOrDefault ["mode", "bank"]
|
||||
} else {
|
||||
_self call ["resolveMode", [_mode]]
|
||||
};
|
||||
private _atmAuthorized = _current getOrDefault ["atmAuthorized", false];
|
||||
|
||||
if (_finalMode isEqualTo "atm") then {
|
||||
if (_resetAuthorization || { (_current getOrDefault ["mode", "bank"]) isNotEqualTo "atm" }) then {
|
||||
_atmAuthorized = false;
|
||||
};
|
||||
} else {
|
||||
_atmAuthorized = false;
|
||||
};
|
||||
|
||||
_self call ["setSessionState", [_uid, createHashMapFromArray [
|
||||
["atmAuthorized", _atmAuthorized],
|
||||
["mode", _finalMode]
|
||||
]]]
|
||||
}],
|
||||
["submitPin", compileFinal {
|
||||
params [["_uid", "", [""]], ["_context", createHashMap, [createHashMap]]];
|
||||
|
||||
_self call ["setSessionState", [_uid, createHashMapFromArray [["atmAuthorized", true], ["mode", "atm"]]]];
|
||||
GVAR(BankMessenger) call ["sendClientNotification", [_uid, "info", "Bank", "ATM access granted."]];
|
||||
GVAR(BankStore) call ["hydrateSession", [_uid, "atm", false]];
|
||||
true
|
||||
}]
|
||||
]];
|
||||
|
||||
GVAR(BankSessionManager)
|
||||
350
arma/server/addons/bank/functions/fnc_initStore.sqf
Normal file
350
arma/server/addons/bank/functions/fnc_initStore.sqf
Normal file
@ -0,0 +1,350 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* File: fnc_initStore.sqf
|
||||
* Author: IDSolutions
|
||||
* Date: 2025-12-17
|
||||
* Last Update: 2026-03-16
|
||||
* Public: No
|
||||
*
|
||||
* Description:
|
||||
* Initializes the bank store for managing player bank accounts.
|
||||
* Handles account lifecycle (init/fetch/create/migrate), transaction
|
||||
* mutations, checkout charges, and session hydration.
|
||||
*
|
||||
* Parameter(s):
|
||||
* None
|
||||
*
|
||||
* Returns:
|
||||
* Bank store object [HASHMAP OBJECT]
|
||||
*
|
||||
* Example(s):
|
||||
* call forge_server_bank_fnc_initStore
|
||||
*/
|
||||
|
||||
#pragma hemtt ignore_variables ["_self"]
|
||||
GVAR(BankBaseStore) = compileFinal createHashMapFromArray [
|
||||
["#base", EGVAR(common,BaseStore)],
|
||||
["#type", "BankBaseStore"],
|
||||
["#create", compileFinal {
|
||||
GVAR(IndexRegistry) = createHashMap;
|
||||
GVAR(Registry) = createHashMap;
|
||||
GVAR(SessionRegistry) = createHashMap;
|
||||
["INFO", "Bank Store Initialized!"] call EFUNC(common,log);
|
||||
}],
|
||||
["buildChargeResult", compileFinal {
|
||||
params [["_message", "Unable to process bank payment.", [""]]];
|
||||
|
||||
createHashMapFromArray [
|
||||
["success", false],
|
||||
["message", _message],
|
||||
["patch", createHashMap]
|
||||
]
|
||||
}],
|
||||
["buildHydratePayload", compileFinal {
|
||||
params [["_uid", "", [""]], ["_mode", "", [""]], ["_resetAuthorization", false, [false]]];
|
||||
|
||||
if (_uid isEqualTo "") exitWith { createHashMap };
|
||||
|
||||
private _account = GVAR(Registry) getOrDefault [_uid, createHashMap];
|
||||
if (_account isEqualTo createHashMap) then { _account = _self call ["init", [_uid]]; };
|
||||
if (_account isEqualTo createHashMap) exitWith { createHashMap };
|
||||
|
||||
private _session = GVAR(BankSessionManager) call ["syncSessionMode", [_uid, _mode, _resetAuthorization]];
|
||||
private _orgState = _self call ["resolveOrgState", [_uid]];
|
||||
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||
private _playerName = if (isNull _player) then {
|
||||
_account getOrDefault ["name", "Unknown"]
|
||||
} else {
|
||||
name _player
|
||||
};
|
||||
|
||||
createHashMapFromArray [
|
||||
["session", createHashMapFromArray [
|
||||
["atmAuthorized", _session getOrDefault ["atmAuthorized", false]],
|
||||
["mode", _session getOrDefault ["mode", "bank"]],
|
||||
["orgFunds", _orgState getOrDefault ["funds", 0]],
|
||||
["orgName", _orgState getOrDefault ["name", ""]],
|
||||
["playerName", _playerName],
|
||||
["transferTargets", _self call ["buildTransferTargets", [_uid]]],
|
||||
["uid", _uid]
|
||||
]],
|
||||
["account", GVAR(BankMessenger) call ["buildClientAccountPatch", [_account]]]
|
||||
]
|
||||
}],
|
||||
["buildTransferTargets", compileFinal {
|
||||
params [["_sourceUid", "", [""]]];
|
||||
|
||||
private _targets = [];
|
||||
{
|
||||
if (isNull _x) then { continue; };
|
||||
|
||||
private _targetUid = getPlayerUID _x;
|
||||
private _targetName = name _x;
|
||||
if (_targetUid isEqualTo "" || { _targetUid isEqualTo _sourceUid } || { _targetName isEqualTo "" }) then { continue; };
|
||||
|
||||
_targets pushBack (createHashMapFromArray [
|
||||
["name", _targetName],
|
||||
["uid", _targetUid]
|
||||
]);
|
||||
} forEach allPlayers;
|
||||
|
||||
private _targetPairs = _targets apply { [toLowerANSI (_x getOrDefault ["name", ""]), _x] };
|
||||
_targetPairs sort true;
|
||||
_targetPairs apply { _x param [1, createHashMap] }
|
||||
}],
|
||||
["chargeCheckout", compileFinal {
|
||||
params [["_uid", "", [""]], ["_source", "cash", [""]], ["_amount", 0, [0]], ["_commit", false, [false]]];
|
||||
|
||||
private _result = _self call ["buildChargeResult", []];
|
||||
private _field = switch (toLowerANSI _source) do {
|
||||
case "cash": { "cash" };
|
||||
case "bank": { "bank" };
|
||||
default { "" };
|
||||
};
|
||||
|
||||
if (_field isEqualTo "") exitWith {
|
||||
_result set ["message", "Selected bank payment source is unsupported."];
|
||||
_result
|
||||
};
|
||||
|
||||
private _account = GVAR(Registry) getOrDefault [_uid, createHashMap];
|
||||
if (_account isEqualTo createHashMap) exitWith {
|
||||
_result set ["message", "Bank account data is unavailable for checkout."];
|
||||
_result
|
||||
};
|
||||
|
||||
private _balance = _account getOrDefault [_field, 0];
|
||||
if (_balance < _amount) exitWith {
|
||||
private _message = [
|
||||
"Bank balance cannot cover this checkout.",
|
||||
"Cash on hand cannot cover this checkout."
|
||||
] select (_field isEqualTo "cash");
|
||||
|
||||
_result set ["message", _message];
|
||||
_result
|
||||
};
|
||||
|
||||
private _patch = createHashMapFromArray [[_field, (_balance - _amount)]];
|
||||
if (_commit) then {
|
||||
_patch = _self call ["mset", [GVAR(Registry), "bank:update", _uid, _patch, false]];
|
||||
};
|
||||
|
||||
_result set ["success", true];
|
||||
_result set ["message", ""];
|
||||
_result set ["patch", _patch];
|
||||
_result
|
||||
}],
|
||||
["deposit", compileFinal {
|
||||
params [["_uid", "", [""]], ["_amount", 0, [0]], ["_context", createHashMap, [createHashMap]]];
|
||||
|
||||
["INFO", format ["Deposit %1, for %2", _amount, _uid]] call EFUNC(common,log);
|
||||
|
||||
private _bank = _context getOrDefault ["bank", 0];
|
||||
private _cash = _context getOrDefault ["cash", 0];
|
||||
|
||||
private _patch = createHashMapFromArray [
|
||||
["bank", (_bank + _amount)],
|
||||
["cash", (_cash - _amount)]
|
||||
];
|
||||
private _finalPatch = _self call ["mset", [GVAR(Registry), "bank:update", _uid, _patch, false]];
|
||||
|
||||
GVAR(BankMessenger) call ["sendAccountSync", [_uid, _finalPatch]];
|
||||
GVAR(BankMessenger) call ["sendClientNotification", [_uid, "info", "Bank", format ["Deposited $%1", _amount]]];
|
||||
true
|
||||
}],
|
||||
["hydrateSession", compileFinal {
|
||||
params [["_uid", "", [""]], ["_mode", "", [""]], ["_resetAuthorization", false, [false]]];
|
||||
|
||||
private _payload = _self call ["buildHydratePayload", [_uid, _mode, _resetAuthorization]];
|
||||
if (_payload isEqualTo createHashMap) exitWith { false };
|
||||
|
||||
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||
if (isNull _player) exitWith { false };
|
||||
|
||||
[CRPC(bank,responseHydrateBank), [_payload], _player] call CFUNC(targetEvent);
|
||||
true
|
||||
}],
|
||||
["init", compileFinal {
|
||||
params [["_uid", "", [""]]];
|
||||
|
||||
if (_uid isEqualTo "") exitWith { createHashMap };
|
||||
|
||||
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||
private _playerName = if (isNull _player) then { "Unknown" } else { name _player };
|
||||
private _cached = GVAR(Registry) getOrDefault [_uid, createHashMap];
|
||||
if (_cached isNotEqualTo createHashMap) exitWith {
|
||||
GVAR(BankMessenger) call ["sendAccountSync", [_uid, _cached, CRPC(bank,responseInitBank)]];
|
||||
_cached
|
||||
};
|
||||
|
||||
["bank:exists", [_uid]] call EFUNC(extension,extCall) params ["_result", "_isSuccess"];
|
||||
if !(_isSuccess) exitWith {
|
||||
["ERROR", format ["Failed to check if bank account %1 exists! Using fallback account.", _uid]] call EFUNC(common,log);
|
||||
|
||||
private _fallbackAccount = GVAR(BankModel) call ["fromPlayer", [_player]];
|
||||
_fallbackAccount set ["uid", _uid];
|
||||
if ((_fallbackAccount getOrDefault ["name", ""]) isEqualTo "") then {
|
||||
_fallbackAccount set ["name", _playerName];
|
||||
};
|
||||
|
||||
private _regEntry = createHashMapFromArray [["uid", _uid], ["name", _playerName]];
|
||||
GVAR(IndexRegistry) set [_uid, _regEntry];
|
||||
GVAR(Registry) set [_uid, _fallbackAccount];
|
||||
|
||||
GVAR(BankMessenger) call ["sendAccountSync", [_uid, _fallbackAccount, CRPC(bank,responseInitBank)]];
|
||||
_fallbackAccount
|
||||
};
|
||||
|
||||
private _finalAccount = createHashMap;
|
||||
if (_result isEqualTo "true") then {
|
||||
_finalAccount = _self call ["fetch", ["bank:get", _uid]];
|
||||
["INFO", format ["Found bank account for %1", _uid]] call EFUNC(common,log);
|
||||
} else {
|
||||
_finalAccount = GVAR(BankModel) call ["fromPlayer", [_player]];
|
||||
_finalAccount set ["uid", _uid];
|
||||
if ((_finalAccount getOrDefault ["name", ""]) isEqualTo "") then {
|
||||
_finalAccount set ["name", _playerName];
|
||||
};
|
||||
|
||||
private _json = _self call ["toJSON", [_finalAccount]];
|
||||
["bank:create", [_uid, _json]] call EFUNC(extension,extCall) params ["_createResult", "_createSuccess"];
|
||||
if (!_createSuccess) exitWith {
|
||||
["ERROR", format ["Failed to create bank account %1! Using fallback account.", _uid]] call EFUNC(common,log);
|
||||
|
||||
private _regEntry = createHashMapFromArray [["uid", _uid], ["name", _playerName]];
|
||||
GVAR(IndexRegistry) set [_uid, _regEntry];
|
||||
GVAR(Registry) set [_uid, _finalAccount];
|
||||
|
||||
GVAR(BankMessenger) call ["sendAccountSync", [_uid, _finalAccount, CRPC(bank,responseInitBank)]];
|
||||
_finalAccount
|
||||
};
|
||||
|
||||
["INFO", format ["Created new bank account for %1", _uid]] call EFUNC(common,log);
|
||||
};
|
||||
|
||||
_finalAccount = GVAR(BankModel) call ["migrate", [_finalAccount]];
|
||||
if ((_finalAccount getOrDefault ["uid", ""]) isEqualTo "") then {
|
||||
_finalAccount set ["uid", _uid];
|
||||
};
|
||||
if ((_finalAccount getOrDefault ["name", ""]) isEqualTo "") then {
|
||||
_finalAccount set ["name", _playerName];
|
||||
};
|
||||
|
||||
GVAR(IndexRegistry) set [_uid, createHashMapFromArray [["uid", _uid], ["name", _playerName]]];
|
||||
GVAR(Registry) set [_uid, _finalAccount];
|
||||
|
||||
GVAR(BankMessenger) call ["sendAccountSync", [_uid, _finalAccount, CRPC(bank,responseInitBank)]];
|
||||
_finalAccount
|
||||
}],
|
||||
["payment", compileFinal {
|
||||
params [["_uid", "", [""]], ["_amount", 0, [0]], ["_context", createHashMap, [createHashMap]]];
|
||||
|
||||
["INFO", format ["Payment %1, for %2", _amount, _uid]] call EFUNC(common,log);
|
||||
|
||||
private _bank = _context getOrDefault ["bank", 0];
|
||||
private _patch = createHashMapFromArray [["bank", (_bank + _amount)]];
|
||||
private _finalPatch = _self call ["mset", [GVAR(Registry), "bank:update", _uid, _patch, false]];
|
||||
|
||||
GVAR(BankMessenger) call ["sendAccountSync", [_uid, _finalPatch]];
|
||||
GVAR(BankMessenger) call ["sendClientNotification", [_uid, "info", "Bank", format ["Paid $%1", _amount]]];
|
||||
true
|
||||
}],
|
||||
["resolveOrgState", compileFinal {
|
||||
params [["_uid", "", [""]]];
|
||||
|
||||
private _defaultState = createHashMapFromArray [
|
||||
["funds", 0],
|
||||
["name", ""]
|
||||
];
|
||||
if (_uid isEqualTo "") exitWith { _defaultState };
|
||||
|
||||
private _actor = EGVAR(actor,Registry) getOrDefault [_uid, createHashMap];
|
||||
private _orgID = _actor getOrDefault ["organization", "default"];
|
||||
if (_orgID isEqualTo "") then { _orgID = "default"; };
|
||||
|
||||
private _org = EGVAR(org,OrgStore) call ["loadById", [_orgID]];
|
||||
if (_org isEqualTo createHashMap) then {
|
||||
_org = EGVAR(org,OrgStore) call ["loadById", ["default"]];
|
||||
};
|
||||
if (_org isEqualTo createHashMap) exitWith { _defaultState };
|
||||
|
||||
createHashMapFromArray [
|
||||
["funds", _org getOrDefault ["funds", 0]],
|
||||
["name", _org getOrDefault ["name", ""]]
|
||||
]
|
||||
}],
|
||||
["transfer", compileFinal {
|
||||
params [["_uid", "", [""]], ["_target", "", [""]], ["_amount", 0, [0]], ["_context", createHashMap, [createHashMap]]];
|
||||
|
||||
private _account = _context getOrDefault ["account", createHashMap];
|
||||
private _targetAccount = _context getOrDefault ["targetAccount", createHashMap];
|
||||
private _sourceField = _context getOrDefault ["sourceField", "bank"];
|
||||
private _selected = _context getOrDefault ["sourceBalance", 0];
|
||||
private _targetBank = _context getOrDefault ["targetBank", 0];
|
||||
|
||||
private _sourcePatch = createHashMapFromArray [[_sourceField, (_selected - _amount)]];
|
||||
private _targetPatch = createHashMapFromArray [["bank", (_targetBank + _amount)]];
|
||||
private _finalSourcePatch = _self call ["mset", [GVAR(Registry), "bank:update", _uid, _sourcePatch, false]];
|
||||
private _finalTargetPatch = _self call ["mset", [GVAR(Registry), "bank:update", _target, _targetPatch, false]];
|
||||
|
||||
GVAR(BankMessenger) call ["sendAccountSync", [_uid, _finalSourcePatch]];
|
||||
GVAR(BankMessenger) call ["sendAccountSync", [_target, _finalTargetPatch]];
|
||||
|
||||
private _targetPlayer = [_target] call EFUNC(common,getPlayer);
|
||||
private _targetName = if (isNull _targetPlayer) then {
|
||||
_targetAccount getOrDefault ["name", "Recipient"]
|
||||
} else {
|
||||
name _targetPlayer
|
||||
};
|
||||
private _player = [_uid] call EFUNC(common,getPlayer);
|
||||
private _playerName = if (isNull _player) then {
|
||||
_account getOrDefault ["name", "Unknown"]
|
||||
} else {
|
||||
name _player
|
||||
};
|
||||
|
||||
GVAR(BankMessenger) call ["sendClientNotification", [_uid, "info", "Bank", format ["Transferred $%1 to %2", _amount, _targetName]]];
|
||||
GVAR(BankMessenger) call ["sendClientNotification", [_target, "info", "Bank", format ["Received $%1 from %2", _amount, _playerName]]];
|
||||
true
|
||||
}],
|
||||
["withdraw", compileFinal {
|
||||
params [["_uid", "", [""]], ["_amount", 0, [0]], ["_context", createHashMap, [createHashMap]]];
|
||||
|
||||
["INFO", format ["Withdraw %1, for %2", _amount, _uid]] call EFUNC(common,log);
|
||||
|
||||
private _bank = _context getOrDefault ["bank", 0];
|
||||
private _cash = _context getOrDefault ["cash", 0];
|
||||
|
||||
private _patch = createHashMapFromArray [
|
||||
["bank", (_bank - _amount)],
|
||||
["cash", (_cash + _amount)]
|
||||
];
|
||||
private _finalPatch = _self call ["mset", [GVAR(Registry), "bank:update", _uid, _patch, false]];
|
||||
|
||||
GVAR(BankMessenger) call ["sendAccountSync", [_uid, _finalPatch]];
|
||||
GVAR(BankMessenger) call ["sendClientNotification", [_uid, "info", "Bank", format ["Withdrew $%1", _amount]]];
|
||||
true
|
||||
}],
|
||||
["depositEarnings", compileFinal {
|
||||
params [["_uid", "", [""]], ["_amount", 0, [0]], ["_context", createHashMap, [createHashMap]]];
|
||||
|
||||
["INFO", format ["Deposit Earnings %1, for %2", _amount, _uid]] call EFUNC(common,log);
|
||||
|
||||
private _bank = _context getOrDefault ["bank", 0];
|
||||
private _earnings = _context getOrDefault ["earnings", 0];
|
||||
|
||||
private _patch = createHashMapFromArray [
|
||||
["bank", (_bank + _amount)],
|
||||
["earnings", (_earnings - _amount)]
|
||||
];
|
||||
private _finalPatch = _self call ["mset", [GVAR(Registry), "bank:update", _uid, _patch, false]];
|
||||
|
||||
GVAR(BankMessenger) call ["sendAccountSync", [_uid, _finalPatch]];
|
||||
GVAR(BankMessenger) call ["sendClientNotification", [_uid, "info", "Bank", format ["Deposited $%1 from earnings", _amount]]];
|
||||
true
|
||||
}]
|
||||
];
|
||||
|
||||
GVAR(BankStore) = createHashMapObject [GVAR(BankBaseStore)];
|
||||
GVAR(BankStore)
|
||||
259
arma/server/addons/bank/functions/fnc_initValidator.sqf
Normal file
259
arma/server/addons/bank/functions/fnc_initValidator.sqf
Normal file
@ -0,0 +1,259 @@
|
||||
#include "..\script_component.hpp"
|
||||
|
||||
/*
|
||||
* File: fnc_validator.sqf
|
||||
* Author: IDSolutions
|
||||
* Date: 2026-03-16
|
||||
* Last Update: 2026-03-16
|
||||
* Public: No
|
||||
*
|
||||
* Description:
|
||||
* Initializes the bank validator for pre-checking action payloads
|
||||
* before they reach the bank store. Each method uses try/catch to
|
||||
* validate inputs and state, sending a notice to the player on
|
||||
* failure and returning false. On success returns a context hashmap
|
||||
* containing resolved data (account, balances, etc.) for the store.
|
||||
*
|
||||
* Parameter(s):
|
||||
* None
|
||||
*
|
||||
* Returns:
|
||||
* Validator object [HASHMAP OBJECT]
|
||||
*
|
||||
* Example(s):
|
||||
* call forge_server_bank_fnc_validator
|
||||
*/
|
||||
|
||||
#pragma hemtt ignore_variables ["_self"]
|
||||
GVAR(BankValidator) = createHashMapObject [[
|
||||
["#type", "BankValidator"],
|
||||
["resolveAccount", compileFinal {
|
||||
params [["_uid", "", [""]]];
|
||||
|
||||
private _account = GVAR(Registry) getOrDefault [_uid, createHashMap];
|
||||
if (_account isEqualTo createHashMap) then {
|
||||
throw "Bank account data is unavailable.";
|
||||
};
|
||||
|
||||
_account
|
||||
}],
|
||||
["validateDeposit", compileFinal {
|
||||
params [["_uid", "", [""]], ["_amount", 0, [0]]];
|
||||
|
||||
private _context = createHashMap;
|
||||
|
||||
[_uid, _amount] try {
|
||||
if (_uid isEqualTo "") then { throw "Empty/Invalid UID!" };
|
||||
if (_amount <= 0) then { throw "Enter a valid deposit amount." };
|
||||
|
||||
private _session = GVAR(BankSessionManager) call ["getSessionState", [_uid]];
|
||||
if ((_session getOrDefault ["mode", "bank"]) isEqualTo "atm") then {
|
||||
if !(_session getOrDefault ["atmAuthorized", false]) then {
|
||||
throw "ATM authorization is required before deposit.";
|
||||
};
|
||||
};
|
||||
|
||||
private _account = _self call ["resolveAccount", [_uid]];
|
||||
private _bank = _account getOrDefault ["bank", 0];
|
||||
private _cash = _account getOrDefault ["cash", 0];
|
||||
|
||||
if (_cash < _amount) then { throw "Cash on hand cannot cover that deposit." };
|
||||
|
||||
_context set ["account", _account];
|
||||
_context set ["bank", _bank];
|
||||
_context set ["cash", _cash];
|
||||
} catch {
|
||||
["ERROR", format ["Deposit validation failed: %1", _exception]] call EFUNC(common,log);
|
||||
GVAR(BankMessenger) call ["sendNotice", [_uid, "error", _exception]];
|
||||
};
|
||||
|
||||
if (_context isEqualTo createHashMap) exitWith { false };
|
||||
_context
|
||||
}],
|
||||
["validateWithdraw", compileFinal {
|
||||
params [["_uid", "", [""]], ["_amount", 0, [0]]];
|
||||
|
||||
private _context = createHashMap;
|
||||
|
||||
[_uid, _amount] try {
|
||||
if (_uid isEqualTo "") then { throw "Empty/Invalid UID!" };
|
||||
if (_amount <= 0) then { throw "Enter a valid withdrawal amount." };
|
||||
|
||||
private _session = GVAR(BankSessionManager) call ["getSessionState", [_uid]];
|
||||
if ((_session getOrDefault ["mode", "bank"]) isEqualTo "atm") then {
|
||||
if !(_session getOrDefault ["atmAuthorized", false]) then {
|
||||
throw "ATM authorization is required before withdrawal.";
|
||||
};
|
||||
};
|
||||
|
||||
private _account = _self call ["resolveAccount", [_uid]];
|
||||
private _bank = _account getOrDefault ["bank", 0];
|
||||
private _cash = _account getOrDefault ["cash", 0];
|
||||
|
||||
if (_bank < _amount) then { throw "Bank balance cannot cover that withdrawal." };
|
||||
|
||||
_context set ["account", _account];
|
||||
_context set ["bank", _bank];
|
||||
_context set ["cash", _cash];
|
||||
} catch {
|
||||
["ERROR", format ["Withdraw validation failed: %1", _exception]] call EFUNC(common,log);
|
||||
GVAR(BankMessenger) call ["sendNotice", [_uid, "error", _exception]];
|
||||
};
|
||||
|
||||
if (_context isEqualTo createHashMap) exitWith { false };
|
||||
_context
|
||||
}],
|
||||
["validateTransfer", compileFinal {
|
||||
params [["_uid", "", [""]], ["_target", "", [""]], ["_from", "", [""]], ["_amount", 0, [0]]];
|
||||
|
||||
private _context = createHashMap;
|
||||
|
||||
[_uid, _target, _from, _amount] try {
|
||||
if (_uid isEqualTo "") then { throw "Empty/Invalid UID!" };
|
||||
if (_uid isEqualTo _target) then { throw "You cannot transfer funds to yourself." };
|
||||
if (_amount <= 0) then { throw "Enter a valid transfer amount." };
|
||||
|
||||
private _session = GVAR(BankSessionManager) call ["getSessionState", [_uid]];
|
||||
if ((_session getOrDefault ["mode", "bank"]) isNotEqualTo "bank") then {
|
||||
throw "Transfers are only available from the full bank interface.";
|
||||
};
|
||||
|
||||
private _account = _self call ["resolveAccount", [_uid]];
|
||||
|
||||
private _targetAccount = GVAR(Registry) getOrDefault [_target, createHashMap];
|
||||
if (_targetAccount isEqualTo createHashMap) then {
|
||||
_targetAccount = GVAR(BankStore) call ["init", [_target]];
|
||||
};
|
||||
if (_targetAccount isEqualTo createHashMap) then {
|
||||
throw "Selected transfer recipient is unavailable.";
|
||||
};
|
||||
|
||||
private _sourceField = ["bank", "cash"] select (toLowerANSI _from isEqualTo "cash");
|
||||
private _selected = _account getOrDefault [_sourceField, 0];
|
||||
if (_selected < _amount) then {
|
||||
private _message = [
|
||||
"Bank balance cannot cover that transfer.",
|
||||
"Cash on hand cannot cover that transfer."
|
||||
] select (_sourceField isEqualTo "cash");
|
||||
throw _message;
|
||||
};
|
||||
|
||||
_context set ["account", _account];
|
||||
_context set ["targetAccount", _targetAccount];
|
||||
_context set ["sourceField", _sourceField];
|
||||
_context set ["sourceBalance", _selected];
|
||||
_context set ["targetBank", _targetAccount getOrDefault ["bank", 0]];
|
||||
} catch {
|
||||
["ERROR", format ["Transfer validation failed: %1", _exception]] call EFUNC(common,log);
|
||||
GVAR(BankMessenger) call ["sendNotice", [_uid, "error", _exception]];
|
||||
};
|
||||
|
||||
if (_context isEqualTo createHashMap) exitWith { false };
|
||||
_context
|
||||
}],
|
||||
["validateDepositEarnings", compileFinal {
|
||||
params [["_uid", "", [""]], ["_amount", 0, [0]]];
|
||||
|
||||
private _context = createHashMap;
|
||||
|
||||
[_uid, _amount] try {
|
||||
if (_uid isEqualTo "") then { throw "Empty/Invalid UID!" };
|
||||
|
||||
private _session = GVAR(BankSessionManager) call ["getSessionState", [_uid]];
|
||||
if ((_session getOrDefault ["mode", "bank"]) isNotEqualTo "bank") then {
|
||||
throw "Earnings deposits are only available from the full bank interface.";
|
||||
};
|
||||
|
||||
if (_amount <= 0) then { throw "No earnings are available to deposit." };
|
||||
|
||||
private _account = _self call ["resolveAccount", [_uid]];
|
||||
private _bank = _account getOrDefault ["bank", 0];
|
||||
private _earnings = _account getOrDefault ["earnings", 0];
|
||||
|
||||
if (_earnings < _amount) then { throw "Pending earnings cannot cover that deposit request." };
|
||||
|
||||
_context set ["account", _account];
|
||||
_context set ["bank", _bank];
|
||||
_context set ["earnings", _earnings];
|
||||
} catch {
|
||||
["ERROR", format ["DepositEarnings validation failed: %1", _exception]] call EFUNC(common,log);
|
||||
GVAR(BankMessenger) call ["sendNotice", [_uid, "error", _exception]];
|
||||
};
|
||||
|
||||
if (_context isEqualTo createHashMap) exitWith { false };
|
||||
_context
|
||||
}],
|
||||
["validatePayment", compileFinal {
|
||||
params [["_uid", "", [""]], ["_amount", 0, [0]]];
|
||||
|
||||
private _context = createHashMap;
|
||||
|
||||
[_uid, _amount] try {
|
||||
if (_uid isEqualTo "") then { throw "Empty/Invalid UID!" };
|
||||
if (_amount <= 0) then { throw "Enter a valid payment amount." };
|
||||
|
||||
private _account = _self call ["resolveAccount", [_uid]];
|
||||
private _bank = _account getOrDefault ["bank", 0];
|
||||
|
||||
_context set ["account", _account];
|
||||
_context set ["bank", _bank];
|
||||
} catch {
|
||||
["ERROR", format ["Payment validation failed: %1", _exception]] call EFUNC(common,log);
|
||||
GVAR(BankMessenger) call ["sendNotice", [_uid, "error", _exception]];
|
||||
};
|
||||
|
||||
if (_context isEqualTo createHashMap) exitWith { false };
|
||||
_context
|
||||
}],
|
||||
["validateSubmitPin", compileFinal {
|
||||
params [["_uid", "", [""]], ["_pin", "", [""]]];
|
||||
|
||||
private _context = createHashMap;
|
||||
|
||||
[_uid, _pin] try {
|
||||
if (_uid isEqualTo "") then { throw "Empty/Invalid UID!" };
|
||||
|
||||
private _session = GVAR(BankSessionManager) call ["getSessionState", [_uid]];
|
||||
if ((_session getOrDefault ["mode", "bank"]) isNotEqualTo "atm") then {
|
||||
_session = GVAR(BankSessionManager) call ["setSessionState", [_uid, createHashMapFromArray [
|
||||
["atmAuthorized", false],
|
||||
["mode", "atm"]
|
||||
]]];
|
||||
};
|
||||
|
||||
private _account = GVAR(Registry) getOrDefault [_uid, createHashMap];
|
||||
if (_account isEqualTo createHashMap) then {
|
||||
_account = GVAR(BankStore) call ["init", [_uid]];
|
||||
};
|
||||
if (_account isEqualTo createHashMap) then {
|
||||
throw "Bank account data is unavailable.";
|
||||
};
|
||||
|
||||
private _enteredPin = _pin;
|
||||
if !(_enteredPin isEqualType "") then {
|
||||
_enteredPin = str _enteredPin;
|
||||
};
|
||||
if ((count _enteredPin) isNotEqualTo 4) then {
|
||||
throw "Enter your four-digit access PIN.";
|
||||
};
|
||||
|
||||
private _accountPin = str (_account getOrDefault ["pin", 1234]);
|
||||
if (_enteredPin isNotEqualTo _accountPin) then {
|
||||
GVAR(BankSessionManager) call ["setSessionState", [_uid, createHashMapFromArray [["atmAuthorized", false]]]];
|
||||
throw "Incorrect PIN.";
|
||||
};
|
||||
|
||||
_context set ["account", _account];
|
||||
_context set ["session", _session];
|
||||
} catch {
|
||||
["ERROR", format ["SubmitPin validation failed: %1", _exception]] call EFUNC(common,log);
|
||||
GVAR(BankMessenger) call ["sendNotice", [_uid, "error", _exception]];
|
||||
GVAR(BankStore) call ["hydrateSession", [_uid, "atm", false]];
|
||||
};
|
||||
|
||||
if (_context isEqualTo createHashMap) exitWith { false };
|
||||
_context
|
||||
}]
|
||||
]];
|
||||
|
||||
GVAR(BankValidator)
|
||||
@ -91,18 +91,17 @@ GVAR(LockerBaseStore) = compileFinal createHashMapFromArray [
|
||||
private _category = toLowerANSI (_x getOrDefault ["category", ""]);
|
||||
private _quantity = floor ((_x getOrDefault ["quantity", 0]) max 0);
|
||||
private _lockerCategory = switch (_category) do {
|
||||
case "item": { "item" };
|
||||
case "item";
|
||||
case "attachment": { "item" };
|
||||
case "weapon": { "weapon" };
|
||||
case "magazine": { "magazine" };
|
||||
case "backpack": { "backpack" };
|
||||
default { "" };
|
||||
};
|
||||
|
||||
if (_className isEqualTo "" || { _lockerCategory isEqualTo "" } || { _quantity <= 0 }) exitWith {
|
||||
_result set ["message", "Checkout item was missing a valid classname, category, or quantity."];
|
||||
_result set ["success", false];
|
||||
};
|
||||
|
||||
if (_className isEqualTo "" || { _lockerCategory isEqualTo "" } || { _quantity <= 0 }) then {
|
||||
["WARN", format ["Skipping invalid locker grant entry: %1 (category: %2)", _className, _category]] call EFUNC(common,log);
|
||||
} else {
|
||||
private _entry = +(_locker getOrDefault [_className, createHashMap]);
|
||||
private _amount = _entry getOrDefault ["amount", 0];
|
||||
private _updatedEntry = createHashMapFromArray [
|
||||
@ -118,6 +117,7 @@ GVAR(LockerBaseStore) = compileFinal createHashMapFromArray [
|
||||
["category", _lockerCategory],
|
||||
["quantity", _quantity]
|
||||
]);
|
||||
};
|
||||
} forEach _items;
|
||||
|
||||
if ((count (keys _locker)) > 25) exitWith {
|
||||
|
||||
@ -23,7 +23,11 @@ if (isNil QEGVAR(common,BaseStore)) then { call EFUNC(common,baseStore); };
|
||||
if (isNil QEGVAR(actor,ActorStore)) then { call EFUNC(actor,initActorStore); };
|
||||
|
||||
// Bank
|
||||
if (isNil QEGVAR(bank,BankStore)) then { call EFUNC(bank,initBankStore); };
|
||||
if (isNil QEGVAR(bank,BankSessionManager)) then { call EFUNC(bank,initSessionManager); };
|
||||
if (isNil QEGVAR(bank,BankMessenger)) then { call EFUNC(bank,initMessenger); };
|
||||
if (isNil QEGVAR(bank,BankModel)) then { call EFUNC(bank,initModel); };
|
||||
if (isNil QEGVAR(bank,BankStore)) then { call EFUNC(bank,initStore); };
|
||||
if (isNil QEGVAR(bank,BankValidator)) then { call EFUNC(bank,initValidator); };
|
||||
|
||||
// Garage
|
||||
if (isNil QEGVAR(garage,GarageStore)) then { call EFUNC(garage,initGarageStore); };
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user