For random numbers in ability filters specifically, it is probably wrong to use them, but if my ability filter is complicated and calls other parts of my code, and one of those parts uses random numbers, I want to see that as a warning message right away rather than having it show up as an unexplained combat OOS several turns later.

For random numbers in synchronize_choice (which is legitimate), it's possible to use math.random currently, but it would be nice (and less confusing for add-on developers) to be able to use the same syntax as for synced random numbers.

Another one: You could have a complex menu that allows you to create and customize a unit (possibly with some legitimately random elements), then passes the unit to the other players as the return value of wesnoth.synchronize choice.

Simple: For some filter, I have a unit table (not a proxy unit) and I want to check its movement cost on a particular hex using wesnoth.unit_movement_cost. So I make a proxy unit with wesnoth.create_unit(), but that (by default) desyncs the RNG.

so you want to be able to call random or [unit] from lua filters?
the nomral rule is that you shall not change the gamestate during an (possibly) unsynced event, which [unit] normaly does.
i argee that wesnoth.synchronized would be a useful feature, but could you please give an example where "lua functions can be invoked during ability filters, which can happen at arbitrary times in the middle of other events"

Currently, we have
- No warnings for using random, synchronize_choice, or global_variables in non-synced events, except for OOS errors, which aren't very readable and (for random things) may show up much later.
- An RNG state that can become desynchronized - for all future uses - from just a single misuse in a non-synchronized context.
- Stealth features that can silently desync the game without the add-on writer referring to them directly: generate_name, random_traits and random_gender. (They default to true, and they can be invoked variously by [unit], wesnoth.put_unit, and wesnoth.create_unit (possibly [unstore_unit]? I don't know). It's quite nonintuitive to be able to break the game by creating a private unit just to check something about it)
- No way for a WML or Lua script to check whether it's currently in a synchronized context.

Running into these can be a major stumbling block for an add-on designer.

Of course, the designer can always desync the game by basing conditionals on timestamps or controller= or math.random, and I don't propose removing those features. But the engine could certainly track whether the current event stack should normally be synchronized or not - which an add-on writer cannot know (consider that an attack event could be invoked by the era/scenario/modification from inside a side_turn_end event - you could say it's irresponsible for someone to do that, but that requires a sophisticated understanding of the networking system that we cannot, and should not, expect from the average WML writer).

In the add-on I'm currently developing, I attempted to track synchronization by setting a global lua variable at the beginning of each synchronized event and unsetting it at the end. However, this did not work because lua functions can be invoked during ability filters, which can happen at arbitrary times in the middle of other events, and as a result, a process that should have been "safe" (because I made it check the sync state and behave differently if it wasn't synced) became unsafe. Looking back on it now, I could have put it in a local variable that is passed around through every function I use (but that would be cluttered) or I could do something complicated with metatables (but again, I want these features to be usable by the average scripter, not just someone with mad skills.)

I propose:
1) Whenever rand=, random_gender=, random_traits=, or generate_name= is used in a context the engine can know isn't synchronized, two things happen: One, it gives a result based on a local RNG state, so that the game doesn't completely desync. Two, it issues a warning message - with a stacktrace - unless the user also specifies "allow_desynced=yes" in the [set_variable] tag or unit table that invoked it. The warning message would say something like "Warning: Using {key=value} in a non-synchronized context. If you know what you're doing, set allow_desynced=yes"
2) A lua attribute "wesnoth.synchronized" so that scripts can check the sync state.

Copyright (C) 2004-2006, the Gna! people. Posted items are owned by whoever posted them.
Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved.