Page MenuHomeFeedback Tracker

2.14 HashMapObject functionality broken (CTOR mostly)
Feedback, NormalPublic

Description

[Mistakenly first posted in Forums]

With 2.14, the newly introduced HashMapObject functionality seems broken.

I observed that self in an object's constructor does not seem to be an actual HashMap object.

Using 2.14.150957 and the actual example code on the Biki (Example 4):

private _temporaryVehicle = [
	["#create", {
		params ["_vehicleType", "_vehiclePos", "_lifetimeSeconds"]; // handle constructor arguments
		private _newVehicle = _vehicleType createVehicle _vehiclePos;
		_self set ["MyVehicle", _newVehicle ]; // Store the vehicle inside the object for later

		// because _self is passed as parameter, it will be referenced by the spawned script until it ends.
		[_lifetimeSeconds, _self] spawn { params ["_lifetimeSeconds", "_self"]; sleep _lifetimeSeconds; };
	}],
	["#delete", {
		deleteVehicle (_self get "MyVehicle"); // delete the vehicle when we go away
	}],
	["MyVehicle", objNull] // placeholder, this is not needed
];

// create a temporary RoadCone, at player position, that will delete itself after 5 seconds.
createHashMapObject [_temporaryVehicle, ["RoadCone_F", getPos player, 5]];

Results in this error:

22:11:41 Error in expression <leType createVehicle _vehiclePos;
_self set ["MyVehicle", _newVehicle ]; 


[_li>
22:11:41   Error position: <set ["MyVehicle", _newVehicle ]; 


[_li>
22:11:41   Error Type String, expected Number
22:11:41 File mpmissions\scripting-test.Tanoa\initPlayerLocal.sqf..., line 5

Using _self in actual methods, however, seems to work fine.


Somebody went on to comment:

That works without error, for me. I just placed the code in a radio trigger, without any comment.

Should work also in initPlayerLocal... so, not sure to understand. Sorry.

It doesn't work in initPlayerLocal.sqf, though.

I did, however, work in the debug console.

See the attached minimal Stratis map. The RPT (no mods) is included in the ZIP file.

Details

Severity
Minor
Resolution
Open
Reproducibility
Always
Operating System
Windows 10 x64
Category
Scripting
Steps To Reproduce
  • Place example code in initPlayerLocal.sqf
  • Run the map
  • Observe above error message
Additional Information

Behavior is observable from 3DEN editor and dedicated server (Windows; current version as of the time of this report).

Event Timeline

I can confirm the error but, it seems to be related to using spawn and createhashmapobject. And it only affects the constructor... I am guessing constructor runs either scheduled or unscheduled depending on the parent scope when it should probably always be unscheduled. So, for example, using [] call { ...code... } without the uiSleep doesn't produce the error -and- I am guessing methods always work because they are 'called'. as Biki states:

Methods (if they are called correctly, either by engine or via call - Syntax 3) will have the objects instance inside the "_self" variable.

So doing a '[] spawn { createhashmapobject [....] }' seems to be putting the constructor in a scheduled state (-- is my guess). When it should probably be unscheduled as it is called 'by the engine'?

Nice detective work. I also found only the CTOR affected, but - sadly - whether scheduled or unscheduled, the error persists. (The code in the attached map was my actual second attempt; the first was without spawn()).

initPlayerLocal.sqf is ran as scheduled iirc. If you had wrapped it in a call block, I believe it would run that block unscheduled.

Yeah, no dice. Tried it wrapped, tried w/ calling a setter instead of using set... All to no avail. Remains broken.

crashdome added a comment.EditedSep 24 2023, 9:43 PM

I thought I had it working with 'call' instead of 'spawn' (which it does work if called unscheduled) but, you are right. Call in initPlayerLocal.sqf is scheduled. I don't know why I thought it wasn't. So if you wrap it using isNil, it does fix it as a workaround. This does need to be fixed though.

Here is what I put in initPlayerLocal to get it to work:

uiSleep 5;

private _temporaryVehicle = [
    	["#create", {
    		params ["_vehicleType", "_vehiclePos", "_lifetimeSeconds"]; // handle constructor arguments
    		private _newVehicle = _vehicleType createVehicle _vehiclePos;

    		_self set ["MyVehicle", _newVehicle ]; // Store the vehicle inside the object for later

    		// because _self is passed as parameter, it will be referenced by the spawned script until it ends.
    		[_lifetimeSeconds, _self] spawn { params ["_lifetimeSeconds", "_self"]; sleep _lifetimeSeconds; };
    	}],
    	["#delete", {
    		deleteVehicle (_self get "MyVehicle"); // delete the vehicle when we go away
    	}],
    	["MyVehicle", objNull] // placeholder, this is not needed
];

isNil {  //Force unscheduled in a scheduled environment
    // create a temporary RoadCone, at player position, that will delete itself after 5 seconds.
    createHashMapObject [_temporaryVehicle, ["RoadCone_F", getPos player, 5]];
};

systemChat "DONE";

Just to clarify the issue, when createHashmapObject is invoked in a scheduled code block, the variable '_self' refers to the array passed to the command and NOT the created 'hashmapObject' in the '#create' constructor method but works fine if called from an unscheduled code block.
Methods called in scheduled code block are fine. '_self' refers to the object. For example:

private _temporaryVehicle = [
 	["MyConstructor", {
 		params ["_vehicleType", "_vehiclePos", "_lifetimeSeconds"]; // handle constructor arguments
 		private _newVehicle = _vehicleType createVehicle _vehiclePos;

 		_self set ["MyVehicle", _newVehicle ]; // Store the vehicle inside the object for later

 		// because _self is passed as parameter, it will be referenced by the spawned script until it ends.
 		[_lifetimeSeconds, _self] spawn { params ["_lifetimeSeconds", "_self"]; sleep _lifetimeSeconds; };
 	}],
 	["#delete", {
 		deleteVehicle (_self get "MyVehicle"); // delete the vehicle when we go away
 	}],
 	["MyVehicle", objNull] // placeholder, this is not needed
 ];

 // create a temporary RoadCone, at player position, that will delete itself after 5 seconds.
 MyObject = createHashMapObject [_temporaryVehicle];
 MyObject call ["MyConstructor",["RoadCone_F", getPos player, 5]];
 systemChat "DONE";
This comment was removed by dedmen.
dedmen added a subscriber: dedmen.Sep 26 2023, 1:55 PM

The constructor call environment depends on the caller.
its just a call. So if called from scheduled, it will also be scheduled. If called from unscheduled, it will

also be unscheduled.

initPlayerLocal.sqf is ran as scheduled iirc. If you had wrapped it in a call block, I believe it would run that block unscheduled.

no, call doesn't change the context/scheduled-ness

dedmen added a comment.EditedSep 26 2023, 2:06 PM

Wow. I indeed F'ed up the variable passing in scheduled execution.
Instead of the new map, it passes the argument that you passed to createHashMapObject.

So if you do this

createHashMapObject [_temporaryVehicle, ["RoadCone_F", getPos player, 5]];

in unscheduled (either when you force it via flag, or when you call the code from unscheduled script) _self will be the new map that's created.
If it executes in unscheduled, then _self is [_temporaryVehicle, ["RoadCone_F", getPos player, 5]];
That doesn't match your script error message... don't quite understand that part, the argument MUST be an array.
But I definitely typoed that code.

Fixed in next profiling branch build. I don't know when/how we can get that into stable.

dedmen set Ref Ticket to AIII-55574.Sep 26 2023, 2:07 PM
dedmen changed the task status from New to Feedback.Sep 26 2023, 2:25 PM