Jump to content
  • 0

Looking for mod to prevent ammo cheat


ekroemer

Question

Hi there,

imo one of the most annoying things with A2 Epoch is that you have infinite ammo. Just keep a single bullet in the mag before reload, re-log and all mags are full, again. The same holds for any storage: put partially filled mags in and after a restart the magical ammo fairy has come.

That's a shame, especially on PvE servers where ammo should be a resource you have to scrounge for.

So, is there a mod out to prevent this?

Alternatively, what could be done about it. I could imagine a workaround that

  • would either delete a partially filled mag or, better
  • delete it with a probability of bullets_still_in_mag / mag_capacity
  • whenever a player logs out or
  • puts ammo into another storage.

But this exceeds my scripting ability, especially

  • where to put this functionality (there'll be a OnPlayerLogout, but where to catch all mag transfer into other storage?)
  • how to get current state and capacity of all ammo items in the gear.

Please tell me, someone has done this, already! ;)

Edited by ekroemer
Link to comment
Share on other sites

21 answers to this question

Recommended Posts

  • 1

That's a nice link :-)

I think, when I've time (not before the weekend), I'll activate some of the diag_logs, e.g in server_playerSync

		_playerGear = [weapons _character,_magazines];
		//diag_log ("playerGear: " +str(_playerGear));

and look at the format of _playerGear.

Then I'll hopefully find some commands to dissect that thing.

Link to comment
Share on other sites

  • 0

You do not need a mod to fix this problem. The reason why this is happening is because of a scripting issue. In ArmA 3 there is a command to add non-full mags to player's inventory but not sure if that command is also available in A2.

Link to comment
Share on other sites

  • 0

If I understand you correctly, the scripts that fill players' or objects' inventory at spawn time are flawed?

Could you be more specific about where that happens?

Also, while I've on occasion seen partially filled mag entries in the DB, I believe that has only been while the character was in-game. A logged off character seems to have full mag DB entries, only, so tweaking character setup won't be enough because the DB entry is already flawed.

Link to comment
Share on other sites

  • 0

Yes I think that script is indeed flawed. There are two ways to add a magazine to the player's inventory:

player addMagazine "30rnd_STANAG_NATO"; // Adds a full magazine of given classname
player addMagazine ["30rnd_STANAG_NATO", 2] // Adds given magazine with two bullets in it

Maybe that helps you? :)

Link to comment
Share on other sites

  • 0

Thanks!

server_playerSync looks right for saving gear and backpack to DB, and server_updateObject.sqf:_object_inventory seems to handle object inventories.

Now the 'only' thing one would have to do, would be to analyze the magazine variable (hopefully it's an array, not a string), identify elements signifying partially filled mags (assuming at this place this information is still present) and deleting them with a suitable probability RndLeft/RndMax.

I'm still afraid that exceeds my scripting ability....any hint on how to get the number of rounds in a mag from it's classname?

 

 

Link to comment
Share on other sites

  • 0

Config files seem to have the round number in them so you can more than likely fetch max rounds but i have no idea how you would go about saving how many rounds are left in each mag

https://github.com/vbawol/DayZ-Epoch/blob/master/SQF/dayz_code/Configs/CfgMagazines/Ammo.hpp

 

That's just something i found on git but can't seem to find it in client files so i guess most of them are pre-defined somewhere in arma itself

Link to comment
Share on other sites

  • 0

In the past when I asked about this, I was told you couldn't have partial mags saved to an inventory because there's no classnames for them. You'd need a classname for all possible ammo amounts. For example, 30rnd_STANAG_NATO is a STANAG magazine with 30 rounds. There's no 29rnd_STANAG_NATO for 29 rounds. 

Link to comment
Share on other sites

  • 0

That's right but imo no problem.

Shamelessly citing myself (bah, the new forum SW seems to disallow copy&paste from posts?), http://epochmod.com/forum/index.php?/topic/2704-ammo-mags-refilling-after-relog/&do=findComment&comment=22276, while a player is in game the database record shows a rather involved syntax, e.g. ["100Rnd_762x54_PK",5] while a full mag simply appears as "100Rnd_762x54_PK" (no array).

That means that in principle the ammo information is available.

I expect (will test on weekend, if I have time) that the inventory array in the sqfs shows exactly this information, that is then discarded when writing to DB.

I don't want to try to preserve the whole information - if that would be within my abilities it would have been done long ago.

But it should be possible to code something that on average will have the same effect - random choice between full and no mag, the chance depending on the fill ratio

E.g. a full 100rnd mag won't disappear ever, with 99rnds in it it has a 1% chance of not being saved to DB, with 1rnd the chance of disappearing would be 99%.

To be clear: I'm only talking about manipulating the saving of the current in-game status to DB (relevant at the last writing time, for a character that's at logut). There'll be nothing changed in-game.

