Compare commits

..

11 Commits

Author SHA1 Message Date
github-actions
016a204499 v1.0.0 Build 11 2025-05-11 06:20:45 +00:00
Jacob Schmidt
461a678f8b feat: Implement timesheet submission and rating system in bank addon
All checks were successful
Build / Build (push) Successful in 25s
This commit introduces the ability for players to submit timesheets and receive payment based on their rating. It also includes updates to player data handling to incorporate the new rating system.

Key changes include:

- Added `getPlayer` PREP.
- Implemented `BANK::SUBMIT::TIMESHEET` case in `XEH_preInit_server.sqf` to handle timesheet submissions.
- Modified `fnc_initBankStore.sqf` to include rating in player data and update account information.
- Added `submitTimesheet` function to `fnc_initBankStore.sqf` to process timesheet submissions and pay players based on rating.
- Updated `handlePlayerLoad` and `updateAccount` functions in `fnc_initBankStore.sqf` to include rating.
- Added `getRating` function to `fnc_initBankStore.sqf` to retrieve player rating.
- Updated `BANK::HANDLE::PLAYER::LOAD` case in `XEH_preInit_server.sqf` to include rating value.
- Added database saving for reputation.

These changes introduce a new gameplay mechanic and provide a way for players to earn money based on their performance.
2025-05-11 01:19:58 -05:00
github-actions
fb7c198e56 v1.0.0 Build 10 2025-05-10 22:50:38 +00:00
Jacob Schmidt
2ff4a37677 Merge branch 'master' of https://gitea.innovativedevsolutions.org/IDSolutions/server
All checks were successful
Build / Build (push) Successful in 38s
2025-05-10 17:50:25 -05:00
Jacob Schmidt
991ee5019a feat: Enhance admin and bank event handling with improved logging
This commit refines the event handling for both admin and bank functionalities, ensuring better clarity and consistency in logging.

Key changes include:

- Updated event names in admin handling to follow a consistent naming convention.
- Improved parameter handling in admin event functions, ensuring validation checks are in place.
- Enhanced logging messages to provide clearer context regarding the source of events and actions taken.
- Introduced bank event handling for deposit, transfer, withdrawal, and balance inquiries, with appropriate validation and logging.

These changes aim to improve the maintainability and readability of the codebase while ensuring robust event management.
2025-05-10 17:50:21 -05:00
github-actions
17d5942de4 v1.0.0 Build 9 2025-05-05 01:03:33 +00:00
Jacob Schmidt
74b9f78141 Merge branch 'master' of https://gitea.innovativedevsolutions.org/IDSolutions/server
All checks were successful
Build / Build (push) Successful in 25s
2025-05-04 20:03:03 -05:00
Jacob Schmidt
db238970f7 feat: Update server mod icons and properties
This commit updates the server mod with new icons and properties.

The following changes were made:

- Updated `picture` to `title_ca.paa` in `mod.cpp`.
- Updated `logoSmall` to `icon_64_ca.paa` in `mod.cpp`.
- Updated `logo` to `icon_128_ca.paa` in `mod.cpp`.
- Updated `logoOver` to `icon_128_highlight_ca.paa` in `mod.cpp`.
- Added `dlcColor` to `mod.cpp`.
- Updated binary files for `icon_128_highlight_ca.paa`, `icon_64_ca.paa`, and `icon_128_ca.paa`.
2025-05-04 20:02:30 -05:00
github-actions
ba88f1ccbd v1.0.0 Build 8 2025-05-04 00:33:58 +00:00
Jacob Schmidt
eec98d03eb feat: Implement admin event handling and improve player data saving
All checks were successful
Build / Build (push) Successful in 25s
This commit introduces admin event handling to manage various administrative actions and enhances the player data saving process.

The following changes were made:

-   Implemented event handling for admin actions such as advancing funds, handling paydays, transferring funds, sending messages, and updating paygrades. These events are triggered via the `QGVAR(handleEvents)` event handler.
-   Added `initAdminStore` and `verifyAdminStore` functions to manage the admin store.
-   Refactored the `fnc_handleDisconnect.sqf` script to use the `GETVAR` macro for retrieving player data, ensuring consistency and readability.
-   Replaced hardcoded default values with `QUOTE()` wrapped values in `fnc_handleDisconnect.sqf` for better maintainability and configuration.
2025-05-03 19:33:45 -05:00
Jacob Schmidt
a1fc5090ad Removed sqf_validator.py tool 2025-04-05 14:30:14 -05:00
22 changed files with 584 additions and 239 deletions

24
.vscode/tasks.json vendored
View File

@ -1,24 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Start Arma 3 Server",
"type": "process",
"command": "wscript.exe",
"args": [
"D:\\SteamLibrary\\steamapps\\common\\Arma 3\\start_serverhub_hidden.vbs"
],
"presentation": {
"reveal": "silent",
"panel": "shared",
"showReuseMessage": false,
"clear": true
},
"problemMatcher": [],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

View File

@ -1 +1,3 @@
PREP(initAdmin);
PREP(initAdmin);
PREP(initAdminStore);
PREP(verifyAdminStore);

View File

@ -1,3 +1,56 @@
#include "script_component.hpp"
call FUNC(initAdmin);
call FUNC(initAdmin);
call FUNC(initAdminStore);
[QGVAR(handleEvents), {
params ["_event", "_data"];
diag_log format ["[FORGE::Server::Admin::XEH_preInit] Received event: %1 with data: %2", _event, _data];
switch (_event) do {
case "ADMIN::ADVANCE::ALL": {
private _adminStore = call FUNC(verifyAdminStore);
_data params [["_amount", 0, [0]]];
if (_amount isEqualTo 0) exitWith { diag_log "[FORGE::Server::Admin::XEH_preInit::advanceAll] Invalid amount!"; };
_adminStore call ["handleTransfer", ["advanceAll", _amount]];
};
case "ADMIN::PAYDAY": {
private _adminStore = call FUNC(verifyAdminStore);
_adminStore call ["handleTransfer", ["payday"]];
};
case "ADMIN::TRANSFER": {
private _adminStore = call FUNC(verifyAdminStore);
_data params [["_condition", "", [""]], ["_amount", 0, [0]], ["_uid", "", [""]]];
if (_condition isEqualTo "" || _amount isEqualTo 0 || _uid isEqualTo "") exitWith { diag_log "[FORGE::Server::Admin::XEH_preInit::handleTransfer] Invalid condition, amount, or UID!"; };
_adminStore call ["handleTransfer", [_condition, _amount, _uid]];
};
case "ADMIN::SEND::MESSAGE": {
private _adminStore = call FUNC(verifyAdminStore);
_data params [["_uid", "", [""]], ["_message", "", [""]]];
if (_message isEqualTo "") exitWith { diag_log "[FORGE::Server::Admin::XEH_preInit::sendMessage] Invalid message!"; };
if (_uid isEqualTo "") then {
_adminStore call ["broadcastMessage", [_message]];
} else {
_adminStore call ["sendMessage", [_uid, _message]];
};
};
case "ADMIN::UPDATE::PAYGRADE": {
private _adminStore = call FUNC(verifyAdminStore);
_data params [["_uid", "", [""]], ["_paygrade", "", [""]]];
if (_uid isEqualTo "" || _paygrade isEqualTo "") exitWith { diag_log "[FORGE::Server::Admin::XEH_preInit::updatePaygrade] Invalid UID or paygrade!"; };
_adminStore call ["updatePaygrade", [_uid, _paygrade]];
};
default {
diag_log format ["[FORGE::Server::Admin::XEH_preInit] Unknown event: %1 with data: %2", _event, _data];
};
};
}] call CFUNC(addEventHandler);

View File

@ -13,6 +13,6 @@
_cpof allowDamage false;
_cpof setVariable ["isCPOF", true, true];
diag_log text format ["[FORGE Admin] ClassName: '%1' Pos: '%2' Dir: '%3'", _className, _pos, _dir];
diag_log text format ["[FORGE::Server::Admin::initAdmin] ClassName: '%1' Pos: '%2' Dir: '%3'", _className, _pos, _dir];
} forEach ("true" configClasses (missionConfigFile >> "CfgCpofs" >> "cpofs"));

View File

