PersistenceWML

From Wesnoth


Contents

Purpose

Persistent Variables are used to record information that can be accessed across savegames and session instances.

For instance, assume that completing a campaign on hard mode should unlock a special item in subsequent playthroughs of this campaign. With a persistent variable, that can be recorded, and made available to the campaign on later plays.

This can also be used to allow two or more related campaigns to communicate with each other about what has happened in each, allowing the player's choices in one campaign to influence the state of another.

Overview

Persistent Variables work like normal Variables, with a few differences.

WARNING: in multiplayer, do not use persistent variables in prestart, start, and other non-synchronized events.
  • During multiplayer, Persistent Variables cannot be used in events that occur before side 1 turn 1, and will force a quit if they occur there.
  • Persistent Variables may be assigned, retrieved, or cleared, but not directly queried.
  • Persistent Variables may only be assigned from an existing variable, not directly from a constant
  • In addition to a name, each Persistent Variable must have a namespace
  • In multiplayer campaigns, each Persistent Variable also requires a side
  • Persistent Variables will retain their values after wesnoth is closed, until they are cleared or the file containing their namespace is erased or lost.

Transactions

For the purposes of efficiency, and gameplay consistency, persistent variables are usually not written out to file immediately. Instead, they are grouped into transactions which are sets of changes that are either all written at once, or not written at all.
The policy regarding persistent variables will be written is as follows:

  • Transactions will be committed (written to permanent file) whenever the game autosaves, whenever the player saves the game manually, or whenever a scenario is completed in either victory or defeat.
  • Transactions will be cancelled (ignored and wiped from memory) whenever the player loads an earlier savegame, or quits without saving.
  • A WML author can mark a change as an exception to a transaction, to be committed immediately by itself, by using setting the attribute "immediate" to "yes" in a [set_global_variable] or [clear_global_variable] tag. Such operations will be written immediately, regardless of the transaction status, without committing any waiting transactions.
NOTE: Because of the interaction between persistent variable transactions and user loading/saving of games, it is suggested that any variables a WML author wants to remain consistent for a single playthrough of a scenario should be read in immediately at the beginning of the scenario (side 1 turn 1), and set or cleared only in the victory and/or defeat events.

WML Syntax

The following WML Tags are provided.

   [set_global_variable]
     namespace=my_addon
     from_local=foo
     to_global=my_variable_name
     side=1
     immediate=no
   [/set_global_variable]

Assigns a persistent variable with the contents of a standard variable

   [get_global_variable]
     namespace=my_addon
     from_global=my_variable_name
     to_local=foo
     side=1
   [/get_global_variable]

Retrieves the contents of a persistent variable and stores them in a standard variable. and

   [clear_global_variable]
     namespace=my_addon
     global=my_variable_name
     side=1
     immediate=no
   [/clear_global_variable]

Clears a persistent variable entirely.

Attribute information

namespace

This atribute specifies the name of the namespace that the persistent variable resides in.

  • In this attribute, the character "." means "child of",
    • A namespace equal to "foo.bar" will access a "bar" namespace inside "foo", creating it if it doesn't already exist, and store the peristent variable inside "bar".
    • If this attribute begins with "." it will access a child namespace of the default namespace.
      • A namespace equal to ".bar" with the default namespace of "foo" will access a "bar" namespace inside the "foo" namespace.
  • In this attribute, the character "^" means "parent of", must follow a namespace and may only be directly followed by a "." or another "^". And is provided so that content creators can easily use macros to eliminate data repitition for the most frequently used namespaces.
    • a namespace equal to "{def}^" with the def macro "foo" will access the "foo" namespace.
    • a namespace equal to "{def}^" with the def macro "foo.bar" will access the "foo" namespace.
    • a namespace equal to "{def}^" with the def macro "foo.bar.test" will access the "bar" namespace. inside the "foo" namespace.
    • a namespace equal to "{def}^^" with the def macro "foo.bar.test" will access the "foo" namespace.
    • a namespace equal to "{def}^.rod" with the def macro of "foo.bar" will access the "rod" namespace within the "foo" namespace.
    • a namespace equal to "{def}^^.rod" with the def macro "foo.bar.test" will access the "rod" namespace within the "foo" namespace.
    • a namespace equal to "{def}^.quiz" with the def macro "foo.bar.test" will access the "quiz" namespace within the "rod" namespace within the "foo" namespace.
    • a namespace beginning with "^" is invalid and will generate a WML error, because it is impossible to determine the parent of nothing.

*global

These attributes (from_global, to_global, and global) specify the name of the persistent variable to be worked with.
The name of the persistent variable must be a valid variable name, as defined in VariablesWML

*local

These attributes (from_local, to_local) specify the name of the standard variable to be worked with.

side

This attribute specifies which player client's persistence data should read from or written to in multiplayer.
This attribute is not used in single player, and may be omitted in single-player only scenarios.
If this attribute is equal to "global", the currently active player's data will be read in [get_global_variable] operations, and all player's data will be written to in [set_global_variable] and [clear_global_variable] operations.

immediate