Link to comment
Share on other sites

  • 0

That is correct for object_data.inventory, but I'm quite sure that in character_data multiple items in the players inventory (not backpack!) are listed separately, leading to substrings like

["mag", 3], ["mag", 1], "mag", "mag", "mag"

describing 3 full and 2 partially filled mags of the same type.

 

Thinking about it, the syntax for object_data.inventory isn't [["magA", numA], ["magB", numB]] either, but

[["magA", "magB"],[numA, numB]].

 

But be that as it may, fact is that in-game the information about rounds remaining in a partially used mag is present and thus there has to be a way of accessing it before it get's pressed into DB-format.

Link to comment
Share on other sites

  • 0

[["magA", "magB"],[numA, numB]]

The first sub-array lists all the different ammo magazines. The second sub-array lists the amount of mags for each listed mag. So, the example above does not include the amount of bullets inside the mag.

Link to comment
Share on other sites

  • 0

I know.

I detailed that to show that your claim ["mag",5] meant 5 mags of type "mag" did not apply here.

We concur that ammo count is not normally written into DB, but if you have a look at character_data for an in-game player you'll see that, sometimes, it is (but apparently never after logout).

And, anyhow, the point is moot because I want to access the information in-game, not from the DB.

Link to comment
Share on other sites

  • 0

I can confirm that the game saves ammo count in DB while you are ingame so it looks like this

[["BAF_L86A2_ACOG","ItemKeyBlack1972"],["30Rnd_556x45_Stanag",["30Rnd_556x45_Stanag",5]]]

Where second one has 5 rounds left and the other is full

However it doesn't save ammo count while mags are in your backpack, even if it's just the one with 5 rounds left.

["DZ_LargeGunBag_EP1",[[],[]],[["30Rnd_556x45_Stanag"],[1]]]

 

Edit: Unless you're looking to overwrite epoch/dayz inventory saving system there's pretty much no way to stop this

Edited by Antichrist
Link to comment
Share on other sites

  • 0

I wouldn't dream of trying lowlevel changes ;-)

But I think it will be possible to identify the entry ["30Rnd_556x45_Stanag",5] in server_playerSync's variables _playerGear and _playerBackp and to delete it there before it can be written to DB. Same for any object inventory.

Of course I'd like to make this deletion dependent on the actual amount of ammo left in relation to the full amount - but that's a secondary problem.

Regarding the backpack: here, I guess, the save-to-DB procedure is the same as for storage objects, storing only classname information. Nevertheless, in-game the round count is available: you can't refill a magazine by putting it into storage and getting it out, again without a server restart (that forces DB read instead of internal information use).

 

Link to comment
Share on other sites

  • 0