@ -0,0 +1,119 @@
#include "..\script_component.hpp"
/*
* Function: forge_server_admin_fnc_initAdminStore
* Author: IDSolutions
*
* Description:
* Initializes a server-side admin store interface for managing admin operations
* Provides CRUD operations for admin data, including database persistence through ArmaDragonflyClient
*
* Creates a hashMap object with methods for:
* - Sending messages to players
* - Managing player data
* - Performing administrative tasks
*
* Returns: <HASHMAP>
*
* Example:
* private _adminStore = call forge_server_admin_fnc_initAdminStore;
* _adminStore call ["sendMessage", [getPlayerUID player, "Hello, this is a test message"]];
*/
private _adminStore = createHashMapObject [[
["#type", "IAdminStore"],
["broadcastMessage", {
params ["_message"];
[format ["Incoming Message from Field Commander: <br/>%1", _message], "warning", 5] remoteExec ["forge_client_misc_fnc_notify", 0];
}],
["sendMessage", {
params ["_uid", "_message"];
private _target = objNull;
{
if (getPlayerUID _x == _uid) exitWith { _target = _x; };
} forEach allPlayers;
if (!isNull _target) then {
[format ["Incoming Message from Field Commander: <br/>%1", _message], "warning", 5] remoteExec ["forge_client_misc_fnc_notify", _target];
};
}],
["updatePaygrade", {
params ["_uid", "_paygrade"];
private _target = objNull;
{
if (getPlayerUID _x == _uid) exitWith { _target = _x; };
} forEach allPlayers;
if (!isNull _target) then { SETPVAR(_target,FORGE_PayGrade,_paygrade); };
}],
["handleTransfer", {
params ["_condition", "_amount", "_uid"];
switch (_condition) do {
case ("advance"): {
private _target = objNull;
{
if (getPlayerUID _x == _uid) exitWith { _target = _x; };
} forEach allPlayers;
if (isNull _target) exitWith {};
private _bank = GETVAR(_target,FORGE_Bank,0);
private _newBalance = _bank + _amount;
SETPVAR(_target,FORGE_Bank,_newBalance);
};
case ("advanceAll"): {
{
private _player = _x;
private _bank = GETVAR(_player,FORGE_Bank,0);
private _newBalance = _bank + _amount;
SETPVAR(_player,FORGE_Bank,_newBalance);
} forEach allPlayers;
};
case ("deduct"): {
private _target = objNull;
{
if (getPlayerUID _x == _uid) exitWith { _target = _x; };
} forEach allPlayers;
if (isNull _target) exitWith {};
private _bank = GETVAR(_target,FORGE_Bank,0);
private _newBalance = _bank - _amount;
if (_newBalance < 0) then { _newBalance = 0; };
SETPVAR(_target,FORGE_Bank,_newBalance);
};
case ("payday"): {
private _payGrades = (missionConfigFile >> "CfgPaygrades" >> "payGrades") call BIS_fnc_getCfgData;
{
private _player = _x;
private _payGrade = GETVAR(_player,FORGE_PayGrade,nil);
{
_x params ["_payGradeIndex", "_payGradeBonus"];
if (_payGradeIndex == _payGrade) then {
private _bank = GETVAR(_player,FORGE_Bank,0);
private _newBalance = _bank + _payGradeBonus;
SETPVAR(_player,FORGE_Bank,_newBalance);
};
} forEach _payGrades;
} forEach allPlayers;
};
};
}]
]];
SETMVAR(FORGE_ADMIN_STORE_REG,_adminStore);
GETMVAR(FORGE_ADMIN_STORE_REG,_adminStore);

View File

@ -0,0 +1,32 @@
#include "..\script_component.hpp"
/*
* Function: forge_server_admin_fnc_verifyAdminStore
* Author: IDSolutions
*
* [Description]
* Ensures the admin store is initialized and returns the store object.
* Acts as a singleton accessor for the admin store interface.
* If the store doesn't exist yet, it initializes it first.
*
* Arguments:
* None
*
* Return Value:
* Admin Store <OBJECT> - The admin store interface object
*
* Example:
* private _adminStore = call forge_server_admin_fnc_verifyAdminStore
*
* Public: No
*/
private _store = GETMVAR(FORGE_ADMIN_STORE_REG,nil);
if (isNil "_store") then {
_store = call FUNC(initAdminStore);
diag_log text "[FORGE::Server::Admin::verifyAdminStore] Admin store initialized";
};
_store

View File

@ -1 +1,3 @@
PREP(initBank);
PREP(initBank);
PREP(initBankStore);
PREP(verifyBankStore);

View File

