Refactor the syncing of experience of units
Description
The implementation of the experience (veterancy) system is done via the Sync table. Because of this it includes a lot of additional baggage, which includes:
-
- A complicated (and expensive) indexing approach for syncing the data with the UI
-
- A lot of table and field allocations with regard to the
UnitDatatable
- A lot of table and field allocations with regard to the
For 1, note that this is the meta table of self.Sync as set here. As a consequence, all the index calls run via these functions. And there are a lot of those!
Course of action
We'll take a similar approach to https://github.com/FAForever/fa/issues/4134, where we use the recently found Unit:GetStat and Unit:SetStat functions. These allow for direct communication with the UI, without using the sync. As a result we'll need to make the following changes:
-
- Instead of storing the intermediate status in
self.Sync, we store it in separate values that we keep track of inself
- Instead of storing the intermediate status in
-
- We initialize all required values during
OnCreate, unless the unit does not support keeping track of experience. This includes the amount of experience and the amount required for the next level
- We initialize all required values during
-
- We initialize the stats by calling
unit:GetStatduring OnCreate
- We initialize the stats by calling
-
- We update all functions related to experience, including:
-
Unit:CalculateVeterancyLevel
-
Unit:CalculateVeterancyLevelAfterTransfer
-
Unit:SetVeterancy
-
- We update the UI to use
UserUnit:GetStatwhen we want to view the current experience values
- We update the UI to use
Note that 1 and 2 should closely resemble how it works in https://github.com/FAForever/fa/issues/4134, and that 3 is required to prevent an engine bug. We need to try and get the stat (as that prepares the statistic in the engine) before we can set it (which crashes if it tries to read a value that does not exist).
Test plan
The functionality of the experience system should remain the same. Technically you should be able to play a replay without desyncing, as the computation of experience is not adjusted (just the information we send to the UI is). In practice this is impossible due to floating point inaccuracy.
Sanity checklist:
- There should virtually be no more calls to
self.Syncin the unit class - At no point can the game do a
Unit:SetStaton a value that has not been prepared usingUnit:GetStat
Learning goals
Tackle large refactoring that spans across the sim to the UI.