No longer manage a Group's lifetime explicitly in native code, but instead leverage Lua's garbage collector to handle it's lifetime.
In this change, Groups will be stored internally in the core inside a weak table, so that they will still be reference-able when needed, but if no reference were to exist in Lua code, they will be subject to garbage collection.
Whenever a Group is used by native code, its wrapped by a owned_lua template class, this is akin to std::shared_ptr--It reference counts the usages of that group internally in the core, including managing it in the Lua registry index when in use, so that it won't be collected.
With this approach, the exponential memory usage growth caused by long running procedures due to the creation hundredths of thousands of groups per iteration, is mitigated, since the temporary groups will be reaped more frequently, as opposed to the old approach of them being cleared only after the topmost Lua function had finished executing.
For now a single GC step is triggered whenever the total number of groups is more than 2048--an arbitrary value that seems to work well enough, but which can be changed/optimized after proper profiling.
A major upgrade with this approach is the removal of the concept of Groups "kept alive" and having to "explicitly delete" them to make them outlive a Lua function execution, since they will now follow Lua's variables lifetime properly.
Functions from the auxiliary library, assume the stack has always at least 5 empty slots, add macro ensure_luaL_stack to wrap every such call so that the stack is checked for enough free space beforehand. This will make mandatory to use the latest version of visual studio 2017 as minimum for windows (it was already being required for c++17 support regardless), as it needs the conformant c++ preprocessor option to be used.
It was being called on a range sorted with `card_sort` but without passing that specific comparator function, thus triggering undefined behaviour that could potentially lead to a crash
Avoid having to deal with UB while relying on signed integer overflow, add new value Processors::restart to be used in place of the -1 when "looping" a process (a value that when incremented by 1 will properly overflow to 0)
Rewrite the whole ``process`` function of the processor to use instead tag dispatching on variant objects.
Each single processor message got rewritten as a struct, inheriting from a base templated Process struct, containing only the required data for that process, rather than using the generic structure that ended up needing to spam bitwise operations to pack as many arguments in a byte as possible. This makes things easier to follow and streamlines the addition of new processes:
- Define its struct with the parameters it expects by also providing a constructor
- If the process requires an answer, inherit it from ``Process<true>``, if it requires no answer, inherit it from ``Process<false>``
- Add such struct to the ``processors`` variant type
- Declare and implement its corresponding ``bool process(Processors::NewProcess& arg);`` function
* Update Card.IsReleasable to accept a second, optional parameter for the reason. Update GetReleaseGroup, GetReleaseGroupCount the same way as well, with the new parameter being the 4th. Defaulting it to REASON_COST will prevent massive script changes (cards that tribute by effect already use Card.IsReleasableByEffect).
* Update EFFECT_CANNOT_RELEASE to pass the reason and the reason effect a 4th and 5th parameters to its target function
* Pass the reason down the line until get_release_list, check_release_list,is_player_can_release and card::is_releasable_by_nonsummon, updating internal function calls accordingly
---------
Co-authored-by: Edoardo Lolletti <edoardo762@gmail.com>
It was setting the statuses STATUS_LEAVE_CONFIRMED and STATUS_ACTIVATE_DISABLED only after checking if the card was in the szone, not accounting for instances where in the same chain the card left the field and got put back in on there before negating the effect. Now only set those statuese if the card is still related to the chain.
* Update the handling of changing positions via effects
Currently, EFFECT_CANNOT_CHANGE_POS_E prevents the affected cards from changing to any position. This changes that behavior to use that effect's value to acquire the positions the card(s) cannot be changed to. If no value is provided, no position change is allowed
---------
Co-authored-by: Edoardo Lolletti <edoardo762@gmail.com>
Implement it natively rather than via lua, correctly handle the scenario where one of the xyz monsters to be overlaid has an effect to banish any card sent to the grave and it would be still applying if duel.sendtograve was called manually from the script
Decouple the summon location from the summon type, also keep track of the sequence and if the card was in a pzone, this allows to use symbolic locations in Card.IsSummonLocation as wel
LOCATION_STZONE (the Spell & Trap zone, so excluding mr3 pzones and field zone), LOCATION_MMZONE and LOCATION_EMZONE. Usable in GetMatchingCard, GetFieldGroup, etc
Remove the arbitrairy cal to a max of 5 values for the results, add strict checks to Duel.SetCoinResult and Duel.SetDiceResult to ensure you set the exact amount of values and no more, also make Duel.GetCoinResult and Duel.GetDiceResult return the actual number of values rather than the fixes 5 values
Add a separate ``set`` function and use memcpy to store/retrieve the data in the buffer, removes the raw cast to a reference that could have also lead to aligment issues.