I tested a bit in server_playerSync.sqf, changing

	if (_isNewGear || _forceGear) then {
		//diag_log ("gear..."); sleep 0.05;
		_playerGear = [weapons _character,_magazines];

to

	private ["_kr_magazines", "_chanceToDelete"];
    if (_isNewGear || _forceGear) then {
		//diag_log ("gear..."); sleep 0.05;
		_kr_magazines = [];
		{ 
		  diag_log ("_magazines entry: " +str(_x)); 
          if (typeName _x == "ARRAY") then {
		    _chanceToDelete = 1;
		  } else {
		    _chanceToDelete = 0;
		  };
		  if ( random 1 > _chanceToDelete ) then {
	 	    _kr_magazines set [count _kr_magazines, _x];
		  };
		} forEach _magazines;
		diag_log ("_magazines: " +str(_magazines));
		diag_log ("_kr_magazines: " +str(_kr_magazines));

		_playerGear = [weapons _character, _kr_magazines];

Up to a point that did what I expected, namely deleting all magazines not completely filled from the array:

18:41:53 "_magazines: ["ItemPainkiller","ItemPainkiller","FoodCanFrankBeans",["100Rnd_762x51_M240",40],["100Rnd_762x51_M240",92],"100Rnd_762x51_M240","ItemBandage","ItemBandage","ItemBandage","ItemBandage","ItemBandage","ItemBandage","ItemBandage","15Rnd_9x19_M9SD"]"
18:41:53 "_kr_magazines: ["ItemPainkiller","ItemPainkiller","FoodCanFrankBeans","100Rnd_762x51_M240","ItemBandage","ItemBandage","ItemBandage","ItemBandage","ItemBandage","ItemBandage","ItemBandage","15Rnd_9x19_M9SD"]"

and propagating the new set of gear nicely into the DB.

 

But: after a while, be it after picking up something or logging off, the variables _magazines (that is a parameter to server_playerSync) reverted to

"100Rnd_762x51_M240", "100Rnd_762x51_M240", "100Rnd_762x51_M240"

instead of the

["100Rnd_762x51_M240",40],["100Rnd_762x51_M240",92],"100Rnd_762x51_M240"

it had a moment before, while, of course, in-game the mags are still only partially filled. (Of course that fits right in with my earlier impression that the DB, when a player is still in-game, sometimes, but not always shows the correct ammo status.

 

So the fault lies within the routine calling server_playerSync.

Which function, other than server_onPlayerDisconnect (that is not the culprit) calls server_playerSync?

Edit: found a call in dayz_code\system\player_monitor.fsm (Action to "Initialized"). Quite interestingly here the _magazineArray seems to be set up containing the correct ammo count.

//Primary Mags
for "_i" from 109 to 120 do 
{
	_control = 	_dialog displayCtrl _i;
	_item = 	gearSlotData _control;
	_val =		gearSlotAmmoCount _control;
	_max = 		getNumber (configFile >> "CfgMagazines" >> _item >> "count");
	if (_item != "") then {
		if (_val != _max) then {
			_magazineArray set [count _magazineArray,[_item,_val]];
		} else {
			_magazineArray set [count _magazineArray,_item];
		};
	};
};

So, when, where and why does that method fail or where is yet another call to server_playerSync?

 

Edit2: Found another one, I think. dayz_code\system\player_spawn_2.sqf has a block

	if (dayz_unsaved) then {
		if ((time - dayz_lastSave) > DZE_SaveTime) then {
			_dayzMags = if (!isNil "dayz_Magazines" && {typeName dayz_Magazines == "ARRAY"} && {count dayz_Magazines > 0}) then {dayz_Magazines} else {magazines player};
			PVDZE_plr_Save = [player,_dayzMags,false,false];
			publicVariableServer "PVDZE_plr_Save";
			dayz_unsaved = false;
			dayz_lastSave = time;
			dayz_Magazines = [];
		};
	};

Now I don't know for sure what player_spawn_2 does but it seems to care for a periodic player status update.

 

Here's what I gleaned about the mechanisms involved:

  • server_playerSync writes player state to DB and is called with an argument _magazines
  • server_playerSync is linked via addPublicVariableEventHandler to a public variable PVDZE_plr_Save
  • the magazine part of this variable is defined (correctly) in player_monitor.fsm where, simultaneously a global variable dayz_Magazines is defined but emptied afterwards. (does not make 100% sense to use it and then to delete it)
  • there is also dayz_code\compile\player_gearSync.sqf which defines dayz_Magazines in the same way that player_monitor.fsm does

The problem is the following:

  • while player_gearSync.sqf and player_monitor.fsm play nice and generate the correct magazine information,
  • server_playerSync does not use it because dayz_Magazines has been set to [].
  • Instead magazines player ist used and that generates only full mags.

 

Looking at all occurrences of dayz_Magazines I don't understand why it is set to [].

The only location where it is referenced without being set locally, before, is in player_spawn_2,sqf and there, imo, its value of [] results in information loss.

Edited by ekroemer
Link to comment
Share on other sites

  • 0

I have modified server_playerSync to

	if (_isNewGear || _forceGear) then {
		//diag_log ("gear..."); sleep 0.05;

		// build an alternative _magazine potentially skipping mags with less than full ammo
		_kr_magazines = [];
		{ 
		  diag_log ("_magazines entry: " +str(_x)); 
          if (typeName _x == "ARRAY") then {
		    _actMagClass = (_x select 0);
			_actAmmo = (_x select 1);
			_maxAmmo = getNumber (configFile >> "CfgMagazines">> _actMagClass >> "count");
		    _chanceToDelete = 1.0 - _actAmmo/_maxAmmo;
			diag_log (str(_actMagClass)+", chance to delete: " + str(_chanceToDelete));
		  } else {
		    _actMagClass = _x;
		    _chanceToDelete = 0;
		  };
		  if ( random 1 > _chanceToDelete ) then {
	 	    _kr_magazines set [count _kr_magazines, _actMagClass];
		  };
		} forEach _magazines;
		diag_log ("_magazines: " + str(_magazines));
		diag_log ("_kr_magazines: " + str(_kr_magazines));

		_playerGear = [weapons _character, _kr_magazines];

and that piece of code does what it should.

I've also changed player_monitor.sqm and player_spawn_2.sqf commenting out the lines dayz_Magazines = [];

 

But still server_playerSync sometimes is called with a parameter _magazines not containing the ammo count...I have yet to find from where.

 

Another question: where is the transfer from the player's main gear to backpack or other storage handled?

Link to comment
Share on other sites

  • 0

It seems I'm too daft to load my modified player_monitor.sqm.

In MPMission's init.sqf I'm calling custom\compiles.sqf, that one has

player_monitor = compile preprocessFileLineNumbers "custom\AntiAmmoCheat\player_monitor.sqf";

and that one has

diag_log ("player_monitor.sqf: calling modified version of player_monitor.fsm");
_id = [] execFSM "custom\AntiAmmoCheat\player_monitor.fsm";

But I don't even see the diag_log from player_monitor.sqf, let alone the from the sqm.

What am I doing wrong?

 

Edit: got it: player_monitor is not only defined in compiles.sqf but also directly in init.sqf.

 

Edit2: now I get a 'you have an outdated version of Epoch' when connecting - obviously the game does not like changing player_monitor....

 

Edit3: tried replacing player_monitor from scratch, works now and the full ammo information persists until the very end....but the DB write at disconnect still only has full mags.

Reason: server_onPlayerDisconnect has

		// prevent saving more than 20 magazine items
		_magazines = [(magazines _playerObj),20] call array_reduceSize;

		[_playerObj,_magazines,true,true,_isplayernearby] call server_playerSync;

in it and (magazines _playerObj) just fetches the classnames, not the ammo stats.

Changing that to

		// prevent saving more than 20 magazine items
		//_magazines = [(magazines _playerObj),20] call array_reduceSize;
		if ( (count (magazines _playerObj)) > 20 ) then {
		  _magazines = [(magazines _playerObj),20] call array_reduceSize;
		  [_playerObj,_magazines,true,true,_isplayernearby] call server_playerSync;
		} else {
		  [_playerObj,dayz_Magazines,true,true,_isplayernearby] call server_playerSync;
		};

finally seems to do the trick.

 

 

 

Edited by ekroemer
Link to comment
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
  • Discord

×
×
  • Create New...