[Tutorial] Saving AEG Variables to the Hive

isathar

Member
I've been playing around with this for a couple of weeks and had enough fun with it to share it here. I figured out how to save the variables needed for the power grid provided by AEG to the database, allowing for persistent power states between server restarts. In my opinion, this kind of thing could - with a little work from the modding community - become a small step towards a much-needed endgame in DayZ. Sure, restoring a power plant is not going to be something that can realistically be achieved in this kind of scenario, but 1. it's a game, and 2. with some creativity, you can come up with alternate scenarios.
For example, on my lan server, I'm saving the amount of time the plants have been running, made several crafting recipes (purified water, mechanical items, etc) require powered buildings, set the substations to fail randomly, and the power plants to fail permanently after a few days (lots of fun).

This works on Chernarus, but can easily be ported to other maps as long as you know the power grid variable names. It's also fully server-side, so clients will not need to download any extra files unless you are using the addon version of AEG (in which case they need that).

NOTES:

- A word of warning, this requires you to have access to, and be comfortable with editing your custom database. I made the setUpPowerGrid script as beginner-friendly as possible, giving relevant log output for everything I could think of that can possibly go wrong. Your server will still crash if you don't set up the new table this requires in the hive properly, though.

- Think of this as more of a modders' resource than of an actual release. It's why I posted it in this section.

- This is made for DayZ 1.7.6.1. As long as nothing changed about the custom hive call (Child:999) in the hiveExt.dll, this should work on later versions, as well.

- In case there are any differences on how you would set this up on different server packages, I'm using the reality pack and MySQL administrator to maintain the hive.


Instructions:

1. Database Preparation:

(manual)
Add a new table called 'power_status' to your database. Create the following variables in this new table:

Code:
  Variable name        |    Type    |            Flags              |      Default
-------------------------------------------------------------------------------------------------------------------
power_worldID          | INT(2)      | Primary Key, Not Null, Unsigned | your instance id
plantStatus            | VARCHAR(16) | Not Null                        | '[false,false]'
distributionStatus      | VARCHAR(40) | Not Null                        | '[false,false,false,false,false,false]'
transmissionStatus      | VARCHAR(52) | Not Null                        | '[false,false,false,false,false,false,false,false]'
distributionFunctional  | VARCHAR(40) | Not Null                        | '[false,false,false,false,false,false]'
transmissionFunctional  | VARCHAR(52) | Not Null                        | '[false,false,false,false,false,false,false,false]'
-------------------------------------------------------------------------------------------------------------------

(script)
If you don't want to manually add all that, execute something like this in your database administration application:
Code:
delimiter $$
 