@ -1,3 +1,78 @@
#include "script_component.hpp"
call FUNC(initBank);
call FUNC(initBank);
call FUNC(initBankStore);
[QGVAR(handleEvents), {
params ["_event", "_data"];
diag_log text format ["[FORGE::Server::Bank::XEH_preInit] Received event: '%1' with data: '%2'", _event, _data];
switch (_event) do {
case "BANK::DEPOSIT": {
private _bankStore = call FUNC(verifyBankStore);
_data params [["_uid", "", [""]], ["_amount", 0, [0]]];
if (_uid isEqualTo "" || _amount isEqualTo 0) exitWith { diag_log "[FORGE::Server::Bank::XEH_preInit::handleDeposit] Invalid UID and amount, UID cannot be empty and amount must be greater than 0!"; };
_bankStore call ["deposit", [_uid, _amount]];
};
case "BANK::SUBMIT::TIMESHEET": {
private _bankStore = call FUNC(verifyBankStore);
_data params [["_uid", "", [""]], ["_rating", 0, [0]]];
if (_uid isEqualTo "" || _rating <= 0) exitWith { diag_log "[FORGE::Server::Bank::XEH_preInit::handleSubmitTimesheet] Invalid UID, UID cannot be empty!"; };
_bankStore call ["submitTimesheet", [_uid, _rating]];
};
case "BANK::TRANSFER": {
private _bankStore = call FUNC(verifyBankStore);
_data params [["_fromUid", "", [""]], ["_toUid", "", [""]], ["_amount", 0, [0]]];
if (_fromUid isEqualTo "" || _toUid isEqualTo "" || _amount isEqualTo 0) exitWith { diag_log "[FORGE::Server::Bank::XEH_preInit::handleTransfer] Invalid UIDs and amount, UID cannot be empty and amount must be greater than 0!"; };
_bankStore call ["transfer", [_fromUid, _toUid, _amount]];
};
case "BANK::WITHDRAW": {
private _bankStore = call FUNC(verifyBankStore);
_data params [["_uid", "", [""]], ["_amount", 0, [0]]];
if (_uid isEqualTo "" || _amount isEqualTo 0) exitWith { diag_log "[FORGE::Server::Bank::XEH_preInit::handleWithdraw] Invalid UID and amount!"; };
_bankStore call ["withdraw", [_uid, _amount]];
};
case "BANK::GET::BALANCE": {
private _bankStore = call FUNC(verifyBankStore);
_data params [["_uid", "", [""]]];
if (_uid isEqualTo "") exitWith { diag_log "[FORGE::Server::Bank::XEH_preInit::handleGetBalance] Invalid UID, UID cannot be empty!"; };
_bankStore call ["getBalance", [_uid]];
};
case "BANK::GET::CASH": {
private _bankStore = call FUNC(verifyBankStore);
_data params [["_uid", "", [""]]];
if (_uid isEqualTo "") exitWith { diag_log "[FORGE::Server::Bank::XEH_preInit::handleGetCash] Invalid UID, UID cannot be empty!"; };
_bankStore call ["getCash", [_uid]];
};
case "BANK::HANDLE::PLAYER::LOAD": {
private _bankStore = call FUNC(verifyBankStore);
_data params [["_uid", "", [""]], ["_bankValue", 0, [0]], ["_cashValue", 0, [0]], ["_ratingValue", 0, [0]]];
if (_uid isEqualTo "") exitWith { diag_log "[FORGE::Server::Bank::XEH_preInit::handlePlayerLoad] Invalid UID, UID cannot be empty!"; };
_bankStore call ["handlePlayerLoad", [_uid, _bankValue, _cashValue, _ratingValue]];
};
case "BANK::GET::TRANSACTION::HISTORY": {
private _bankStore = call FUNC(verifyBankStore);
_data params [["_uid", "", [""]], ["_limit", 10, [0]]];
_bankStore call ["getTransactionHistory", [_uid, _limit]];
};
default {
diag_log format ["[FORGE::Server::Bank::XEH_preInit] Unknown event: %1 with data: %2", _event, _data];
};
};
}] call CFUNC(addEventHandler);

View File

@ -26,5 +26,5 @@
_bank setVariable ["BIS_enableRandomization", false];
};
diag_log text format ["[FORGE Bank] ClassName: '%1' Pos: '%2' Dir: '%3'", _className, _pos, _dir];
diag_log text format ["[FORGE::Server::Bank::initBank] ClassName: '%1' Pos: '%2' Dir: '%3'", _className, _pos, _dir];
} forEach ("true" configClasses (missionConfigFile >> "CfgBanks" >> "banks"));

View File

