Community Forum

Help with Utils.appendedFunction()?

Forum Overview >> Scripting

Created21.08.2021 08:51

Pete Mueller (ClubPetey) 21.08.2021 08:51
I'm trying to understand how Utils.appendedFunction() works, because my usage of it has introduced a bug and I cannot understand why.

I'm trying to edit the capacity of a vehicle after its loaded. To do this I am appending my function to the Vehicle.loadFinished function, using the code:
function init()
Vehicle.loadFinished = Utils.appendedFunction(Vehicle.loadFinished, vehicleLoadFinished)

function vehicleLoadFinished(vehicle, i3dnode, args)
MyMod.log:debug("Vehicle Loaded: ".. vehicle.configFileName)
return vehicle.loadingState

I have verified using the debug statement that my appended function is executing for each vehicle that is loaded. However, when I add my mod into the game and my Sheep farm generates wool palettes, they are empty and never fill up.

Removing the appendedFunction() statement will make wool palettes generate and fill normally so I know this is the cause, but I'm not sure what I'm doing wrong

Some thoughts:
1. The original Vehicle.loadFinished returns the Vehcile's loadingState, so I did as well, is this wrong?
2. I'm using a sheep farm from a mod, maybe it also uses appendedFunction(), can multiple mods call appendedFunction() on the same base function?
3. Can you append functions to any base functions, or is the only supported in certain classes/areas of the code?
4. Is there a better way to edit vehicle capacity/fillUnits?

Thank for any help.

Bilbo Beutlin (BBeutlin) 21.08.2021 11:01
1. NEVER use globals like in your code 'init' and 'vehicleLoadFinished'. This will override possibly existing variables/functions with perhaps unknown side effects.
You should ALWAYS make your functions and variables local within your 'myMod={}' class. So in your code 'myMod.init()' and 'myMod.vehicleLoadFinished'.

2. In general multiple 'appendedFunction' calls don't conflict (IF they are programmed properly). Then they are simply queued.

3. Whenever appending/prepending functions you should always use the same function pattern and parameter names as in original code.

----- try this code -----

-- use initial basic code instead function 'init()'
Vehicle.loadFinished = Utils.appendedFunction(Vehicle.loadFinished, myMod.vehicleLoadFinished)

function myMod:vehicleLoadFinished(i3dNode, arguments)
-- do some stuff
return self.loadingState
----- end -----

And finally: ALWAYS check the game log. I'm not sure if your above code will give errors, but I'd guess it will.

Pete Mueller (ClubPetey) 22.08.2021 09:07
Thank you for the tips. I have moved everything inside the class. The new code looks like this.

function PriceFixing.init()
Vehicle.loadFinished = Utils.appendedFunction(Vehicle.loadFinished, PriceFixing.vehicleLoadFinished)

function PriceFixing.vehicleLoadFinished(vehicle, i3dnode, args)
PriceFixing.log:debug("Vehicle Loaded: ".. vehicle.configFileName)
return vehcile.loadingState

However, the bug still exists and removing the assignment to Vehicle.loadFinished causes the bug to go away. Check the log, and found no errors. Any other ideas?

Bilbo Beutlin (BBeutlin) 22.08.2021 15:06
A function hook in init() is perhaps too late. This is why I told you to use the basic code.
Instead your "PriceFixing.log:debug()" use a simple console "print()". Since the FS engine supports only a subset of standard LUA, the LUA debug library is probably not included and debug calls might give unpredictive results.

Pete Mueller (ClubPetey) 22.08.2021 21:38
The debug() call is a reference to my own logging class, it eventually uses print(). So now, I've stripped my mod down to something similar to what you provided.

PriceFixing = {};

function PriceFixing:vehicleLoadFinished(i3dnode, args)
print("Vehicle Loaded: ".. self.configFileName)
return self.loadingState

-- had to move this here so that function is declared first
Vehicle.loadFinished = Utils.appendedFunction(Vehicle.loadFinished, PriceFixing.vehicleLoadFinished)