CREATE TABLE `power_status` (
  `power_worldID` int(2) unsigned NOT NULL AUTO_INCREMENT,
  `plantStatus` varchar(16) NOT NULL DEFAULT '[false,false]',
  `distributionStatus` varchar(40) NOT NULL DEFAULT '[false,false,false,false,false,false]',
  `transmissionStatus` varchar(52) NOT NULL DEFAULT '[false,false,false,false,false,false,false,false]',
  `distributionFunctional` varchar(40) NOT NULL DEFAULT '[false,false,false,false,false,false]',
  `transmissionFunctional` varchar(52) NOT NULL DEFAULT '[false,false,false,false,false,false,false,false]',
  PRIMARY KEY (`power_worldID`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8$$

Some examples of variations in the above default values:
- All power on: Change everything to true.
- Everything operational, but not transmitting (best if you don't plan to add breaker repair actions):
Set everything in 'distributionFunctional' and 'transmissionfunctional' to true
- only power plants/smokestacks on:
Change 'plantStatus' to '[true,true]'

continued in the next post.
 
2. Scripts:
-I'm only going to post instructions on how to install this in your mission file, server pbo instructions are in the linked github repo.

Create a folder named AEG_hive in your mission folder ad the following two scripts in it:

server_setUpPowerGrid.sqf
Code:
private["_customKey","_PowerVars","_powerPlantStatus","_distributionStatus","_transmissionStatus"];

if (isServer) then {
    sleep 180;    //wait to let everything else initialize
    diag_log ("AEG_Hive: Initializing Powergrid...");

    //    init hive stream
    _customKey = format["CHILD:999: select plantStatus, distributionStatus, transmissionStatus, distributionFunctional, transmissionFunctional from power_status WHERE power_WorldID = %1:[0]:",dayZ_instance];
    _PowerVars = _customKey call server_hiveReadWrite;
    diag_log(format["AEG_Hive:READ:Debug_Count: %1",_PowerVars]);
    if (count _PowerVars > 1) then {
        if ((_PowerVars select 1) > 0) then {
        //get values from database
            _customKey = format["CHILD:999: select plantStatus, distributionStatus, transmissionStatus, distributionFunctional, transmissionFunctional from power_status WHERE power_WorldID = %1:[0]:",dayZ_instance];
            _PowerVars = _customKey call server_hiveReadWrite;
            diag_log(format["AEG_Hive:READ:SUCCESS: %1",_PowerVars]);

        // convert imported string values to arrays
            _powerPlantStatus =    call compile (_PowerVars select 0);
            _distributionStatus =    call compile (_PowerVars select 1);
            _transmissionStatus =    call compile (_PowerVars select 2);
            _distributionFunct =    call compile (_PowerVars select 3);
            _transmissionFunct =    call compile (_PowerVars select 4);

        //    plantStatus = (covers plant and smokestack output, operational)
        //    [(Cherno PP on/off),(Elektro PP on/off)]
            if (count _powerPlantStatus > 0) then {

            //output
                AEG_out_P_Cher = _powerPlantStatus select 0;
                publicVariable "AEG_out_P_Cher";
                AEG_out_S_Cher = _powerPlantStatus select 0;
                publicVariable "AEG_out_S_Cher";
                sleep 1;

            //operational
                AEG_op_P_Cher = _powerPlantStatus select 0;
                publicVariable "AEG_op_P_Cher";
                AEG_op_S_Cher = _powerPlantStatus select 0;
                publicVariable "AEG_op_S_Cher";
                sleep 5;

                AEG_out_P_Elek = _powerPlantStatus select 1;
                publicVariable "AEG_out_P_Elek";
                AEG_out_S_Elek = _powerPlantStatus select 1;
                publicVariable "AEG_out_S_Elek";
                sleep 1;
                AEG_op_P_Elek = _powerPlantStatus select 1;
                publicVariable "AEG_op_P_Elek";
                AEG_op_S_Elek = _powerPlantStatus select 1;
                publicVariable "AEG_op_S_Elek";
                sleep 5;

            //    distributionStatus =
            //    [AEG_out_D_Bere, AEG_out_D_Soln, AEG_out_D_Zele, AEG_out_D_Cher, AEG_out_D_SZag, AEG_out_D_Elek]
                if (count _distributionStatus > 0) then {
                    AEG_out_D_Bere = _distributionStatus select 0;
                    publicVariable "AEG_out_D_Bere";
                    AEG_out_D_Soln = _distributionStatus select 1;
                    publicVariable "AEG_out_D_Soln";
                    AEG_out_D_Zele = _distributionStatus select 2;
                    publicVariable "AEG_out_D_Zele";
                    AEG_out_D_Cher = _distributionStatus select 3;
                    publicVariable "AEG_out_D_Cher";
                    AEG_out_D_SZag = _distributionStatus select 4;
                    publicVariable "AEG_out_D_SZag";
                    AEG_out_D_Elek = _distributionStatus select 5;
                    publicVariable "AEG_out_D_Elek";
                    sleep 5;

                //    transmissionStatus =
                //    [AEG_out_T_Zele_1, AEG_out_T_Zele_2, AEG_out_T_Cher_1, AEG_out_T_SZag_1, AEG_out_T_SZag_2, AEG_out_T_SZag_3, AEG_out_T_Elek_1, AEG_out_T_Elek_2]
                    if (count _transmissionStatus > 0) then {
                        AEG_out_T_Zele_1 = _transmissionStatus select 0;
                        publicVariable "AEG_out_T_Zele_1";
                        AEG_out_T_Zele_2 = _transmissionStatus select 1;
                        publicVariable "AEG_out_T_Zele_2";
                        AEG_out_T_Cher_1 = _transmissionStatus select 2;
                        publicVariable "AEG_out_T_Cher_1";
                        AEG_out_T_SZag_1 = _transmissionStatus select 3;
                        publicVariable "AEG_out_T_SZag_1";
                        AEG_out_T_SZag_2 = _transmissionStatus select 4;
                        publicVariable "AEG_out_T_SZag_2";
                        AEG_out_T_SZag_3 = _transmissionStatus select 5;
                        publicVariable "AEG_out_T_SZag_3";
                        AEG_out_T_Elek_1 = _transmissionStatus select 6;
                        publicVariable "AEG_out_T_Elek_1";
                        AEG_out_T_Elek_2 = _transmissionStatus select 7;
                        publicVariable "AEG_out_T_Elek_2";
                        sleep 5;

                        //    distributionFunctional =
                        //    [AEG_op_D_Bere, AEG_op_D_Soln, AEG_op_D_Zele, AEG_op_D_Cher, AEG_op_D_SZag, AEG_op_D_Elek]
                        if (count _distributionFunct > 0) then {
                            AEG_op_D_Bere = _distributionFunct select 0;
                            publicVariable "AEG_op_D_Bere";
                            AEG_op_D_Soln = _distributionFunct select 1;
                            publicVariable "AEG_op_D_Soln";
                            AEG_op_D_Zele = _distributionFunct select 2;
                            publicVariable "AEG_op_D_Zele";
                            AEG_op_D_Cher = _distributionFunct select 3;
                            publicVariable "AEG_op_D_Cher";
                            AEG_op_D_SZag = _distributionFunct select 4;
                            publicVariable "AEG_op_D_SZag";
                            AEG_op_D_Elek = _distributionFunct select 5;
                            publicVariable "AEG_op_D_Elek";
                            sleep 5;

                            //    transmissionFunctional =
                            //    [AEG_op_T_Zele_1, AEG_op_T_Zele_2, AEG_op_T_Cher_1, AEG_op_T_SZag_1, AEG_op_T_SZag_2, AEG_op_T_SZag_3, AEG_op_T_Elek_1, AEG_op_T_Elek_2]
                            if (count _transmissionFunct > 0) then {
                                AEG_op_T_Zele_1 = _transmissionFunct select 0;
                                publicVariable "AEG_op_T_Zele_1";
                                AEG_op_T_Zele_2 = _transmissionFunct select 1;
                                publicVariable "AEG_op_T_Zele_2";
                                AEG_op_T_Cher_1 = _transmissionFunct select 2;
                                publicVariable "AEG_op_T_Cher_1";
                                AEG_op_T_SZag_1 = _transmissionFunct select 3;
                                publicVariable "AEG_op_T_SZag_1";
                                AEG_op_T_SZag_2 = _transmissionFunct select 4;
                                publicVariable "AEG_op_T_SZag_2";
                                AEG_op_T_SZag_3 = _transmissionFunct select 5;
                                publicVariable "AEG_op_T_SZag_3";
                                AEG_op_T_Elek_1 = _transmissionFunct select 6;
                                publicVariable "AEG_op_T_Elek_1";
                                AEG_op_T_Elek_2 = _transmissionFunct select 7;
                                publicVariable "AEG_op_T_Elek_2";
                                sleep 5;

                            //only execute if everything is working so far:
                                _id = [] execVM "AEG_Hive\server_savePowerGrid.sqf";
                            } else {
                                diag_log ("AEG_Hive:READ:FAILURE: ...Can't read transmissionFunctional");
                            };
                        } else {
                            diag_log ("AEG_Hive:READ:FAILURE: ...Can't read distributionFunctional");
                        };
                    } else {
                        diag_log ("AEG_Hive:READ:FAILURE: ...Can't read transmissionStatus");
                    };
                } else {
                    diag_log ("AEG_Hive:READ:FAILURE: ...Can't read distributionStatus");
                };
            } else {
                diag_log ("AEG_Hive:READ:FAILURE: ...Can't read plantStatus");
            };
        } else {
            diag_log ("AEG_Hive:READ:FAILURE: ...Table is readable but not sending values");
        };
    } else {
        diag_log ("AEG_Hive:READ:FAILURE: ...Error reading table");
    };
};

continued in next post
 
Scripts continued:

server_savePowerGrid.sqf
Code:
private["_powerPlantStatus","_distributionStatus","_transmissionStatus","_custKey"];

diag_log ("AEG_Hive: Save script initialized...");
sleep 300;

while {true} do {
    diag_log ("AEG_Hive: Saving Powergrid...");

//get variables
    _powerPlantStatus = [AEG_op_P_Cher,AEG_op_P_Elek];
    _distributionStatus = [AEG_out_D_Bere,AEG_out_D_Soln,AEG_out_D_Zele,AEG_out_D_Cher,AEG_out_D_SZag,AEG_out_D_Elek];
    _transmissionStatus = [AEG_out_T_Zele_1,AEG_out_T_Zele_2,AEG_out_T_Cher_1,AEG_out_T_SZag_1,AEG_out_T_SZag_2,AEG_out_T_SZag_3,AEG_out_T_Elek_1,AEG_out_T_Elek_2];
    _distributionFunct = [AEG_op_D_Bere,AEG_op_D_Soln,AEG_op_D_Zele,AEG_op_D_Cher,AEG_op_D_SZag,AEG_op_D_Elek];
    _transmissionFunct = [AEG_op_T_Zele_1,AEG_op_T_Zele_2,AEG_op_T_Cher_1,AEG_op_T_SZag_1,AEG_op_T_SZag_2,AEG_op_T_SZag_3,AEG_op_T_Elek_1,AEG_op_T_Elek_2];
//format to key and send
    _custKey = format ["CHILD:999:update `power_status` set `plantStatus` = '%1', `distributionStatus` = '%2', `transmissionStatus` = '%3', `distributionFunctional` = '%4', `transmissionFunctional` = '%5' where `power_WorldID` = %6:[0]:",_powerPlantStatus,_distributionStatus,_transmissionStatus,_distributionFunct,_transmissionFunct,dayZ_instance];
    diag_log (format["AEG_Hive:WRITE: %1",_custKey]);
    _custKey call server_hiveWrite;
    diag_log ("AEG_Hive: ...Finished Powergrid Save");
    sleep 300;
};
diag_log ("AEG_Hive:INFO: ...Save script terminated");

Now add something like this to your init.sqf (either at or close to the bottom):

Code:
if (isDedicated) then {
    _pg = [] execVM "AEG_Hive\server_setUpPowerGrid.sqf";
};
The comments in these scripts should explain what's going on and what the variables mean pretty well, if not, feel free to post a question.


3. Making AEG JIP-compatible

- This ensures that players logging in and respawning will have the correct power grid variables.

I don't know if this is the 'proper' way to do this, but I've had success adding anything I need executed on JIP to the bottom of the Initialize state in the player monitor.

To override your player monitor in the mission file, add a folder called 'custom' to it, and copy dayz_code\system\player_monitor.sqf and .fsm to it. Then alter player_monitor.sqf with the following:
replace
_id = [] execFSM "\z\addons\dayz_code\system\player_monitor.fsm";
with

_id = [] execFSM "custom\player_monitor.fsm";

Open the copy of player_monitor.fsm in the fsm editor, and add the following to the bottom of the 'Initialize' state:
_nul = [] execVM "AEG\Scripts\AEG_JIP.sqf";

Now add the following to your init.sqf (after the dayz-specific compiles) to override the original player monitor:
player_monitor = compile preprocessFileLineNumbers "custom\player_monitor.sqf";

That should be it, let me know if I missed anything or am making no sense :D

Credits:
All I did was use existing function from Dayz to save variables from AEG, so all credit should go to the creators of those two mods: loyalguard and rocket.


-- Sorry about splitting this up into three posts, I didn't think about the character limit when I wrote this.
 
Back
Top