@ -0,0 +1,227 @@
#include "..\script_component.hpp"
/*
* Author: IDSolutions
* Initializes a server-side bank store interface for managing banking operations.
* Provides CRUD operations for bank data, including database persistence.
*
* Arguments:
* None
*
* Return Value:
* <HASHMAP> Bank store object
*
* Example:
* private _bankStore = call forge_server_bank_fnc_initBank;
* _bankStore call ["deposit", [getPlayerUID player, 1000]];
*
* Public: Yes
*/
private _bankStore = createHashMapObject [[
["#type", "IBankStore"],
["#create", {
_self set ["accounts", createHashMap];
_self set ["transactions", []];
_self set ["isLoaded", true];
true
}],
["handlePlayerLoad", {
params [["_uid", "", [""]], ["_bankValue", 0, [0]], ["_cashValue", 0, [0]], ["_ratingValue", 0, [0]]];
if (_uid isEqualTo "") exitWith { false };
_self call ["updateAccount", [_uid, _bankValue, _cashValue, _ratingValue]];
diag_log text format ["[FORGE::Server::Bank::Store::handlePlayerLoad] Loaded player data for '%1' - Bank: '%2', Cash: '%3', Rating: '%4'", _uid, _bankValue, _cashValue, _ratingValue];
true
}],
["updateAccount", {
params [["_uid", "", [""]], ["_bankValue", 0, [0]], ["_cashValue", 0, [0]], ["_ratingValue", 0, [0]], ["_isSave", false, [false]]];
if (_uid isEqualTo "") exitWith { false };
private _accounts = _self get "accounts";
private _account = _accounts getOrDefault [_uid, createHashMap];
private _player = [_uid] call EFUNC(misc,getPlayer);
_account set ["bank", _bankValue];
_account set ["cash", _cashValue];
_account set ["rating", _ratingValue];
_accounts set [_uid, _account];
_self set ["accounts", _accounts];
private _balance = _account getOrDefault ["bank", 0];
private _cash = _account getOrDefault ["cash", 0];
SETPVAR(_player,FORGE_Bank,_balance);
SETPVAR(_player,FORGE_Cash,_cash);
SETPVAR(_player,FORGE_Rating,_ratingValue);
if (_isSave) then {
["hsetid", _uid, "bank", -1, [_bankValue]] call dragonfly_db_fnc_addTask;
["hsetid", _uid, "cash", -1, [_cashValue]] call dragonfly_db_fnc_addTask;
["hsetid", _uid, "reputation", -1, [_ratingValue]] call dragonfly_db_fnc_addTask;
};
diag_log text format ["[FORGE::Server::Bank::Store::updateAccount] Updated player data for '%1' - Bank: '%2', Cash: '%3', Rating: '%4'", _uid, _bankValue, _cashValue, _ratingValue];
true
}],
["getBalance", {
params [["_uid", "", [""]]];
if (_uid isEqualTo "") exitWith { 0 };
private _accounts = _self get "accounts";
private _account = _accounts getOrDefault [_uid, createHashMap];
_account getOrDefault ["bank", 0]
}],
["getCash", {
params [["_uid", "", [""]]];
if (_uid isEqualTo "") exitWith { 0 };
private _accounts = _self get "accounts";
private _account = _accounts getOrDefault [_uid, createHashMap];
_account getOrDefault ["cash", 0]
}],
["getRating", {
params [["_uid", "", [""]]];
if (_uid isEqualTo "") exitWith { 0 };
private _accounts = _self get "accounts";
private _account = _accounts getOrDefault [_uid, createHashMap];
_account getOrDefault ["rating", 0]
}],
["deposit", {
params [["_uid", "", [""]], ["_amount", 0, [0]]];
if (_uid isEqualTo "" || _amount <= 0) exitWith { false };
private _accounts = _self get "accounts";
private _account = _accounts getOrDefault [_uid, createHashMap];
private _currentCash = _account getOrDefault ["cash", 0];
private _currentBalance = _account getOrDefault ["bank", 0];
private _currentRating = _account getOrDefault ["rating", 0];
if (_currentCash < _amount) exitWith { false };
private _newBalance = _currentBalance + _amount;
private _newCash = _currentCash - _amount;
_self call ["updateAccount", [_uid, _newBalance, _newCash, _currentRating, true]];
private _transactions = _self get "transactions";
_transactions pushBack [_uid, "deposit", _amount, time];
_self set ["transactions", _transactions];
true
}],
["submitTimesheet", {
params [["_uid", "", [""]], ["_rating", 0, [0]]];
if (_uid isEqualTo "" || _rating <= 0) exitWith { false };
private _accounts = _self get "accounts";
private _account = _accounts getOrDefault [_uid, createHashMap];
private _currentBalance = _account getOrDefault ["bank", 0];
private _currentCash = _account getOrDefault ["cash", 0];
private _payMultiplyer = "MULTIPLYR" call BFUNC(getParamValue);
private _multiplyer = _rating * _payMultiplyer;
private _newBalance = _currentBalance + _multiplyer;
_self call ["updateAccount", [_uid, _newBalance, _currentCash, 0, true]];
private _transactions = _self get "transactions";
_transactions pushBack [_uid, "timesheet", _multiplyer, time];
_self set ["transactions", _transactions];
true
}],
["transfer", {
params [["_fromUid", "", [""]], ["_toUid", "", [""]], ["_amount", 0, [0]]];
if (_fromUid isEqualTo "" || _toUid isEqualTo "" || _amount <= 0) exitWith { false };
private _accounts = _self get "accounts";
private _fromAccount = _accounts getOrDefault [_fromUid, createHashMap];
private _toAccount = _accounts getOrDefault [_toUid, createHashMap];
private _fromBalance = _fromAccount getOrDefault ["bank", 0];
private _toBalance = _toAccount getOrDefault ["bank", 0];
private _fromCash = _fromAccount getOrDefault ["cash", 0];
private _toCash = _toAccount getOrDefault ["cash", 0];
private _fromRating = _fromAccount getOrDefault ["rating", 0];
private _toRating = _toAccount getOrDefault ["rating", 0];
if (_fromBalance < _amount) exitWith { false };
_self call ["updateAccount", [_fromUid, _fromBalance - _amount, _fromCash, _fromRating, true]];
_self call ["updateAccount", [_toUid, _toBalance + _amount, _toCash, _toRating, true]];
private _transactions = _self get "transactions";
_transactions pushBack [_fromUid, "transfer_out", _amount, time, _toUid];
_transactions pushBack [_toUid, "transfer_in", _amount, time, _fromUid];
_self set ["transactions", _transactions];
true
}],
["withdraw", {
params [["_uid", "", [""]], ["_amount", 0, [0]]];
if (_uid isEqualTo "" || _amount <= 0) exitWith { false };
private _accounts = _self get "accounts";
private _account = _accounts getOrDefault [_uid, createHashMap];
private _currentBalance = _account getOrDefault ["bank", 0];
private _currentCash = _account getOrDefault ["cash", 0];
private _currentRating = _account getOrDefault ["rating", 0];
if (_currentBalance < _amount) exitWith { false };
private _newBalance = _currentBalance - _amount;
private _newCash = _currentCash + _amount;
_self call ["updateAccount", [_uid, _newBalance, _newCash, _currentRating, true]];
private _transactions = _self get "transactions";
_transactions pushBack [_uid, "withdraw", _amount, time];
_self set ["transactions", _transactions];
true
}],
["getTransactionHistory", {
params [["_uid", "", [""]], ["_limit", 10, [0]]];
private _transactions = _self get "transactions";
private _filteredTransactions = [];
if (_uid isEqualTo "") then {
_filteredTransactions = _transactions;
} else {
{
if ((_x select 0) == _uid) then { _filteredTransactions pushBack _x; };
} forEach _transactions;
};
_filteredTransactions sort false;
_filteredTransactions resize (_limit min (count _filteredTransactions));
_filteredTransactions
}]
]];
SETMVAR(FORGE_BANK_STORE_REG,_bankStore);
GETMVAR(FORGE_BANK_STORE_REG,_bankStore);