This attribute is an optional boolean value that specifies whether the changes made by the [set_global_variable] or [clear_global_variable] should be stored permanently at the moment they occur.
If it is set to yes, the namespace's permanent storage will be updated with the change that occurred in the marked tag immediately.
The default value is no.
This attribute does not apply to [get_global_variable], which always occurs immediately.

About Namespaces

Namespaces exist in order to prevent variable collisions from occuring when two or more unrelated campaigns use the same name for some of their persistent variables. A namespace is simply a name that identifies one set of variables. A namespace may contain only alphabetic characters, digits, and underscores.

Internal Separation

Just as variables can contain other variables, namespaces may contain other namespaces. The syntax for this works just like container variables: to refer to a namespace "bar" contained within a namespace "foo", use namespace="foo.bar"

Uniqueness

Because situations may arise where persistent variables from campaigns that are not installed concurrently have to coexist, a namespace should be unique per-context. This means that two unrelated campaigns with the same name should use different namespaces, but two or more related campaigns with different names may use the same namespace.

The following guidelines are provided in order to facilitate this:

Single Standalone Campaign

For a single campaign which is not intended to interact with other campaigns, the format "(author_handle)_(campaign_name)" is suggested for namespaces. As an example, if an author going by "upthorn" in the community were to create a campaign named "foo", the suggested namespace would be "upthorn_foo".

In cases where the author wishes to remain anonymous, or there are many contributing authors (more than say, two or three), the alternate format "(campaign_name)_(month)(year)" is suggested. Using the above example, assuming that upthorn began work on the campaign on the day this wiki page was created, the resulting namespace would be "foo_may2010"

Multiple Related Campaigns

For multiple related campaigns, there are a few different suggested namespace options depending on order of development and the intended level of interaction.

Developed or Conceived Concurrently

If a campaign is intended from the beginning to be part of a larger world of related campaigns, it is recommended that a namespace be created for the overall world, using the format "(author_handle)_(world_name)" for a named author, or "(world_name)_(month)(year)" for an unnamed author. As an example, if an author going by "upthorn" were to begin development on a "hypothetical" world the day this page was created, this would result in a namespace of either "upthorn_hypothetical" or "hypothetical_may2010"

If a campaign in this world is intended to have a high level of interaction, it is recommended that the campaign simply uses the world's namespace as default, and stores any information that it needs to be kept separate in a child namespace of the format "(campaign_name)". Building on the above example, if upthorn made a "foo" campaign in the "hypothetical" world, with high level of intended interactivity with the world, it would use "upthorn_hypothetical" as its default namespace, perhaps occasionally storing information in "upthorn_hypothetical.foo".

If a campaign in this world is intended to have a medium level of interaction, it is recommended that the campaign creates a child namespace of the format "(campaign_name)" in the world's overall namespace, and uses it as default, accessing the parent namespace when it needs to get information from the overall world. Continuing with the above examples, if upthorn made a "bar" campaign in the "hypothetical" world, with mid level of intended interactivity with the world, it would use "upthorn_hypothetical.bar" as its default namespace, and somewhat frequently access information in "upthorn_hypothetical", by using the namespace "^" or explicitly naming "upthorn_hypothetical".

If a campaign in this world is intended to have a low level of interaction, it is recommended that the campaign uses an entirely separate namespace, of the format "(world_namespace)_(campaign)" as its default, and accesses the overall world's namespace when it needs to communicate with the world. Continuing with the above examples, if upthorn made a "fnord" campaign in the "hypothetical" world, with a low level of intended interactivity with the world, it would use "upthorn_hypothetical_fnord" as its default namespace, and occasionally access information in "upthorn_hypothetical", by explicitly naming "upthorn_hypothetical".

Developed Sequentially

In cases where the first campaign is intended as a standalone, but some time after its release the same author, or other authors decide to make other campaigns in the same overall continuity, the first campaign's namespace cannot be changed.

If campaigns are intended to have a high level of interaction, it is recommended that the later campaign uses the namespace of the prior campaign, and makes a child namespace for itself 'if' it needs to save any of its variables separately. As an example, if an author going by "upthorn" developed a campaign "foo" and later developed a related campaign "bar", the "bar" campaign would use "upthorn_foo" as its default namespace, and perhaps occasionally store information in "upthorn_foo.bar"

If campaigns are intended to have a medium level of interaction, it is recommended that the later campaign makes its default namespace a child inside the namespace of the prior campaign that it is most heavily realted to, and accesses the parent namespace when it needs to work with variables from the prior campaign(s). Working from the above example, upthorn's "bar" campaign would use "upthorn_foo.bar" as its default namespace, and somewhat frequently access the "foo" campaign's information in "upthorn_foo", either by setting the namespace attribute to "upthorn_foo" explicitly, or by using "^" to get "upthorn_foo.bar"'s parent.

If campaigns are intended to have a low level of interaction, it is recommended that the later campaign(s) has its own separate namespace using the "Single Standalone Campaign" guideline format, and explicitly name the prior campaign(s)'s namespace(s) for the few times it needs to access them. Working from the above examples, upthorn's "bar" campaign would use "upthorn_bar" as its default namespace, and occasionally access the "foo" campaign's information in "upthorn_foo", by explicitly naming "upthorn_foo" as the namespace.

This page was last modified on 29 January 2012, at 16:11.