Yet, the bug remains. I've verified that the modded sheep pen has no LUA scripts of its own. I've also verified this bug appear on the base-game sheep pen. AND I've found another related bug: when TABing between vehicles on the map every time I come to one specific vehicle, the game freezes and my player doesn't render in the cab.

Using SHIFT-TAB I can switch back to the previous vehicle, but I cannot perform any actions in the "frozen" vehicle and cannot TAB forward to the next vehicle, only back. Again, removing my mod allows normal TABing through all vehicles. The particular vehicle is a mod, but again, does not have any LUA scripts of it's own. Further, I have about 10 vehicles on the map, and only this one vehicle exhibits this behavior, all others work fine.

I have the Remote Debugger installed, but it's not obvious how to step through the chain of appended functions to see if something is wrong. The source for the Uitls class is not included in the game source provided with the debugger.

Side note: I did find another mod that does something similar to what I'm trying to do, that mod chose instead of appending to the Vehicle.loadFinished function to create their own custom specialization that listens to the various specialization events and overrides the values of the fillUnit specialization. This does achieve the goal, with the draw back that it would only work for changing base-game specializations, but at this point I'm stuck on why appending this function breaks the game.

Bilbo Beutlin (BBeutlin) 23.08.2021 00:26
hmm .. I've no concrete idea why the 'appendedFunction' method fails here. I've used it often without any problems.
Though the order of execution is sometimes important. It can be necessary to rename the mod so that the basic setup is executed before or after other mods, eg. "AAA_myMod" or "ZZZ_myMod".

Could also be, after "Vehicle.loadFinished()" it's too late already, perhaps conflicts then with storeItem tables. Try appending to "Vehicle.load()"

Pete Mueller (ClubPetey) 23.08.2021 02:44
Switching to appending the Vehicle.load() function works correctly. It still puzzles me why the mere act of appending to that function breaks the palettes. Still, thank you again for your help.


Pete Mueller (ClubPetey) 24.08.2021 01:37
I'm adding this for anyone else trying to change vehicle properties like I am. There were several key points that took me hours to discover. Hopefully this helps others save some time.

1. Vehicle.load() is too early in the Vehicle lifecycle to make changes. It turns out that specializations are added in Vehcile.loadFinished() which is called asynchronously. So load() returns before the vehicle (and specializations) is actually loaded. And, due to what I can only figure is some strange bug or conflict. You cannot use appendFunction() with loadFinished. The only successful way that I've found to change vehicle properties is using a custom specialization

2. If you are going to add a specializations via scripting, there is really only ONE place you can do it, and that is to use prependFunction() on VehicleTypeManager.validateVehicleTypes. After this function specialization are locked in. This function is very early in the game lifecycle, the mission has not been created yet, nor have most of the manager (g_specialzationManager and g_vehcileTypeManager are available). Use this function to both register your custom specialization with g_specializationManager:addSpecialization() and attach it to vehicleTypes with g_vehicleTypeManager:addSpecialization

3. The LUADOC isn't always clear what is a static class, and what instanced. Use the global instance g_vehicleTypeManager to access functions in the VehcileTypeManager class.

4. You do not load your specialization lua file like other script files. g_specializationManager:addSpecialization() takes four parameters. The name of your specialization, the class name handling the events, the lua file to load, and "nil". The nil is critical because...

5. Mod-created specializations are prefixed with the mod name by the engine, so if you are making the mod "MyMod" and pass "myFeature" to addSpecialization() the actual specialization name is "MyMod.myFeature" You need to use this full name when calling g_vehicleTypeManager:addSpecialization()

6. When your code is responding to specialization events, "self" is the vehicle, not your class.

7. The best event to change vehicle properties is "onLoadFinished" as the base-game setup of the vehicle is complete. It is safe to make graphical adjustments as well, as the i3d file is loaded by this point.

Note: Log in to post. Create a new account here.