View File

@ -0,0 +1,32 @@
#include "..\script_component.hpp"
/*
* Function: forge_server_bank_fnc_verifyBankStore
* Author: IDSolutions
*
* [Description]
* Ensures the bank store is initialized and returns the store object.
* Acts as a singleton accessor for the bank store interface.
* If the store doesn't exist yet, it initializes it first.
*
* Arguments:
* None
*
* Return Value:
* Bank Store <OBJECT> - The bank store interface object
*
* Example:
* private _bankStore = call forge_server_bank_fnc_verifyBankStore
*
* Public: No
*/
private _store = GETMVAR(FORGE_BANK_STORE_REG,nil);
if (isNil "_store") then {
_store = call FUNC(initBankStore);
diag_log text "[FORGE::Server::Bank::verifyBankStore] Bank store initialized";
};
_store

View File

@ -1,4 +1,4 @@
#define MAJOR 1
#define MINOR 0
#define PATCH 0
#define BUILD 7
#define BUILD 11

View File

@ -1,4 +1,5 @@
PREP(cargoToPairs);
PREP(getPlayer);
PREP(playSound);
PREP(playerGroup2Server);
PREP(redirectClient2Server);

View File

@ -0,0 +1,27 @@
#include "..\script_component.hpp"
/*
* Author: IDSolutions
* Gets a player object by UID.
*
* Arguments:
* 0: Player UID <STRING>
*
* Return Value:
* Player object or objNull if not found <OBJECT>
*
* Example:
* ["76561198012345678"] call forge_crate_common_fnc_getPlayer
*
* Public: Yes
*/
params ["_uid"];
private _player = objNull;
{
if ((getPlayerUID _x) isEqualTo _uid) exitWith { _player = _x; };
} forEach allPlayers;
_player

View File

@ -27,38 +27,22 @@ addMissionEventHandler ["HandleDisconnect", {
private _data = [
_uid,
// "armory_unlocks", [_unit getVariable ["Armory_Unlocks", _default_armory_unlocks]],
// "garage_unlocks", [_unit getVariable ["Garage_Unlocks", _default_garage_unlocks]],
"armory_unlocks", [GETVAR(_unit,Armory_Unlocks,_default_armory_unlocks)],
"garage_unlocks", [GETVAR(_unit,Garage_Unlocks,_default_garage_unlocks)],
// "locker", [_unit getVariable ["FORGE_Locker", []]],
// "garage", [_unit getVariable ["FORGE_Garage", []]],
"locker", [GETVAR(_unit,FORGE_Locker,[])],
"garage", [GETVAR(_unit,FORGE_Garage,[])],
// "cash", [_unit getVariable ["FORGE_Cash", 0]],
// "bank", [_unit getVariable ["FORGE_Bank", 0]],
"cash", [GETVAR(_unit,FORGE_Cash,0)],
"bank", [GETVAR(_unit,FORGE_Bank,0)],
// "number", [_unit getVariable ["FORGE_Phone_Number", "unknown"]],
// "email", [_unit getVariable ["FORGE_Email", "unknown@spearnet.mil"]],
"number", [GETVAR(_unit,FORGE_Phone_Number,"unknown")],
"email", [GETVAR(_unit,FORGE_Email,"unknown@spearnet.mil")],
// "paygrade", [_unit getVariable ["Paygrade", "E1"]],
"paygrade", [GETVAR(_unit,Paygrade,"E1")],
"number", [GETVAR(_unit,FORGE_Phone_Number,QUOTE(unknown))],
"email", [GETVAR(_unit,FORGE_Email,QUOTE(unknown@spearnet.mil))],
"paygrade", [GETVAR(_unit,FORGE_PayGrade,QUOTE(E1))],
"reputation", [rating _unit],
"loadout", [getUnitLoadout _unit],
// "holster", [_unit getVariable ["FORGE_Holster_Weapon", true]],
"holster", [GETVAR(_unit,FORGE_Holster_Weapon,true)],
"position", [getPosASLVisual _unit],
"direction", [getDirVisual _unit]
];
// if (vehicle _unit == _unit) then {
// _data pushBack "currentWeapon";
// _data pushBack [currentMuzzle _unit];
// _data pushBack "stance";
// _data pushBack [stance _unit];
// };
if (isNull objectParent _unit) then {
_data pushBack "currentWeapon";
_data pushBack [currentMuzzle _unit];

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,6 +1,7 @@
dir = "@forge_server";
name = "FORGE Server";
author = "IDSolutions";
picture = "title_co.paa";
picture = "title_ca.paa";
logoSmall = "icon_64_ca.paa";
logo = "icon_128_ca.paa";
logoOver = "icon_128_highlight_ca.paa";
@ -9,4 +10,5 @@ tooltipOwned = "IDS Owned";
overview = "FORGE Server - Official Modification";
description = "FORGE Server - Version 1.0.0";
action = "https://innovativedevsolutions.org";
actionName = "Website";
actionName = "Website";
dlcColor[] = {0.45, 0.47, 0.41, 1};

BIN
title_ca.paa Normal file

Binary file not shown.

Binary file not shown.

View File

@ -1,187 +0,0 @@
#!/usr/bin/env python3
import fnmatch
import os
import re
import sys
def valid_keyword_after_code(content, index):
for word in ["for", "do", "count", "each", "forEach", "else", "and", "not", "isEqualTo", "isNotEqualTo", "in", "call", "spawn", "execVM", "catch", "param", "select", "apply", "findIf", "remoteExec"]:
if content.find(word, index, index + len(word)) != -1:
return True
return False
def check_sqf(filepath):
errors = []
with open(filepath, "r", encoding = "utf-8", errors = "ignore") as file:
content = file.read()
# Store all brackets we find in this file, so we can validate everything on the end
brackets = []
# Used in case we are in a line comment (//)
ignore_till_eol = False
# To check if we are in a comment block
in_comment_block = False
check_if_comment = False
# Used in case we are in a comment block (/* */)
# This is true if we detect a * inside a comment block
# If the next character is a /, it means we end our comment block
check_if_closing = False
# We ignore everything inside a string
in_string = False
# Used to store the starting type of a string, so we can match that to the end of a string
string_type = ""
# Used to check for semicolon after code blocks
last_is_curly_brace = False
check_for_semicolon = False
# Extra information so we know what line we find errors at
line_number = 1
char_index = 0
for c in content:
if last_is_curly_brace:
last_is_curly_brace = False
# Test generates false positives with binary commands that take CODE as 2nd arg (e.g. findIf)
check_for_semicolon = not re.search("findIf", content, re.IGNORECASE)
# Keep track of current line number
if c == "\n":
line_number += 1
# While we are in a string, we can ignore everything else, except the end of the string
if in_string:
if c == string_type:
in_string = False
# Look for the end of this comment block
elif in_comment_block:
if c == "*":
check_if_closing = True
elif check_if_closing:
if c == "/":
in_comment_block = False
elif c != "*":
check_if_closing = False
# If we are not in a comment block, we will check if we are at the start of one or count the () {} and []
else:
# This means we have encountered a /, so we are now checking if this is an inline comment or a comment block
if check_if_comment:
check_if_comment = False
# If the next character after / is a *, we are at the start of a comment block
if c == "*":
in_comment_block = True
# Otherwise, check if we are in an line comment, / followed by another / (//)
elif c == "/":
ignore_till_eol = True
if not in_comment_block:
if ignore_till_eol:
# We are in a line comment, just continue going through the characters until we find an end of line
if c == "\n":
ignore_till_eol = False
else:
if c == '"' or c == "'":
in_string = True
string_type = c
elif c == "/":
check_if_comment = True
elif c == "\t":
errors.append(" ERROR: Found a tab on line {}.".format(line_number))
elif c in ["(", "[", "{"]:
brackets.append(c)
elif c == ")":
if not brackets or brackets[-1] in ["[", "{"]:
errors.append(" ERROR: Missing parenthesis '(' on line {}.".format(line_number))
brackets.append(c)
elif c == "]":
if not brackets or brackets[-1] in ["(", "{"]:
errors.append(" ERROR: Missing square bracket '[' on line {}.".format(line_number))
brackets.append(c)
elif c == "}":
last_is_curly_brace = True
if not brackets or brackets[-1] in ["(", "["]:
errors.append(" ERROR: Missing curly brace '{{' on line {}.".format(line_number))
brackets.append(c)
if check_for_semicolon:
# Keep reading until no white space or comments
if c not in [" ", "\t", "\n", "/"]:
check_for_semicolon = False
if c not in ["]", ")", "}", ";", ",", "&", "!", "|", "="] and not valid_keyword_after_code(content, char_index):
errors.append(" ERROR: Possible missing semicolon ';' on line {}.".format(line_number))
char_index += 1
# Compare opening and closing bracket counts
if brackets.count("(") != brackets.count(")"):
errors.append(" ERROR: Unequal number of parentheses, '(' = {}, ')' = {}.".format(brackets.count("("), brackets.count(")")))
if brackets.count("[") != brackets.count("]"):
errors.append(" ERROR: Unequal number of square brackets, '[' = {}, ']' = {}.".format(brackets.count("["), brackets.count("]")))
if brackets.count("{") != brackets.count("}"):
errors.append(" ERROR: Unequal number of curly braces, '{{' = {}, '}}' = {}.".format(brackets.count("{"), brackets.count("}")))
# Ensure includes are before block comments
if re.compile('\s*(/\*[\s\S]+?\*/)\s*#include').match(content):
errors.append(" ERROR: Found an #include after a block comment.")
return errors
def main():
print("Validating SQF")
print("--------------")
# Allow running from root directory and tools directory
root_dir = ".."
if os.path.exists("addons"):
root_dir = "."
# Check all SQF files in the project directory
sqf_files = []
for root, _, files in os.walk(root_dir):
for file in fnmatch.filter(files, "*.sqf"):
sqf_files.append(os.path.join(root, file))
sqf_files.sort()
bad_count = 0
for filepath in sqf_files:
errors = check_sqf(filepath)
if errors:
print("\nFound {} error(s) in {}:".format(len(errors), os.path.relpath(filepath, root_dir)))
for error in errors:
print(error)
bad_count += 1
print("\nChecked {} files, found errors in {}.".format(len(sqf_files), bad_count))
if bad_count == 0:
print("SQF Validation PASSED")
else:
print("SQF Validation FAILED")
return bad_count
if __name__ == "__main__":
sys.exit(main())